Skip to content

Commit

Permalink
feat: Improve test convention, update to v8 (#20)
Browse files Browse the repository at this point in the history
* feat: Update test convention

* chore: Add cleanup comments

* fix: Enforce __tests__ folder, exclude other utils
  • Loading branch information
bigtimebuddy authored Dec 19, 2024
1 parent 2a8146e commit c58cfc9
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 62 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Generally, the project structure is baked-in to the defaults, however, most of t
* `./examples` - Contains any examples or demos use to test the project
* `./lib` - Generated folder for ES2020 modules and types
* `./src` - Contains all the source code (TypeScript files)
* `./test` - The Jest unit-tests
* `./src/__tests__` - The Jest unit-tests

## Configuration

Expand Down Expand Up @@ -115,13 +115,14 @@ Configuration can be provided using the `extensionConfig` field in **package.jso
* **`environments`** _string[]_ - Environments supports for output, only values supprted `node`, `browser` (default: `['node', 'browser']`)
* **`globals`** _object_ - [Output globals](https://rollupjs.org/guide/en/#outputglobals) as defined by Rollup. This is used for creating the browser bundle. All defaults are provide for the core package of PixiJS (e.g., `@pixi/core`, `@pixi/sprite`, etc).
* **`jestConfig`** _string_ - Optional path to the Jest config file (default: `null`)
* **`lint`** _string[]_ - List of additional folders or files to lint. (default: `['src', 'test', 'examples']`)
* **`lint`** _string[]_ - List of additional folders or files to lint. (default: `['src', 'examples']`)
* **`moduleSource`** _string_ - Input for the `module` output, fallback to `source`. Supports globs. (default: `null`)
* **`namespace`** _string_ - User-defined global namespace for the bundle.
* **`serve`** _string_ - Relative path to the serve folder (default: `examples`).
* **`silent`** _boolean_ - Whether to silence the output (default: `false`)
* **`source`** _string_ - The entry-point for building the extension (default: `src/index.ts`)
* **`tsconfig`** _string_ - Relative path to the tsconfig file for doing type check (default: `tsconfig.json`)
* **`tsconfigTypes`** _string_ - Relative path to the tsconfig file for outputting types, if not defined, will use `tsconfig` option (default: `null`)

### Example

Expand Down
3 changes: 2 additions & 1 deletion lib/configs/jest.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default {
// Support for loading vertex and fragment shaders for PixiJS
'\\.vert$': 'jest-raw-loader',
'\\.frag$': 'jest-raw-loader',
'\\.wgsl$': 'jest-raw-loader',
},
testMatch: ['<rootDir>/test/*.test.ts']
testMatch: ['<rootDir>/**/__tests__/*.test.ts']
};
10 changes: 8 additions & 2 deletions lib/configs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
"sourceMap": true,
"module": "ESNext",
"target": "ES2020",
"moduleResolution": "node",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"noImplicitAny": true
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noImplicitAny": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
}
}
3 changes: 2 additions & 1 deletion lib/extensionConfig.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@ export const extensionConfig = {
environments: ['browser', 'node'],
globals: {},
jestConfig: null,
lint: ['src', 'test', 'examples'],
lint: ['src', 'examples'],
moduleSource: null,
namespace: defaultNamespace,
serve: 'examples',
silent: false,
source: 'src/index.ts',
tsconfig: 'tsconfig.json',
tsconfigTypes: null,
...pkg.extensionConfig,
};
108 changes: 66 additions & 42 deletions lib/index.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env node

import chalk from 'chalk';
import { glob } from 'glob';
import { promises } from 'node:fs';
import path from 'node:path';
import url from 'url';
Expand Down Expand Up @@ -29,38 +30,40 @@ const bundle = (...args) =>
]);
};

/** Wrapper for using typescript's CLI */
const tsc = async (...args) =>
/** Ensure that the project has a tsconfig */
const ensureTsconfig = async (tsconfigFile) =>
{
const tsconfig = path.join(process.cwd(), extensionConfig.tsconfig);
const tsconfig = tsconfigFile ?? path.join(process.cwd(), extensionConfig.tsconfig);

if (await pathExists(tsconfig))
if (!(await pathExists(tsconfig)))
{
return spawn('tsc', ['-p', tsconfig, ...args]);
// eslint-disable-next-line no-console
console.log(chalk.red(`${prefix} Error: TypeScript configuration file "${path.basename(tsconfig)}"`
+ ` not found but is required for linting and building.`));

process.exit(1);
}

return spawn('tsc', [
extensionConfig.source,
'--target', 'ES2020',
'--module', 'ESNext',
'--sourceMap',
'--moduleResolution', 'node',
'--esModuleInterop',
'--noImplicitAny',
'--strict',
...args,
]);
return tsconfig;
};

/**
* Wrapper for using typescript's CLI
* @param {string} tsconfigFile Optional absolute path to the tsconfig file
*
*/
const tsc = async (tsconfigFile, ...args) =>
spawn('tsc', ['-p', await ensureTsconfig(tsconfigFile), ...args]);

/** Wrapper for Jest */
const test = async (additionalArgs) =>
{
const testPath = path.join(process.cwd(), 'test');
const testFiles = await glob(path.join(process.cwd(), '**/__tests__/*.test.ts'));

if (!(await pathExists(testPath)))
if (testFiles.length === 0)
{
// eslint-disable-next-line no-console
console.log(chalk.yellow(`${prefix} Warning: No "test" folder found, skipping tests.`));
console.log(chalk.yellow(`${prefix} Warning: No *.test.ts files found, skipping tests.`));

return;
}
Expand Down Expand Up @@ -88,40 +91,63 @@ const test = async (additionalArgs) =>
await spawn('jest', [
'--config', configPath ?? path.join(__dirname, 'configs/jest.mjs'),
'--rootDir', process.cwd(),
'--passWithNoTests',
...additionalArgs,
]);
};

