From c58cfc9b2caf25374a190c2f214d313a44259514 Mon Sep 17 00:00:00 2001 From: Matt Karl Date: Thu, 19 Dec 2024 14:28:23 -0500 Subject: [PATCH] feat: Improve test convention, update to v8 (#20) * feat: Update test convention * chore: Add cleanup comments * fix: Enforce __tests__ folder, exclude other utils --- README.md | 5 +- lib/configs/jest.mjs | 3 +- lib/configs/tsconfig.json | 10 +- lib/extensionConfig.mjs | 3 +- lib/index.mjs | 108 +++++++++++-------- test/examples/index.html | 22 +++- test/package.json | 6 +- test/{test => src/__tests__}/example.test.ts | 0 test/src/index.ts | 8 +- test/tsconfig.json | 4 +- 10 files changed, 107 insertions(+), 62 deletions(-) rename test/{test => src/__tests__}/example.test.ts (100%) diff --git a/README.md b/README.md index 1e68979..8b86b67 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 diff --git a/lib/configs/jest.mjs b/lib/configs/jest.mjs index 0718288..8a4e1c8 100644 --- a/lib/configs/jest.mjs +++ b/lib/configs/jest.mjs @@ -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: ['/test/*.test.ts'] + testMatch: ['/**/__tests__/*.test.ts'] }; diff --git a/lib/configs/tsconfig.json b/lib/configs/tsconfig.json index 8a9472d..046e1c0 100644 --- a/lib/configs/tsconfig.json +++ b/lib/configs/tsconfig.json @@ -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, } } \ No newline at end of file diff --git a/lib/extensionConfig.mjs b/lib/extensionConfig.mjs index 12cf1e5..481f6cd 100644 --- a/lib/extensionConfig.mjs +++ b/lib/extensionConfig.mjs @@ -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, }; diff --git a/lib/index.mjs b/lib/index.mjs index 18c7aa3..00a5fac 100755 --- a/lib/index.mjs +++ b/lib/index.mjs @@ -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'; @@ -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; } @@ -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( - '/// ', - '/// ' - )); + 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 @@ -264,8 +290,6 @@ const runCommand = async (command, additionalArgs) => case Command.Bundle: { await bundle(); await bundleTypes(); - await fixGlobalTypes(); - break; } case Command.Clean: { @@ -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, diff --git a/test/examples/index.html b/test/examples/index.html index 19c1046..b742ee3 100644 --- a/test/examples/index.html +++ b/test/examples/index.html @@ -4,11 +4,23 @@ Test - - - + \ No newline at end of file diff --git a/test/package.json b/test/package.json index 2203933..455f520 100644 --- a/test/package.json +++ b/test/package.json @@ -13,6 +13,7 @@ } }, "scripts": { + "start": "xs serve", "build": "xs build", "clean": "xs clean", "docs": "xs docs", @@ -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" } } diff --git a/test/test/example.test.ts b/test/src/__tests__/example.test.ts similarity index 100% rename from test/test/example.test.ts rename to test/src/__tests__/example.test.ts diff --git a/test/src/index.ts b/test/src/index.ts index 74f8322..e368fe0 100644 --- a/test/src/index.ts +++ b/test/src/index.ts @@ -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: */ diff --git a/test/tsconfig.json b/test/tsconfig.json index 8bd557b..ca656a2 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,5 +1,3 @@ { - "compilerOptions": { - "skipLibCheck": true - } + "extends": "@pixi/extension-scripts/tsconfig" } \ No newline at end of file