diff --git a/.verdaccio/config.yml b/.verdaccio/config.yml index f74420f2..2e9f83b1 100644 --- a/.verdaccio/config.yml +++ b/.verdaccio/config.yml @@ -1,4 +1,5 @@ # path to a directory with all packages +# overwritten by --storage option storage: ../tmp/local-registry/storage # a list of other known repositories we can talk to diff --git a/e2e/cli-e2e-env/README.md b/e2e/cli-e2e-env/README.md index bb60d674..7f671ba5 100644 --- a/e2e/cli-e2e-env/README.md +++ b/e2e/cli-e2e-env/README.md @@ -1,3 +1,37 @@ # cli-e2e-env End-to-end tests for the `cli` library. + +### Changes/generated files during e2e (already refactored to multiple verdaccio instances) + +```sh +Root/ # πŸ‘ˆ this is your CWD +β”œβ”€β”€ dist/ +β”‚ └── packages/ +β”‚ └── /... +└── tmp/ + └── e2e/ + └── / # e2e setup + β”œβ”€β”€ storage/... # npm publish/unpublish + β”œβ”€β”€ node_modules/ + β”‚ └── + β”‚ └── /... # npm install/uninstall + β”œβ”€β”€ __test__/... + β”‚ └── /... # e2e beforeEach + β”‚ └── /... + β”œβ”€β”€ .npmrc # local npm config configured for project specific verdaccio registry + β”œβ”€β”€ package-lock.json # npm install/uninstall + └── package.json # npm install/uninstall +``` + +### Troubleshooting + +- `nx start-server` +- `nx start-server ` +- `nx start-server --storage tmp/e2e//storage` +- `nx start-env` +- `nx start-env --workspaceRoot tmp/e2e/` +- `nx npm-publish --envProject=` +- `nx run-many -t npm-publish --envProjectName=cli-e2e-env` +- `nx npm-install --envProject=` +- `nx run-many -t npm-install --envProjectName=cli-e2e-env` diff --git a/e2e/cli-e2e-env/project.json b/e2e/cli-e2e-env/project.json index 4b7f2e09..6f3b7219 100644 --- a/e2e/cli-e2e-env/project.json +++ b/e2e/cli-e2e-env/project.json @@ -10,6 +10,7 @@ "inputs": ["default", "^production"], "outputs": ["{options.reportsDirectory}"], "options": { + "globalSetup": ["e2e/cli-e2e-env/setup/global-setup.ts"], "reportsDirectory": "../../coverage/projects/cli-e2e-env" } } diff --git a/e2e/cli-e2e-env/setup/global-setup.ts b/e2e/cli-e2e-env/setup/global-setup.ts index a815b84a..bd71a4c6 100644 --- a/e2e/cli-e2e-env/setup/global-setup.ts +++ b/e2e/cli-e2e-env/setup/global-setup.ts @@ -1,10 +1,14 @@ -import { executeProcess } from '@org/test-utils'; -import { NpmTestEnvResult, startNpmEnv } from '../../../tools/utils/env'; +import { executeProcess, objectToCliArgs } from '@org/test-utils'; +import { NpmTestEnvResult } from '../../../tools/utils/env'; import { join, relative } from 'node:path'; import { rm } from 'node:fs/promises'; +import { VerdaccioExecuterOptions } from '../../../tools/utils/registry'; +import { readJsonFile } from '@nx/devkit'; -const isVerbose: boolean = process.env.NX_VERBOSE_LOGGING === 'true' ?? false; -const teardownEnv: boolean = process.env.E2E_TEARDOWN_ENV !== 'false'; +// DEBUG FLAGS +const isVerbose: boolean = true; // process.env.NX_VERBOSE_LOGGING === 'true' ?? false; +const teardownEnv: boolean = true; //process.env.E2E_TEARDOWN_ENV !== 'false'; +const teardownRegistry: boolean = true; //process.env.E2E_TEARDOWN_REGISTRY !== 'false'; let activeRegistry: NpmTestEnvResult; const projectName = process.env['NX_TASK_TARGET_PROJECT']; @@ -12,57 +16,60 @@ let stopRegistry; export async function setup() { // start registry - activeRegistry = await startNpmEnv({ - projectName, - workspaceRoot: join('tmp', 'e2e', projectName), - targetName: 'start-verdaccio', + await executeProcess({ + command: 'nx', + args: objectToCliArgs< + Partial< + VerdaccioExecuterOptions & { + _: string[]; + } + > + >({ + _: ['start-env', projectName ?? '', '--'], + verbose: isVerbose, + clear: true, + readyWhen: 'Environment ready under', + }), verbose: isVerbose, - clear: true, + shell: true, }); - const { registry, workspaceRoot, stop } = activeRegistry; - stopRegistry = stop; + const workspaceRoot = join('tmp', 'npm-env', projectName); + activeRegistry = await readJsonFile( + join(workspaceRoot, 'verdaccio-registry.json') + ); + const { registry } = activeRegistry; + stopRegistry = () => process.kill(Number(registry.pid)); - const { url } = registry; - const userconfig = join(workspaceRoot, '.npmrc'); // package publish all projects await executeProcess({ command: 'nx', - args: ['run-many', '-t=npm-publish', `--userconfig=${userconfig}`], - observer: { - onStdout: (stdout) => { - if (isVerbose) { - console.info(stdout); - } - }, - onStderr: (stdout) => { - if (isVerbose) { - console.error(stdout); - } - }, - }, + args: objectToCliArgs({ + _: ['run-many', '-t=npm-publish'], + verbose: isVerbose, + envProjectName: projectName, + }), + verbose: isVerbose, }); // package install all projects to test env folder await executeProcess({ command: 'nx', - args: ['run-many', '-t=npm-install', `--prefix=${workspaceRoot}`], - observer: { - onStdout: (stdout) => { - if (isVerbose) { - console.info(stdout); - } - }, - onStderr: (stdout) => { - if (isVerbose) { - console.error(stdout); - } - }, - }, + args: objectToCliArgs({ + _: ['run-many', '-t=npm-install'], + verbose: isVerbose, + envProjectName: projectName, + }), + verbose: isVerbose, }); } export async function teardown() { + console.table(activeRegistry); stopRegistry(); - // teardownEnv && await rm(activeRegistry.workspaceRoot, {recursive: true, force: true}); + if (teardownRegistry) { + } + await rm(activeRegistry.workspaceRoot, { recursive: true, force: true }); + if (teardownEnv) { + } } diff --git a/e2e/cli-e2e-env/test/cli.spec.ts b/e2e/cli-e2e-env/test/cli.spec.ts index 4ee83c52..648503c6 100644 --- a/e2e/cli-e2e-env/test/cli.spec.ts +++ b/e2e/cli-e2e-env/test/cli.spec.ts @@ -1,13 +1,15 @@ import { dirname, join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; -import { execSync } from 'node:child_process'; +import { mkdir, rm, writeFile } from 'node:fs/promises'; +import { executeProcess, objectToCliArgs } from '@org/test-utils'; describe('CLI command - sort', () => { - const baseDir = 'tmp/cli-e2e-env/sort'; + const workspaceRoot = 'tmp/npm-env/cli-e2e-env'; + const baseDir = join(workspaceRoot, 'sort'); + const userconfig = join(baseDir, '.npmrc'); afterEach(async () => { - // await rm(baseDir, {recursive: true, force: true}); + await rm(baseDir, { recursive: true, force: true }); }); it('should execute CLI command sort when param file is given', async () => { @@ -18,8 +20,18 @@ describe('CLI command - sort', () => { JSON.stringify([{ name: 'Michael' }, { name: 'Alice' }]) ); - expect(() => execSync(`npx @org/cli sort --file="${testPath}"`)).toThrow( - 'Command failed: npx @org/cli sort --file="tmp/cli/sort/file-sort/users.json"' + await expect( + executeProcess({ + command: 'npx', + args: objectToCliArgs({ + _: ['@org/cli', 'sort'], + file: testPath, + }), + cwd: workspaceRoot, + verbose: true, + }) + ).rejects.toThrow( + 'The "path" argument must be of type string or an instance of Buffer or URL' ); /* const content = (await readFile(testPath)).toString(); diff --git a/e2e/cli-e2e-graph/.eslintrc.json b/e2e/cli-e2e-graph/.eslintrc.json new file mode 100644 index 00000000..9f709c87 --- /dev/null +++ b/e2e/cli-e2e-graph/.eslintrc.json @@ -0,0 +1,30 @@ +{ + "extends": ["../../.eslintrc.json", "../../.eslintrc.base.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.json"], + "parser": "jsonc-eslint-parser", + "rules": { + "@nx/dependency-checks": [ + "error", + { + "ignoredFiles": ["{projectRoot}/vite.config.{js,ts,mjs,mts}"] + } + ] + } + } + ] +} diff --git a/e2e/cli-e2e-graph/README.md b/e2e/cli-e2e-graph/README.md new file mode 100644 index 00000000..745018b1 --- /dev/null +++ b/e2e/cli-e2e-graph/README.md @@ -0,0 +1,15 @@ +# cli-e2e-graph + +End-to-end tests for the `cli` library. + +### Troubleshooting + +- `nx start-server` +- `nx start-server ` +- `nx start-server --storage tmp/e2e//storage` +- `nx start-env` +- `nx start-env --workspaceRoot tmp/e2e/` +- `nx npm-publish --envProject=` +- `nx run-many -t npm-publish --envProjectName=cli-e2e-graph` +- `nx npm-install --envProject=` +- `nx run-many -t npm-install --envProjectName=cli-e2e-graph` diff --git a/e2e/cli-e2e-graph/project.json b/e2e/cli-e2e-graph/project.json new file mode 100644 index 00000000..f2fa573d --- /dev/null +++ b/e2e/cli-e2e-graph/project.json @@ -0,0 +1,18 @@ +{ + "name": "cli-e2e-graph", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "projects/cli-e2e-graph/test", + "projectType": "library", + "tags": ["npm-env"], + "targets": { + "e2e": { + "executor": "@nx/vite:test", + "inputs": ["default", "^production"], + "outputs": ["{options.reportsDirectory}"], + "options": { + "globalSetup": ["e2e/cli-e2e-graph/setup/global-setup.ts"], + "reportsDirectory": "../../coverage/projects/cli-e2e-graph" + } + } + } +} diff --git a/e2e/cli-e2e-graph/setup/global-setup.ts b/e2e/cli-e2e-graph/setup/global-setup.ts new file mode 100644 index 00000000..bf4de637 --- /dev/null +++ b/e2e/cli-e2e-graph/setup/global-setup.ts @@ -0,0 +1,86 @@ +import { executeProcess, objectToCliArgs } from '@org/test-utils'; +import { NpmTestEnvResult } from '../../../tools/utils/env'; +import { join, relative } from 'node:path'; +import { rm } from 'node:fs/promises'; +import { VerdaccioExecuterOptions } from '../../../tools/utils/registry'; +import { readJsonFile } from '@nx/devkit'; + +const isVerbose: boolean = process.env.NX_VERBOSE_LOGGING === 'true' ?? false; +const teardownEnv: boolean = process.env.E2E_TEARDOWN_ENV !== 'false'; + +let activeRegistry: NpmTestEnvResult; +const projectName = process.env['NX_TASK_TARGET_PROJECT']; +let stopRegistry; + +export async function setup() { + // start registry + await executeProcess({ + command: 'nx', + args: objectToCliArgs< + Partial< + VerdaccioExecuterOptions & { + _: string[]; + verbose: boolean; + cwd: string; + } + > + >({ + _: ['start-verdaccio', projectName ?? ''], + workspaceRoot: join('tmp', 'npm-env', projectName), + verbose: isVerbose, + clear: true, + }), + shell: true, + }); + + activeRegistry = await readJsonFile( + join('tmp', 'npm-env', projectName, 'verdaccio-registry.json') + ); + const { registry, workspaceRoot, stop } = activeRegistry; + stopRegistry = process.kill(Number(registry.pid)); + + const { url } = registry; + const userconfig = join(workspaceRoot, '.npmrc'); + // package publish all projects + await executeProcess({ + command: 'nx', + args: ['run-many', '-t=npm-publish', `--userconfig=${userconfig}`], + observer: { + onStdout: (stdout) => { + if (isVerbose) { + console.info(stdout); + } + }, + onStderr: (stdout) => { + if (isVerbose) { + console.error(stdout); + } + }, + }, + }); + + // package install all projects to test env folder + await executeProcess({ + command: 'nx', + args: ['run-many', '-t=npm-install', `--prefix=${workspaceRoot}`], + observer: { + onStdout: (stdout) => { + if (isVerbose) { + console.info(stdout); + } + }, + onStderr: (stdout) => { + if (isVerbose) { + console.error(stdout); + } + }, + }, + }); +} + +export async function teardown() { + const s = readJsonFile(''); + stopRegistry(); + teardownEnv && + (await rm(activeRegistry.workspaceRoot, { recursive: true, force: true })); +} diff --git a/e2e/cli-e2e-graph/test/cli.spec.ts b/e2e/cli-e2e-graph/test/cli.spec.ts new file mode 100644 index 00000000..79590a0e --- /dev/null +++ b/e2e/cli-e2e-graph/test/cli.spec.ts @@ -0,0 +1,32 @@ +import { dirname, join } from 'node:path'; +import { afterEach, describe, expect, it } from 'vitest'; +import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { execSync } from 'node:child_process'; + +describe('CLI command - sort', () => { + const baseDir = 'tmp/e2e/cli-e2e-graph/sort'; + + afterEach(async () => { + // await rm(baseDir, {recursive: true, force: true}); + }); + + it('should execute CLI command sort when param file is given', async () => { + const testPath = join(baseDir, 'file-sort', 'users.json'); + await mkdir(dirname(testPath), { recursive: true }); + await writeFile( + testPath, + JSON.stringify([{ name: 'Michael' }, { name: 'Alice' }]) + ); + + expect(() => execSync(`npx @org/cli sort --file="${testPath}"`)).toThrow( + 'Command failed: npx @org/cli sort --file="tmp/cli-e2e-graph/sort/file-sort/users.json"' + ); + /* + const content = (await readFile(testPath)).toString(); + expect(JSON.parse(content)).toEqual([ + {name: 'Alice'}, + {name: 'Michael'}, + ]); + */ + }); +}); diff --git a/e2e/cli-e2e-graph/tsconfig.json b/e2e/cli-e2e-graph/tsconfig.json new file mode 100644 index 00000000..821ca814 --- /dev/null +++ b/e2e/cli-e2e-graph/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "esnext", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/tools/tsconfig.spec.json b/e2e/cli-e2e-graph/tsconfig.spec.json similarity index 56% rename from tools/tsconfig.spec.json rename to e2e/cli-e2e-graph/tsconfig.spec.json index 3c002c21..6556415d 100644 --- a/tools/tsconfig.spec.json +++ b/e2e/cli-e2e-graph/tsconfig.spec.json @@ -13,14 +13,14 @@ "include": [ "vite.config.ts", "vitest.config.ts", - "src/**/*.test.ts", - "src/**/*.spec.ts", - "src/**/*.test.tsx", - "src/**/*.spec.tsx", - "src/**/*.test.js", - "src/**/*.spec.js", - "src/**/*.test.jsx", - "src/**/*.spec.jsx", - "src/**/*.d.ts" + "test/**/*.test.ts", + "test/**/*.spec.ts", + "test/**/*.test.tsx", + "test/**/*.spec.tsx", + "test/**/*.test.js", + "test/**/*.spec.js", + "test/**/*.test.jsx", + "test/**/*.spec.jsx", + "test/**/*.d.ts" ] } diff --git a/e2e/cli-e2e-graph/vite.config.ts b/e2e/cli-e2e-graph/vite.config.ts new file mode 100644 index 00000000..2e7dbda0 --- /dev/null +++ b/e2e/cli-e2e-graph/vite.config.ts @@ -0,0 +1,28 @@ +import { defineConfig } from 'vite'; + +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../../node_modules/.vite/projects/cli', + + plugins: [nxViteTsPaths()], + + // Uncomment this if you are using workers. + // worker: { + // plugins: [ nxViteTsPaths() ], + // }, + + test: { + globals: true, + cache: { dir: '../../node_modules/.vitest' }, + environment: 'node', + include: ['test/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + reporters: ['default'], + globalSetup: ['./setup/global-setup.ts'], + coverage: { + reportsDirectory: '../../coverage/projects/cli', + provider: 'v8', + }, + }, +}); diff --git a/e2e/cli-e2e-original/README.md b/e2e/cli-e2e-original/README.md index 6142c9af..cc100c68 100644 --- a/e2e/cli-e2e-original/README.md +++ b/e2e/cli-e2e-original/README.md @@ -1,3 +1,60 @@ # cli-e2e-original End-to-end tests for the `cli` library. + +## Running the tests + +- `nx e2e cli-e2e-original` - run E2E tests for the `cli-e2e-original` library. + - run vitest setup - `e2e/cli-e2e-original/setup/global-setup.ts#setup` + - `nx local-registry --storage` + - `nx run-many --targets=nx-release-publish` + - `nx run-many --targets=npm-install-e2e` + - run tests + - run vitest teardown - `e2e/cli-e2e-original/setup/global-setup.ts#teardown` + - stop server + - delete folder + +### Included targets + +- `workspace-source` + - targets + - `local-registry` +- `cli-e2e-original` + - targets + - `e2e` +- `models`,`utils`,`core`,`cli` + - tags + - `publishable` + - targets + - `original-npm.install` + - `original-npm-uninstall` + +### Changed or generated files during e2e + +```sh +User/ + └── / + β”œβ”€β”€ .npmrc # πŸ”“ added registry and token entry to OS user specific npm config + └──Root/ # πŸ‘ˆ this is your CWD + β”œβ”€β”€ node_modules/ + β”‚ └── + β”‚ └── /... # πŸ”“ npm install installs into repository folder + β”œβ”€β”€ dist/ + β”‚ └── packages/ + β”‚ └── /... + β”œβ”€β”€ tmp/ + β”‚ └── local-registry/ # πŸ˜“ hard to debug a dynamic port + β”‚ β”œβ”€β”€ storage/... + β”‚ β”‚ └── + β”‚ β”‚ └── /... # nx nx-release-publish saves the package's tarball here + β”‚ └── /... + β”‚ └── /... + β”œβ”€β”€ package-lock.json # πŸ”“ npm install/uninstall installs into workspace root + └── package.json # πŸ”“ npm install/uninstall installs into workspace root +``` + +## Troubleshooting + +- `nx start-server` +- `nx start-server ` +- `nx start-server --storage tmp/e2e//storage` diff --git a/e2e/cli-e2e-original/setup/global-setup.ts b/e2e/cli-e2e-original/setup/global-setup.ts index 0bc9ceab..fb91cf09 100644 --- a/e2e/cli-e2e-original/setup/global-setup.ts +++ b/e2e/cli-e2e-original/setup/global-setup.ts @@ -1,60 +1,64 @@ -import { join } from 'node:path'; -import { executeProcess } from '@org/test-utils'; -import { startVerdaccioServer } from '../../../tools/utils/registry'; +import { executeProcess, objectToCliArgs } from '@org/test-utils'; +import { + startVerdaccioServer, + VercaddioServerResult, +} from '../../../tools/utils/registry'; +import { rm } from 'node:fs/promises'; +import { + configureRegistry, + unconfigureRegistry, +} from '../../../tools/utils/npm'; +import * as process from 'process'; const isVerbose: boolean = true; // process.env.NX_VERBOSE_LOGGING === 'true' ?? false; -let stopRegistry; + +let activeRegistry: VercaddioServerResult; +let stopRegistry: () => void; export async function setup() { - // start registry - stopRegistry = await startVerdaccioServer({ + // start Verdaccio server and setup local registry storage + const { stop, registry } = await startVerdaccioServer({ targetName: 'local-registry', - storage: join('tmp', 'cli-source', 'local-registry', 'storage'), verbose: isVerbose, - port: '4873', }); + activeRegistry = registry; + stopRegistry = stop; + + // configure env with verdaccio registry as default + // exec commands: + // - `npm config set //${host}:${port}/:_authToken "secretVerdaccioToken"` + // - `npm config set registry "${url}"` + configureRegistry(registry, isVerbose); // package publish all projects await executeProcess({ command: 'nx', - args: [ - 'run-many', - '-t=nx-release-publish', - '--registry=http://localhost:4873', - ], - observer: { - onStdout: (stdout) => { - if (isVerbose) { - console.info(stdout); - } - }, - onStderr: (stdout) => { - if (isVerbose) { - console.error(stdout); - } - }, - }, + args: objectToCliArgs({ _: ['run-many'], targets: 'nx-release-publish' }), + verbose: isVerbose, }); + // package install all projects await executeProcess({ command: 'nx', - args: ['run-many', '-t=npm-install', '--registry=http://localhost:4873'], - observer: { - onStdout: (stdout) => { - if (isVerbose) { - console.info(stdout); - } - }, - onStderr: (stdout) => { - if (isVerbose) { - console.error(stdout); - } - }, - }, + args: objectToCliArgs({ _: ['run-many'], targets: 'original-npm-install' }), + verbose: isVerbose, }); } export async function teardown() { - // stop registry - stopRegistry(); + // uninstall all projects + await executeProcess({ + command: 'nx', + args: objectToCliArgs({ + _: ['run-many'], + targets: 'original-npm-uninstall', + }), + verbose: isVerbose, + }); + // stopRegistry(); + // exec commands: + // - `npm config delete //${host}:${port}/:_authToken` + // - `npm config delete registry` + // unconfigureRegistry(activeRegistry, isVerbose); + // await rm(activeRegistry.storage, {recursive: true, force: true}); } diff --git a/e2e/cli-e2e-original/test/cli-command-sort.spec.ts b/e2e/cli-e2e-original/test/cli-command-sort.spec.ts new file mode 100644 index 00000000..3a8edefa --- /dev/null +++ b/e2e/cli-e2e-original/test/cli-command-sort.spec.ts @@ -0,0 +1,44 @@ +import { dirname, join } from 'node:path'; +import { afterEach, describe, expect, it } from 'vitest'; +import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { execSync } from 'node:child_process'; +import { executeProcess, objectToCliArgs } from '@org/test-utils'; + +describe('CLI command - sort', () => { + const workspaceRoot = join('tmp', 'cli-e2e-original'); + const baseDir = join(workspaceRoot, 'cli-command-sort'); + + afterEach(async () => { + // await rm(baseDir, {recursive: true, force: true}); + }); + + it('should execute CLI command sort when param file is given', async () => { + const testPath = join(baseDir, 'execute-sort-command', 'users.json'); + await mkdir(dirname(testPath), { recursive: true }); + await writeFile( + testPath, + JSON.stringify([{ name: 'Michael' }, { name: 'Alice' }]) + ); + + await expect( + executeProcess({ + command: 'npx', + args: objectToCliArgs({ + _: ['@org/cli', 'sort'], + file: testPath, + }), + cwd: workspaceRoot, + verbose: true, + }) + ).rejects.toThrow( + 'The "path" argument must be of type string or an instance of Buffer or URL' + ); + /* + const content = (await readFile(testPath)).toString(); + expect(JSON.parse(content)).toEqual([ + {name: 'Alice'}, + {name: 'Michael'}, + ]); + */ + }); +}); diff --git a/e2e/cli-e2e-original/test/cli.spec.ts b/e2e/cli-e2e-original/test/cli.spec.ts deleted file mode 100644 index e41f1633..00000000 --- a/e2e/cli-e2e-original/test/cli.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {dirname, join} from 'node:path'; -import {afterEach, describe, expect, it} from "vitest"; -import {mkdir, readFile, rm, writeFile} from "node:fs/promises"; -import {execSync} from "node:child_process"; - -describe('CLI command - sort', () => { - const baseDir = 'tmp/cli/sort'; - - afterEach(async () => { - await rm(baseDir, {recursive: true, force: true}); - }); - - it('should execute CLI command sort when param file is given', async () => { - const testPath = join(baseDir, 'file-sort', 'users.json'); - await mkdir(dirname(testPath), {recursive: true}); - await writeFile(testPath, JSON.stringify([ - {name: 'Michael'}, - {name: 'Alice'}, - ])); - - expect(() => execSync(`npx @org/cli sort --file="${testPath}"`)).toThrow('Command failed: npx @org/cli sort --file="tmp/cli/sort/file-sort/users.json"'); - /* - const content = (await readFile(testPath)).toString(); - expect(JSON.parse(content)).toEqual([ - {name: 'Alice'}, - {name: 'Michael'}, - ]); - */ - }); -}); diff --git a/e2e/cli-e2e-original/tsconfig.spec.json b/e2e/cli-e2e-original/tsconfig.spec.json index 6556415d..e0e98c7e 100644 --- a/e2e/cli-e2e-original/tsconfig.spec.json +++ b/e2e/cli-e2e-original/tsconfig.spec.json @@ -13,6 +13,7 @@ "include": [ "vite.config.ts", "vitest.config.ts", + "setup/**/*.ts", "test/**/*.test.ts", "test/**/*.spec.ts", "test/**/*.test.tsx", diff --git a/package-lock.json b/package-lock.json index ba28a952..bbed547c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,8 @@ "@nx/vite": "18.2.4", "@nx/web": "18.2.4", "@nx/workspace": "18.2.4", + "@org/cli": "^0.0.1", + "@org/utils": "^0.0.1", "@swc-node/register": "~1.8.0", "@swc/core": "^1.7.22", "@swc/helpers": "~0.5.2", @@ -34,6 +36,7 @@ "eslint-config-prettier": "^9.0.0", "nx": "18.2.4", "prettier": "^2.6.2", + "tsx": "^4.19.0", "typescript": "~5.4.2", "verdaccio": "^5.0.4", "vite": "~5.0.0", @@ -2306,6 +2309,22 @@ "node": ">=12" } }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/openbsd-x64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", @@ -3064,6 +3083,21 @@ "yargs-parser": "21.1.1" } }, + "node_modules/@org/cli": { + "version": "0.0.1", + "resolved": "http://localhost:4874/@org/cli/-/cli-0.0.1.tgz", + "integrity": "sha512-B6SUXBm+5F9ssThtbWg/350RS5aIPSFnUo7fqXSwbYyrrL2DZUm8rENj3G0Cae3Cu/8bFLQNdiLSRwgoWIVNDA==", + "dev": true, + "bin": { + "cli": "bin.js" + } + }, + "node_modules/@org/utils": { + "version": "0.0.1", + "resolved": "http://localhost:4874/@org/utils/-/utils-0.0.1.tgz", + "integrity": "sha512-kGTKsWoXYfbSsvJ4Bf/6wGBl4e5pnA7lnnrgvbfx/lQcjdbmNriCDWJT/LPp/Z+E5Qy8RaesRk67T3GRqKjXTA==", + "dev": true + }, "node_modules/@phenomnomnominal/tsquery": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", @@ -6863,6 +6897,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.0.tgz", + "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/getpass": { "version": "0.1.7", "dev": true, @@ -9244,6 +9290,15 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -10073,6 +10128,432 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "dev": true }, + "node_modules/tsx": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz", + "integrity": "sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "dev": true, diff --git a/package.json b/package.json index 0c1628dd..54dcd35b 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "@nx/vite": "18.2.4", "@nx/web": "18.2.4", "@nx/workspace": "18.2.4", + "@org/cli": "^0.0.1", + "@org/utils": "^0.0.1", "@swc-node/register": "~1.8.0", "@swc/core": "^1.7.22", "@swc/helpers": "~0.5.2", @@ -30,6 +32,7 @@ "eslint-config-prettier": "^9.0.0", "nx": "18.2.4", "prettier": "^2.6.2", + "tsx": "^4.19.0", "typescript": "~5.4.2", "verdaccio": "^5.0.4", "vite": "~5.0.0", @@ -37,5 +40,9 @@ }, "nx": { "includedScripts": [] - } + }, + "description": "", + "main": "index.js", + "keywords": [], + "author": "" } diff --git a/project.json b/project.json index 0ab138a6..eae9a59d 100644 --- a/project.json +++ b/project.json @@ -1,14 +1,5 @@ { - "name": "@org/source", + "name": "workspace-source", "$schema": "node_modules/nx/schemas/project-schema.json", - "targets": { - "local-registry": { - "executor": "@nx/js:verdaccio", - "options": { - "port": 4873, - "config": ".verdaccio/config.yml", - "storage": "tmp/local-registry/storage" - } - } - } + "targets": {} } diff --git a/testing/test-utils/src/lib/execute-process.ts b/testing/test-utils/src/lib/execute-process.ts index b915345d..02d974a5 100644 --- a/testing/test-utils/src/lib/execute-process.ts +++ b/testing/test-utils/src/lib/execute-process.ts @@ -34,6 +34,7 @@ export type ProcessConfig = Omit< > & { command: string; args?: string[]; + verbose?: boolean; observer?: ProcessObserver; ignoreExitCode?: boolean; }; @@ -46,7 +47,14 @@ export type ProcessObserver = { }; export function executeProcess(cfg: ProcessConfig): Promise { - const { command, args, observer, ignoreExitCode = false, ...options } = cfg; + const { + command, + args, + observer, + verbose = false, + ignoreExitCode = false, + ...options + } = cfg; const { onStdout, onStderr, onError, onComplete } = observer ?? {}; const date = new Date().toISOString(); const start = performance.now(); @@ -61,21 +69,27 @@ export function executeProcess(cfg: ProcessConfig): Promise { let stdout = ''; let stderr = ''; - spawnedProcess.stdout.on('data', data => { + spawnedProcess.stdout.on('data', (data) => { stdout += String(data); + if (verbose) { + console.info(String(data)); + } onStdout?.(String(data), spawnedProcess); }); - spawnedProcess.stderr.on('data', data => { + spawnedProcess.stderr.on('data', (data) => { stderr += String(data); + if (verbose) { + console.error(String(data)); + } onStderr?.(String(data), spawnedProcess); }); - spawnedProcess.on('error', err => { + spawnedProcess.on('error', (err) => { stderr += err.toString(); }); - spawnedProcess.on('close', code => { + spawnedProcess.on('close', (code) => { const timings = { date, duration: performance.now() - start }; if (code === 0 || ignoreExitCode) { onComplete?.(); diff --git a/tools/bin/npm-env.ts b/tools/bin/npm-env.ts new file mode 100644 index 00000000..9dfa1aed --- /dev/null +++ b/tools/bin/npm-env.ts @@ -0,0 +1,28 @@ +import { startNpmEnv, StartVerdaccioAndSetupEnvOptions } from '../utils/env'; +import yargs, { Options } from 'yargs'; + +const isVerbose: boolean = process.env.NX_VERBOSE_LOGGING === 'true' ?? false; + +const args = yargs(process.argv.slice(2)) + .version(false) + .options({ + projectName: { + type: 'string', + description: 'Project name', + demandOption: true, + }, + workspaceRoot: { + type: 'string', + description: 'Location of test environment', + }, + verbose: { + type: 'boolean', + description: 'Verbose logging', + default: isVerbose, + }, + } satisfies Partial>) + .parse() as StartVerdaccioAndSetupEnvOptions; + +(async () => { + await startNpmEnv(args); +})(); diff --git a/tools/plugins/npm-env.plugin.ts b/tools/plugins/npm-env.plugin.ts index 00ef2874..3a80485b 100644 --- a/tools/plugins/npm-env.plugin.ts +++ b/tools/plugins/npm-env.plugin.ts @@ -5,6 +5,9 @@ import { } from '@nx/devkit'; import { dirname, join, relative } from 'node:path'; import type { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json'; +import { RunCommandsOptions } from 'nx/src/executors/run-commands/run-commands.impl'; + +const tmpNpmEnv = join('tmp', 'npm-env'); export const createNodes: CreateNodes = [ '**/project.json', @@ -25,7 +28,7 @@ export const createNodes: CreateNodes = [ projects: { [root]: { targets: { - ...(isNpmEnv && verdaccioTargets()), + ...(isNpmEnv && verdaccioTargets(projectConfiguration)), ...(isPublishable && npmTargets({ ...projectConfiguration, root })), }, }, @@ -34,46 +37,63 @@ export const createNodes: CreateNodes = [ }, ]; -function verdaccioTargets() { +function verdaccioTargets(projectConfiguration: ProjectConfiguration) { + const { name: projectName } = projectConfiguration; return { 'start-verdaccio': { executor: '@nx/js:verdaccio', options: { config: '.verdaccio/config.yml', + storage: join('tmp', 'local-registry', 'storage'), + }, + }, + 'start-env': { + command: 'tsx --tsconfig=tools/tsconfig.tools.json tools/bin/npm-env.ts', + options: { + projectName, + targetName: 'start-verdaccio', + workspaceRoot: join(tmpNpmEnv, projectName), + location: 'none', }, }, }; } +const relativeFromPath = (dir) => + relative(join(process.cwd(), dir), join(process.cwd())); + function npmTargets( projectConfiguration: ProjectConfiguration ): Record { - const { root, name, targets } = projectConfiguration; + const { root, name: projectName, targets } = projectConfiguration; const { build } = targets; const { options } = build; const { outputPath } = options; if (outputPath == null) { throw new Error('outputPath is required'); } - const relativeFromOutputPath = relative( - join(process.cwd(), outputPath), - join(process.cwd()) - ); + const { name: packageName, version: pkgVersion } = readJsonFile( join(root, 'package.json') ); + return { 'npm-publish': { - command: `npm publish --userconfig=${relativeFromOutputPath}/{args.userconfig}`, + command: `npm publish --userconfig=${relativeFromPath( + outputPath + )}/${tmpNpmEnv}/{args.envProjectName}/.npmrc`, options: { cwd: outputPath, + envProjectName: `${projectName}-npm-env`, }, }, 'npm-install': { - command: `npm install --no-fund --no-shrinkwrap --no-save ${packageName}@{args.pkgVersion} --perfix={args.prefix} --userconfig={args.prefix}/.npmrc`, + command: `npm install --no-fund --no-shrinkwrap --save ${packageName}@{args.pkgVersion} --prefix=${tmpNpmEnv}/{args.envProjectName} --userconfig=${relativeFromPath( + outputPath + )}/${tmpNpmEnv}/{args.envProjectName}/.npmrc`, options: { pkgVersion, - prefix: '.', + envProjectName: `${projectName}-npm-env`, }, }, 'npm-uninstall': { diff --git a/tools/plugins/original.plugin.ts b/tools/plugins/original.plugin.ts index f798e810..cc9740ba 100644 --- a/tools/plugins/original.plugin.ts +++ b/tools/plugins/original.plugin.ts @@ -11,35 +11,53 @@ export const createNodes: CreateNodes = [ ); const isPublishable = (projectConfiguration?.tags ?? []).some( - (target) => target === 'publishable' + (tag) => tag === 'publishable' ); - if (!isPublishable) { - return {}; - } + const isRoot = root === '.'; return { projects: { [root]: { - targets: npmTargets({ ...projectConfiguration, root }), + targets: { + ...(isRoot && verdaccioTargets({ ...projectConfiguration, root })), + ...(isPublishable && npmTargets({ ...projectConfiguration, root })), + }, }, }, }; }, ]; +function verdaccioTargets(projectConfiguration: ProjectConfiguration) { + const { root, name } = projectConfiguration; + const { name: packageName, version: pkgVersion } = readJsonFile( + join(root, 'package.json') + ); + + return { + 'local-registry': { + executor: '@nx/js:verdaccio', + options: { + config: '.verdaccio/config.yml', + storage: `tmp/local-registry/storage`, + }, + }, + }; +} + function npmTargets(projectConfiguration: ProjectConfiguration) { const { root, name } = projectConfiguration; const { name: packageName, version: pkgVersion } = readJsonFile( join(root, 'package.json') ); return { - 'npm-install': { + 'original-npm-install': { command: `npm install -D ${packageName}@{args.pkgVersion}`, options: { pkgVersion, }, }, - 'npm-uninstall': { + 'original-npm-uninstall': { command: `npm uninstall ${packageName}`, }, }; diff --git a/tools/tsconfig.json b/tools/tsconfig.json index 81610033..212767e1 100644 --- a/tools/tsconfig.json +++ b/tools/tsconfig.json @@ -13,7 +13,7 @@ "include": [], "references": [ { - "path": "./tsconfig.spec.json" + "path": "./tsconfig.tools.json" } ] } diff --git a/tools/tsconfig.tools.json b/tools/tsconfig.tools.json new file mode 100644 index 00000000..4b80da18 --- /dev/null +++ b/tools/tsconfig.tools.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "include": ["src/**/*.ts", "src/**/*.d.ts"] +} diff --git a/tools/utils/env.ts b/tools/utils/env.ts index 4d3c1dc2..21513ec7 100644 --- a/tools/utils/env.ts +++ b/tools/utils/env.ts @@ -1,10 +1,10 @@ import { join } from 'node:path'; import { - Registry, + VercaddioServerResult, startVerdaccioServer, StarVerdaccioOptions, } from './registry'; -import { rm } from 'node:fs/promises'; +import { rm, writeFile } from 'node:fs/promises'; import { configureRegistry, setupNpmWorkspace } from './npm'; export type VerdaccioEnv = { @@ -17,20 +17,32 @@ export type StartVerdaccioAndSetupEnvOptions = Partial< Pick; export type NpmTestEnvResult = VerdaccioEnv & { - registry: Registry; + registry: VercaddioServerResult; stop: () => void; }; export async function startNpmEnv({ projectName, - targetName, - port, verbose = false, - workspaceRoot = '.', + targetName = 'start-verdaccio', + workspaceRoot = join('tmp', 'npm-env', projectName), location = 'none', - // reset or remove cached packages and/or metadata. - clear = true, + port, + clear, }: StartVerdaccioAndSetupEnvOptions): Promise { + if (verbose) { + console.info( + `Start NPM environment with params: ${{ + projectName, + verbose, + targetName, + workspaceRoot, + location, + port, + clear, + }}` + ); + } const storage = join(workspaceRoot, 'storage'); const registryResult = await startVerdaccioServer({ targetName, @@ -47,10 +59,25 @@ export async function startNpmEnv({ const userconfig = join(workspaceRoot, '.npmrc'); configureRegistry({ ...registryResult.registry, userconfig }, verbose); - return { + const activeRegistry: NpmTestEnvResult = { ...registryResult, workspaceRoot, - } satisfies NpmTestEnvResult; + }; + + console.info( + `Save active verdaccio registry data to file: ${join( + activeRegistry.workspaceRoot + )}` + ); + await writeFile( + join(activeRegistry.workspaceRoot, 'verdaccio-registry.json'), + JSON.stringify(activeRegistry.registry, null, 2) + ); + console.info( + `Environment ready under: ${join(activeRegistry.workspaceRoot)}` + ); + + return activeRegistry; } export async function stopVerdaccioAndTeardownEnv(result: NpmTestEnvResult) { diff --git a/tools/utils/npm.ts b/tools/utils/npm.ts index e630ec79..739b3250 100644 --- a/tools/utils/npm.ts +++ b/tools/utils/npm.ts @@ -1,12 +1,17 @@ -import { bold, gray, red } from 'ansis'; +import { bold, gray, red, whiteBright, bgBlue } from 'ansis'; import { execFileSync, execSync } from 'node:child_process'; import { join } from 'node:path'; import { objectToCliArgs } from '@org/test-utils'; -import { Registry } from './registry'; +import { VercaddioServerResult, VerdaccioProcessResult } from './registry'; import { ensureDirectoryExists } from './utils'; export function configureRegistry( - { port, host, url, userconfig }: Registry & { userconfig: string }, + { + port, + host, + url, + userconfig, + }: VerdaccioProcessResult & { userconfig?: string }, verbose?: boolean ) { /** @@ -34,7 +39,39 @@ export function configureRegistry( }).join(' ')}`; if (verbose) { console.info( - `${gray('>')} ${gray(bold('Verdaccio-Env'))} Set registry:\n${userconfig}` + `${bgBlue( + whiteBright(bold(' Verdaccio-Env ')) + )} Set registry:\n${setRegistry}` + ); + } + execSync(setRegistry); +} + +export function unconfigureRegistry( + { port, host, userconfig }: VerdaccioProcessResult & { userconfig?: string }, + verbose?: boolean +) { + const urlNoProtocol = `//${host}:${port}`; + const setAuthToken = `npm config delete ${urlNoProtocol}/:_authToken ${objectToCliArgs( + { userconfig } + ).join(' ')}`; + if (verbose) { + console.info( + `${gray('>')} ${gray( + bold('Verdaccio-Env') + )} Delete authToken:\n${setAuthToken}` + ); + } + execSync(setAuthToken); + + const setRegistry = `npm config delete registry ${objectToCliArgs({ + userconfig, + }).join(' ')}`; + if (verbose) { + console.info( + `${gray('>')} ${gray( + bold('Verdaccio-Env') + )} Delete registry:\n${setRegistry}` ); } execSync(setRegistry); diff --git a/tools/utils/registry.ts b/tools/utils/registry.ts index f7d3a5ee..33dacfe8 100644 --- a/tools/utils/registry.ts +++ b/tools/utils/registry.ts @@ -1,21 +1,23 @@ import { gray, bold, red } from 'ansis'; import { executeProcess, objectToCliArgs } from '@org/test-utils'; +import { join } from 'node:path'; -export type RegistryServer = { +export type VerdaccioProcessResult = { protocol: string; port: string | number; host: string; url: string; }; -export type Registry = RegistryServer & - Required>; +export type VercaddioServerResult = VerdaccioProcessResult & { + pid: number; +} & Required>; export type RegistryResult = { - registry: Registry; + registry: VercaddioServerResult; stop: () => void; }; -export function parseRegistryData(stdout: string): RegistryServer { +export function parseRegistryData(stdout: string): VerdaccioProcessResult { const output = stdout.toString(); // Extract protocol, host, and port @@ -73,27 +75,19 @@ export type StarVerdaccioOptions = VerdaccioExecuterOptions & export async function startVerdaccioServer({ targetName = 'local-registry', projectName = '', - storage, + storage = join('tmp', targetName, 'storage'), port, location, - clear, + clear = true, verbose = false, }: StarVerdaccioOptions): Promise { let startDetected = false; return new Promise((resolve, reject) => { executeProcess({ - command: 'npm', - args: objectToCliArgs< - Partial< - VerdaccioExecuterOptions & { - _: string[]; - verbose: boolean; - cwd: string; - } - > - >({ - _: ['exec', 'nx', targetName, projectName ?? '', '--'], + command: 'nx', + args: objectToCliArgs({ + _: [targetName, projectName ?? '', '--'], storage, port, verbose, @@ -115,6 +109,7 @@ export async function startVerdaccioServer({ const result: RegistryResult = { registry: { + pid: childProcess?.pid, storage, ...parseRegistryData(stdout), }, @@ -126,7 +121,7 @@ export async function startVerdaccioServer({ `${red('>')} ${gray( bold('Verdaccio') )} Can't kill Verdaccio process with id: ${ - childProcess.pid + childProcess?.pid }` ); }