/** Export the types */
const bundleTypes = () => tsc(
'--outDir', path.join(process.cwd(), 'lib'),
'--declaration',
'--emitDeclarationOnly'
);

/**
* Fix/hack for importing global mixins for a project
* because tsc will output the wront triple reference
*/
const fixGlobalTypes = async () =>
/** Generate the types into the lib folder */
const bundleTypes = async () =>
{
const globalTypes = path.join(process.cwd(), 'global.d.ts');
let tsconfigTypesFile = extensionConfig.tsconfigTypes
? path.join(process.cwd(), extensionConfig.tsconfigTypes) : null;

if (await pathExists(globalTypes))
// If no tsconfig types file is defined, we will create one
// based on the current tsconfig.json file
if (!tsconfigTypesFile)
{
const indexFile = path.resolve(process.cwd(), 'lib/index.d.ts');
const buffer = await promises.readFile(indexFile, 'utf8');
const tsconfigFile = await ensureTsconfig();

tsconfigTypesFile = tsconfigFile.replace(/\.json$/, '.types.json');
const config = JSON.parse(await promises.readFile(tsconfigFile, 'utf8'));

await promises.writeFile(tsconfigTypesFile, JSON.stringify({
...config,
compilerOptions: {
...config.compilerOptions,
// Just emit the declaration files only
declaration: true,
emitDeclarationOnly: true,
outDir: './lib',
// make sure we exclude anything, such as scripts,
// outside of the src folder to avoid lib/src/index.d.ts
rootDir: './src',
},
// Make sure to exclude any Jest test files
exclude: ['**/__tests__']
}, null, 2), 'utf8');
}

await promises.writeFile(indexFile, buffer.replace(
'/// <reference types="global" />',
'/// <reference path="../global.d.ts" />'
));
try
{
await tsc(tsconfigTypesFile);
}
finally
{
// Clean up if the tsconfig file was generated
if (!extensionConfig.tsconfigTypes)
{
await promises.unlink(tsconfigTypesFile);
}
}
};

/** Run ESlint with built-in config */
const lint = async (additionalArgs = []) =>
{
// Ensure that the project has a tsconfig
await ensureTsconfig();

const eslintMJS = path.join(process.cwd(), 'eslint.config.mjs');
const eslintJS = path.join(process.cwd(), 'eslint.config.js');
const isDefined = 'eslintConfig' in packageInfo
Expand Down Expand Up @@ -264,8 +290,6 @@ const runCommand = async (command, additionalArgs) =>
case Command.Bundle: {
await bundle();
await bundleTypes();
await fixGlobalTypes();

break;
}
case Command.Clean: {
Expand Down Expand Up @@ -298,7 +322,7 @@ const runCommand = async (command, additionalArgs) =>
}
case Command.Serve: serve().then(() => runCommand(Command.Watch)); break;
case Command.Test: await test(additionalArgs); break;
case Command.Types: await tsc('-noEmit'); break;
case Command.Types: await tsc(null, '-noEmit'); break;
case Command.Upload: {
await spawn('gh-pages', [
'-d', extensionConfig.deployRoot,
Expand Down
22 changes: 17 additions & 5 deletions test/examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,23 @@
<title>Test</title>
</head>
<body>
<script src="https://pixijs.download/dev/pixi.min.js"></script>
<script src="../dist/pixi-my-system.js"></script>
<script>
const app = new PIXI.Application();
document.body.appendChild(app.view);
<script type="importmap">
{
"imports": {
"pixi.js": "https://cdn.jsdelivr.net/npm/pixi.js@8/dist/pixi.mjs",
"@pixi/my-system": "../dist/pixi-my-system.mjs"
}
}
</script>
<script type="module">
import { Application } from 'pixi.js';
import '@pixi/my-system';
const app = new Application();
await app.init();
document.body.appendChild(app.canvas);
setTimeout(() => {
app.destroy(true);
}, 4000);
</script>
</body>
</html>
6 changes: 4 additions & 2 deletions test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
}
},
"scripts": {
"start": "xs serve",
"build": "xs build",
"clean": "xs clean",
"docs": "xs docs",
Expand All @@ -24,9 +25,10 @@
"watch": "xs watch"
},
"peerDependencies": {
"@pixi/core": "^7.3.0"
"pixi.js": ">=8 <9"
},
"devDependencies": {
"@pixi/extension-scripts": "file:../"
"@pixi/extension-scripts": "file:../",
"pixi.js": "^8.0.0"
}
}
File renamed without changes.
8 changes: 4 additions & 4 deletions test/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { extensions, ExtensionType } from '@pixi/core';
import { extensions, ExtensionType } from 'pixi.js';

import type { ExtensionMetadata, ISystem } from '@pixi/core';
import type { ExtensionMetadata, System } from 'pixi.js';

/**
* Renderer system extension example.
* This doesn't do anything meaningful, but it shows how to create a system extension.
*/
class MySystem implements ISystem
class MySystem implements System
{
/** Metadata to install the extension */
static extension: ExtensionMetadata = {
name: 'mySystem',
type: ExtensionType.RendererSystem,
type: [ExtensionType.WebGLSystem, ExtensionType.WebGPUSystem],
};

/** Currention version can be inlined like this: */
Expand Down
4 changes: 1 addition & 3 deletions test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
{
"compilerOptions": {
"skipLibCheck": true
}
"extends": "@pixi/extension-scripts/tsconfig"
}

0 comments on commit c58cfc9

Please sign in to comment.