diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 533b074f..ad3fa647 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,6 +132,34 @@ jobs: - name: Build Affected run: npx nx affected -t build build-storybook --parallel=3 + e2e: + name: End-To-End Affected + needs: build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + # We need to fetch all branches and commits so that Nx affected has a base to compare against. + fetch-depth: 0 + # Derive appropriate SHAs for base and head for `nx affected` commands + - uses: nrwl/nx-set-shas@v3 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + + - name: Cache NPM Dependencies + uses: actions/cache@v4 + with: + path: | + node_modules + ~/.cache + dist + key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} + + - name: Build Affected + run: npx nx affected -t e2e --parallel=3 --exclude audit-runner-e2e, portal-e2e # publish-storybook: # name: Publish Storybook # needs: [build, lint] @@ -161,7 +189,7 @@ jobs: deploy-front-end: name: Deploy Front-End - needs: [build, lint] + needs: [e2e] runs-on: ubuntu-latest steps: @@ -203,7 +231,7 @@ jobs: deploy-server: name: Deploy Server - needs: [build, lint] + needs: [e2e] runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index d0b8d1df..dc3f3940 100644 --- a/.gitignore +++ b/.gitignore @@ -115,3 +115,6 @@ dist # Nx Cache .nx + +# Temporary Files +tmp diff --git a/global-setup.e2e.ts b/global-setup.e2e.ts new file mode 100644 index 00000000..495f8e7a --- /dev/null +++ b/global-setup.e2e.ts @@ -0,0 +1,13 @@ +import { execSync } from 'child_process'; +import startLocalRegistry from './tools/scripts/start-local-registry'; +import stopLocalRegistry from './tools/scripts/stop-local-registry'; + +export async function setup() { + await startLocalRegistry(); + execSync('npm install -D @app-speed/esbuild-meta@e2e'); +} + +export async function teardown() { + stopLocalRegistry(); + execSync('npm uninstall @app-speed/esbuild-meta'); +} diff --git a/nx.json b/nx.json index 369778b4..fb93f2c7 100644 --- a/nx.json +++ b/nx.json @@ -66,7 +66,8 @@ "release": { "version": { "preVersionCommand": "npx nx run-many -t build" - } + }, + "projects": ["esbuild-meta"] }, "plugins": [ { diff --git a/package-lock.json b/package-lock.json index 6a51d3eb..208c01b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,10 +1,10 @@ { - "name": "app-speed", + "name": "@app-speed/source", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "app-speed", + "name": "@app-speed/source", "dependencies": { "@angular/animations": "17.3.2", "@angular/cdk": "17.3.2", @@ -87,6 +87,7 @@ "@vitest/coverage-v8": "1.4.0", "@vitest/ui": "1.4.0", "autoprefixer": "^10.4.0", + "chalk": "^5.3.0", "chromatic": "^7.2.0", "conventional-changelog-conventionalcommits": "^7.0.2", "cypress": "^13.0.0", @@ -120,10 +121,10 @@ "ts-jest": "29.1.0", "typescript": "5.3.3", "verdaccio": "^5.0.4", - "vite": "5.1.3", + "vite": "~5.0.0", "vite-plugin-eslint": "^1.8.1", "vite-tsconfig-paths": "^4.3.1", - "vitest": "1.4.0", + "vitest": "^1.3.1", "webpack": "^5.64.0" } }, @@ -3808,6 +3809,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/parser": { "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", @@ -21818,16 +21832,15 @@ } }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, "engines": { - "node": ">=4" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/char-regex": { @@ -28328,18 +28341,6 @@ "node": ">=18" } }, - "node_modules/inquirer/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/inquirer/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -32416,19 +32417,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/marked-terminal/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "peer": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/marky": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", @@ -44523,6 +44511,21 @@ "node": ">=6" } }, + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/signale/node_modules/figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -48097,13 +48100,13 @@ } }, "node_modules/vite": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.3.tgz", - "integrity": "sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==", + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.13.tgz", + "integrity": "sha512-/9ovhv2M2dGTuA+dY93B9trfyWMDRQw2jdVBhHNP6wr0oF34wG2i/N55801iZIpgUpnHDm4F/FabGQLyc+eOgg==", "dev": true, "dependencies": { "esbuild": "^0.19.3", - "postcss": "^8.4.35", + "postcss": "^8.4.32", "rollup": "^4.2.0" }, "bin": { diff --git a/package.json b/package.json index 4c0a965a..b0e0c3d2 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "app-speed", + "name": "@app-speed/source", "main": "apps/user-flow-runner/src/app/runner.ts", "scripts": { "start": "nx serve user-flow-manager", @@ -90,6 +90,7 @@ "@vitest/coverage-v8": "1.4.0", "@vitest/ui": "1.4.0", "autoprefixer": "^10.4.0", + "chalk": "^5.3.0", "chromatic": "^7.2.0", "conventional-changelog-conventionalcommits": "^7.0.2", "cypress": "^13.0.0", @@ -123,10 +124,10 @@ "ts-jest": "29.1.0", "typescript": "5.3.3", "verdaccio": "^5.0.4", - "vite": "5.1.3", + "vite": "~5.0.0", "vite-plugin-eslint": "^1.8.1", "vite-tsconfig-paths": "^4.3.1", - "vitest": "1.4.0", + "vitest": "^1.3.1", "webpack": "^5.64.0" }, "nx": { diff --git a/packages/esbuild-meta/CHANGELOG.md b/packages/esbuild-meta/CHANGELOG.md index 80b5ab6b..39189429 100644 --- a/packages/esbuild-meta/CHANGELOG.md +++ b/packages/esbuild-meta/CHANGELOG.md @@ -2,6 +2,17 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +## [1.3.1](https://github.com/ChristopherPHolder/app-speed/compare/esbuild-meta-1.3.0...esbuild-meta-1.3.1) (2024-05-05) + + +### Bug Fixes + +* **esbuild-meta:** add missing type ([0e7f76b](https://github.com/ChristopherPHolder/app-speed/commit/0e7f76bdd6f8e980d2f74f023fbc04833ec26685)) +* **esbuild-meta:** add missing type ([973e10e](https://github.com/ChristopherPHolder/app-speed/commit/973e10ed0765a194df316d6c374125431437aa34)) +* **esbuild-meta:** filter only import-statements ([da64edf](https://github.com/ChristopherPHolder/app-speed/commit/da64edf7fb8fb1eb7bd804e32ff5f9e7c617abb7)) + + + # [1.3.0](https://github.com/ChristopherPHolder/app-speed/compare/esbuild-meta-1.2.0...esbuild-meta-1.3.0) (2024-04-18) diff --git a/packages/esbuild-meta/e2e/__snapshots__/esbuild-meta.test.e2e.ts.snap b/packages/esbuild-meta/e2e/__snapshots__/esbuild-meta.test.e2e.ts.snap new file mode 100644 index 00000000..238583a8 --- /dev/null +++ b/packages/esbuild-meta/e2e/__snapshots__/esbuild-meta.test.e2e.ts.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`--help > should show help 1`] = ` +"esbuild-meta [command] + +Commands: + esbuild-meta filter Filters the meta file to only include chunks required by specified entry points [aliases: f] + +Options: + --version Show version number [boolean] + -h, --help Show help [boolean] +" +`; diff --git a/packages/esbuild-meta/e2e/esbuild-meta.test.e2e.ts b/packages/esbuild-meta/e2e/esbuild-meta.test.e2e.ts new file mode 100644 index 00000000..b5323d13 --- /dev/null +++ b/packages/esbuild-meta/e2e/esbuild-meta.test.e2e.ts @@ -0,0 +1,26 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { version } from '../package.json'; + +import { commandOutput } from './utils.js'; + +describe('--help', () => { + let helpOutput: string; + + beforeAll(() => { + helpOutput = commandOutput('npx esbuild-meta --help'); + }); + + it('should show help', () => { + expect(helpOutput).toMatchSnapshot(); + }); + + it('should alias to -h', () => { + expect(commandOutput('npx esbuild-meta -h')).toBe(helpOutput); + }); +}); + +describe('--version', () => { + it('should show version', () => { + expect(commandOutput('npx esbuild-meta --version')).toContain(version); + }); +}); diff --git a/packages/esbuild-meta/e2e/utils.ts b/packages/esbuild-meta/e2e/utils.ts new file mode 100644 index 00000000..d61a3e22 --- /dev/null +++ b/packages/esbuild-meta/e2e/utils.ts @@ -0,0 +1,3 @@ +import { execSync } from 'child_process'; + +export const commandOutput = (command: string) => execSync(command).toString(); diff --git a/packages/esbuild-meta/package.json b/packages/esbuild-meta/package.json index 6e305bf4..b46a9a0d 100644 --- a/packages/esbuild-meta/package.json +++ b/packages/esbuild-meta/package.json @@ -1,14 +1,18 @@ { "name": "@app-speed/esbuild-meta", - "bin": "./main.js", + "bin": { + "esbuild-meta": "./main.js" + }, "type": "module", "license": "MIT", "publishConfig": { "access": "public" }, "dependencies": { - "esbuild": "0.19.2", "yargs": "^17.7.2" }, - "version": "1.3.0" + "devDependencies": { + "esbuild": "0.19.2" + }, + "version": "1.3.1" } diff --git a/packages/esbuild-meta/project.json b/packages/esbuild-meta/project.json index c43eee85..e053d6fa 100644 --- a/packages/esbuild-meta/project.json +++ b/packages/esbuild-meta/project.json @@ -21,12 +21,10 @@ "main": "packages/esbuild-meta/src/main.ts", "tsConfig": "packages/esbuild-meta/tsconfig.lib.json", "assets": ["packages/esbuild-meta/*.md"], - "generatePackageJson": true, "format": ["esm"], "minify": true, "platform": "node", - "bundle": true, - "thirdParty": true + "bundle": true } }, "nx-release-publish": { @@ -41,6 +39,12 @@ "reportsDirectory": "../../coverage/packages/esbuild-meta" } }, + "e2e": { + "executor": "@nx/vite:test", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}-e2e"], + "options": { "config": "packages/esbuild-meta/vitest.config.e2e.ts" }, + "dependsOn": ["build"] + }, "version": { "executor": "@jscutlery/semver:version", "options": { diff --git a/packages/esbuild-meta/src/lib/filter-meta.ts b/packages/esbuild-meta/src/lib/filter-meta.ts index 660b4b46..1c7d4df0 100644 --- a/packages/esbuild-meta/src/lib/filter-meta.ts +++ b/packages/esbuild-meta/src/lib/filter-meta.ts @@ -62,5 +62,5 @@ export const filterMetaCommand: FilterMetaCommandModule = { describe: 'Filters the meta file to only include chunks required by specified entry points', aliases: 'f', builder: filterMetaBuilder, - handler: filterMetaHandler + handler: filterMetaHandler, } diff --git a/packages/esbuild-meta/src/lib/utils.ts b/packages/esbuild-meta/src/lib/utils.ts index fb502a3b..7efea22a 100644 --- a/packages/esbuild-meta/src/lib/utils.ts +++ b/packages/esbuild-meta/src/lib/utils.ts @@ -32,7 +32,7 @@ export function filterMetaFromEntryPoints(meta: Metafile, entryPoints: string[]) const extractedChunks = new Set(); const alreadyExtractedChildren = (chunk: string) => extractedChunks.has(chunk); const addToExtractedChunks = (chunk: string) => extractedChunks.add(chunk) && extractedChunks.add(`${chunk}.map`); - const childImportedChunks = (chunk: string) => meta['outputs'][chunk]['imports'].filter(({kind}) => kind === "import-statement" || "dynamic-import").map(({path}) => path); + const childImportedChunks = (chunk: string) => meta['outputs'][chunk]['imports'].filter(({kind}) => kind === "import-statement").map(({path}) => path); function extractImport(chunks: string[]) { for (const chunk of chunks) { if (alreadyExtractedChildren(chunk)) continue; diff --git a/packages/esbuild-meta/src/main.ts b/packages/esbuild-meta/src/main.ts index f2c1a8b5..db3be97f 100644 --- a/packages/esbuild-meta/src/main.ts +++ b/packages/esbuild-meta/src/main.ts @@ -4,7 +4,14 @@ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { argv } from 'node:process'; import { filterMetaCommand } from './lib/filter-meta.js'; +import { version } from '../package.json'; -yargs(hideBin(argv)).command('$0', 'Default Command is filter', filterMetaCommand).parse(); - -console.log('Esbuild Meta Completed Successfully!'); +yargs(hideBin(argv)) + .scriptName('esbuild-meta') + .version(version).alias('v', 'version') + .showHelpOnFail(true) + .command(filterMetaCommand) + .help() + .alias('h', 'help') + .wrap(null) + .parse(); diff --git a/packages/esbuild-meta/tsconfig.json b/packages/esbuild-meta/tsconfig.json index 897f0b96..4be28a4f 100644 --- a/packages/esbuild-meta/tsconfig.json +++ b/packages/esbuild-meta/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "resolveJsonModule": true, "module": "NodeNext", "moduleResolution": "NodeNext", "esModuleInterop": true, diff --git a/packages/esbuild-meta/tsconfig.spec.json b/packages/esbuild-meta/tsconfig.spec.json index 0edf3f2f..c872c81b 100644 --- a/packages/esbuild-meta/tsconfig.spec.json +++ b/packages/esbuild-meta/tsconfig.spec.json @@ -7,6 +7,8 @@ "include": [ "vite.config.ts", "vitest.config.ts", + "vitest.config.e2e.ts", + "e2e/**/*.test.e2e.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.test.tsx", diff --git a/packages/esbuild-meta/vite.config.ts b/packages/esbuild-meta/vite.config.ts index d6c5228c..568204ea 100644 --- a/packages/esbuild-meta/vite.config.ts +++ b/packages/esbuild-meta/vite.config.ts @@ -4,7 +4,7 @@ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; export default defineConfig({ root: __dirname, - cacheDir: '../../node_modules/.vite/packages/esbuild-meta', + cacheDir: '../../node_modules/.vite/packages/esbuild-meta/unit', plugins: [nxViteTsPaths()], diff --git a/packages/esbuild-meta/vitest.config.e2e.ts b/packages/esbuild-meta/vitest.config.e2e.ts new file mode 100644 index 00000000..78d57d6c --- /dev/null +++ b/packages/esbuild-meta/vitest.config.e2e.ts @@ -0,0 +1,19 @@ +/// +import { defineConfig } from 'vite'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; + +export default defineConfig({ + cacheDir: '../../node_modules/.vite/esbuild-meta/e2e', + + plugins: [nxViteTsPaths()], + + test: { + watch: false, + reporters: ['default'], + globals: true, + cache: { dir: '../../node_modules/.vitest' }, + environment: 'node', + include: ['e2e/**/*.test.e2e.ts'], + globalSetup: '../../global-setup.e2e.ts' + }, +}); diff --git a/project.json b/project.json index 084e3a89..7620b5a4 100644 --- a/project.json +++ b/project.json @@ -1,5 +1,5 @@ { - "name": "app-speed", + "name": "@app-speed/source", "$schema": "node_modules/nx/schemas/project-schema.json", "targets": { "local-registry": { diff --git a/tools/scripts/start-local-registry.ts b/tools/scripts/start-local-registry.ts new file mode 100644 index 00000000..7a747d21 --- /dev/null +++ b/tools/scripts/start-local-registry.ts @@ -0,0 +1,107 @@ +/** + * This script starts a local registry for e2e testing purposes. + * It is meant to be called in jest's globalSetup. + */ +import { execFileSync, execSync, spawn } from 'child_process'; + +export default async () => { + // local registry target to run + const localRegistryTarget = '@app-speed/source:local-registry'; + // storage folder for the local registry + const storage = './tmp/local-registry/storage'; + + global.stopLocalRegistry = await startLocalRegistry({ + localRegistryTarget, + storage, + verbose: true, + }); + + // is is also possible to use nx release to publish the packages to the local registry + execFileSync( + 'npx', + [ + 'nx', + 'release', + 'publish', + '--tag', + 'e2e', + ], + { env: process.env, stdio: 'inherit', shell: true }, + ); +}; + +// soft copy from https://github.com/nrwl/nx/blob/16.9.x/packages/js/src/plugins/jest/start-local-registry.ts +// original function does not work, because it uses require.resolve('nx') and fork, +// and it does not work with vite +function startLocalRegistry({ + localRegistryTarget, + storage, + verbose, +}: { + localRegistryTarget: string; + storage?: string; + verbose?: boolean; +}) { + if (!localRegistryTarget) { + throw new Error(`localRegistryTarget is required`); + } + return new Promise<() => void>((resolve, reject) => { + const childProcess = spawn( + 'npx', + [ + 'nx', + ...`run ${localRegistryTarget} --location none --clear true`.split(' '), + ...(storage ? [`--storage`, storage] : []), + ], + { stdio: 'pipe', shell: true }, + ); + + const listener = data => { + if (verbose) { + process.stdout.write(data); + } + if (data.toString().includes('http://localhost:')) { + const port = parseInt( + data.toString().match(/localhost:(?\d+)/)?.groups?.port, + ); + console.info('Local registry started on port ' + port); + + const registry = `http://localhost:${port}`; + process.env.npm_config_registry = registry; + execSync( + `npm config set //localhost:${port}/:_authToken "secretVerdaccioToken"`, + ); + + // yarnv1 + process.env.YARN_REGISTRY = registry; + // yarnv2 + process.env.YARN_NPM_REGISTRY_SERVER = registry; + process.env.YARN_UNSAFE_HTTP_WHITELIST = 'localhost'; + + console.info('Set npm and yarn config registry to ' + registry); + + resolve(() => { + childProcess.kill(); + execSync(`npm config delete //localhost:${port}/:_authToken`); + }); + childProcess?.stdout?.off('data', listener); + } + }; + childProcess?.stdout?.on('data', listener); + childProcess?.stderr?.on('data', data => { + process.stderr.write(data); + }); + childProcess.on('error', err => { + console.error('local registry error', err); + reject(err); + }); + childProcess.on('exit', code => { + console.info('local registry exit', code); + if (code !== 0) { + reject(code); + } else { + resolve(() => {}); + } + }); + }); +} diff --git a/tools/scripts/stop-local-registry.ts b/tools/scripts/stop-local-registry.ts new file mode 100644 index 00000000..31d5d347 --- /dev/null +++ b/tools/scripts/stop-local-registry.ts @@ -0,0 +1,10 @@ +/** + * This script stops the local registry for e2e testing purposes. + * It is meant to be called in jest's globalTeardown. + */ + +export default () => { + if (global.stopLocalRegistry) { + global.stopLocalRegistry(); + } +};