From 778400b1cf58d97d26de2a2d6488f45841e64342 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Fri, 31 Mar 2023 16:27:37 -0400 Subject: [PATCH 1/4] fix(misc): create-nx-workspace should show some stdout if stderr empty on error (#16020) --- packages/create-nx-workspace/src/utils/child-process-utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/create-nx-workspace/src/utils/child-process-utils.ts b/packages/create-nx-workspace/src/utils/child-process-utils.ts index 708e12aa85067b..9b57d4cb971225 100644 --- a/packages/create-nx-workspace/src/utils/child-process-utils.ts +++ b/packages/create-nx-workspace/src/utils/child-process-utils.ts @@ -33,7 +33,8 @@ export function execAndWait(command: string, cwd: string) { if (error) { const logFile = join(cwd, 'error.log'); writeFileSync(logFile, `${stdout}\n${stderr}`); - rej(new CreateNxWorkspaceError(stderr, error.code, logFile)); + const message = stderr && stderr.trim().length ? stderr : stdout; + rej(new CreateNxWorkspaceError(message, error.code, logFile)); } else { res({ code: 0, stdout }); } From bfea455e64d659702ef865646f4f17f0bb719829 Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Fri, 31 Mar 2023 16:37:14 -0400 Subject: [PATCH 2/4] fix(bundling): add missing @babel/core dependency to @nrwl/webpack (#16023) --- packages/webpack/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/webpack/package.json b/packages/webpack/package.json index 942a505f142bab..3e7bab39690509 100644 --- a/packages/webpack/package.json +++ b/packages/webpack/package.json @@ -29,6 +29,7 @@ "migrations": "./migrations.json" }, "dependencies": { + "@babel/core": "^7.15.0", "@nrwl/devkit": "file:../devkit", "@nrwl/js": "file:../js", "@nrwl/workspace": "file:../workspace", From 39646cfa9ad173251a7171773c15c45173712755 Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Fri, 31 Mar 2023 17:22:34 -0400 Subject: [PATCH 3/4] feat(core): allow referencing other packages to specify implementations for executors + generators (#15987) --- packages/nx/src/config/workspaces.ts | 55 ++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/packages/nx/src/config/workspaces.ts b/packages/nx/src/config/workspaces.ts index cf4348bd66e5b7..4030440f46bbee 100644 --- a/packages/nx/src/config/workspaces.ts +++ b/packages/nx/src/config/workspaces.ts @@ -40,7 +40,6 @@ import { import { getNxRequirePaths } from '../utils/installation-directory'; import { getIgnoredGlobs } from '../utils/ignore'; import { - createProjectRootMappings, findProjectForPath, normalizeProjectRoot, } from '../project-graph/utils/find-project-for-path'; @@ -183,7 +182,10 @@ export class Workspaces { const { executorsFilePath, executorConfig, isNgCompat } = this.readExecutorsJson(nodeModule, executor); const executorsDir = path.dirname(executorsFilePath); - const schemaPath = path.join(executorsDir, executorConfig.schema || ''); + const schemaPath = this.resolveSchema( + executorConfig.schema, + executorsDir + ); const schema = normalizeExecutorSchema(readJsonFile(schemaPath)); const implementationFactory = this.getImplementationFactory( @@ -246,7 +248,10 @@ export class Workspaces { generatorsJson.generators?.[normalizedGeneratorName] || generatorsJson.schematics?.[normalizedGeneratorName]; const isNgCompat = !generatorsJson.generators?.[normalizedGeneratorName]; - const schemaPath = path.join(generatorsDir, generatorConfig.schema || ''); + const schemaPath = this.resolveSchema( + generatorConfig.schema, + generatorsDir + ); const schema = readJsonFile(schemaPath); if (!schema.properties || typeof schema.properties !== 'object') { schema.properties = {}; @@ -346,11 +351,10 @@ export class Workspaces { const [implementationModulePath, implementationExportName] = implementation.split('#'); return () => { - const possibleModulePath = path.join(directory, implementationModulePath); - const validImplementations = ['', '.js', '.ts'].map( - (x) => possibleModulePath + x + const modulePath = this.resolveImplementation( + implementationModulePath, + directory ); - const modulePath = validImplementations.find((f) => existsSync(f)); if (extname(modulePath) === '.ts') { registerPluginTSTranspiler(); } @@ -361,6 +365,43 @@ export class Workspaces { }; } + private resolveSchema(schemaPath: string, directory: string): string { + const maybeSchemaPath = join(directory, schemaPath); + if (existsSync(maybeSchemaPath)) { + return maybeSchemaPath; + } + + return require.resolve(schemaPath, { + paths: [directory], + }); + } + + private resolveImplementation( + implementationModulePath: string, + directory: string + ): string { + const validImplementations = ['', '.js', '.ts'].map( + (x) => implementationModulePath + x + ); + + for (const maybeImplementation of validImplementations) { + const maybeImplementationPath = join(directory, maybeImplementation); + if (existsSync(maybeImplementationPath)) { + return maybeImplementationPath; + } + + try { + return require.resolve(maybeImplementation, { + paths: [directory], + }); + } catch {} + } + + throw new Error( + `Could not resolve "${implementationModulePath}" from "${directory}".` + ); + } + private readExecutorsJson(nodeModule: string, executor: string) { const { json: packageJson, path: packageJsonPath } = readPluginPackageJson( nodeModule, From 6e7234c1aac7b96d3e367149fd02db9013eedc3b Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Fri, 31 Mar 2023 17:23:21 -0400 Subject: [PATCH 4/4] feat(nx-plugin): reuse utilities from create-nx-workspace for create-nx-plugin (#15743) --- .eslintrc.json | 3 +- docs/generated/manifests/menus.json | 8 + docs/generated/manifests/packages.json | 9 + docs/generated/packages-metadata.json | 9 + .../packages/nx-plugin/generators/preset.json | 28 ++ e2e/utils/create-project-utils.ts | 2 +- .../src/create-nx-plugin.test.ts | 11 +- packages/create-nx-plugin/.eslintrc.json | 12 +- .../create-nx-plugin/bin/create-nx-plugin.ts | 303 +++++++--------- .../bin/detect-invoked-package-manager.ts | 32 -- packages/create-nx-plugin/bin/shared.ts | 106 ------ packages/create-nx-plugin/package.json | 7 +- .../bin/create-nx-workspace.ts | 325 ++++-------------- packages/create-nx-workspace/index.ts | 1 + .../src/create-workspace.ts | 3 +- .../src/internal-utils/prompts.ts | 159 +++++++++ .../src/internal-utils/yargs-options.ts | 79 +++++ .../src/rules/nx-plugin-checks.ts | 10 +- .../library/files/lib/package.json__tmpl__ | 5 - packages/js/src/generators/library/library.ts | 39 ++- packages/js/src/utils/schema.d.ts | 1 + packages/nx-plugin/generators.json | 7 + .../src/generators/e2e-project/e2e.ts | 11 +- .../src/generators/e2e-project/schema.d.ts | 1 + .../src/generators/executor/executor.spec.ts | 2 +- .../src/generators/executor/executor.ts | 2 +- .../generators/generator/generator.spec.ts | 2 +- .../src/generators/generator/generator.ts | 2 +- .../src/generators/migration/migration.ts | 31 +- .../files/plugin/executors.json__tmpl__ | 4 - .../files/plugin/generators.json__tmpl__ | 6 - .../plugin/files/plugin/package.json__tmpl__ | 7 - .../src/generators/plugin/plugin.spec.ts | 25 +- .../nx-plugin/src/generators/plugin/plugin.ts | 10 +- .../src/generators/plugin/schema.d.ts | 1 + .../plugin/utils/normalize-schema.ts | 6 +- .../src/generators/preset/generator.spec.ts | 30 ++ .../src/generators/preset/generator.ts | 45 +++ .../src/generators/preset/schema.d.ts | 3 + .../src/generators/preset/schema.json | 16 + packages/nx/src/command-line/new.ts | 53 +-- .../js/package-json/create-package-json.ts | 14 +- tsconfig.base.json | 2 + 43 files changed, 742 insertions(+), 690 deletions(-) create mode 100644 docs/generated/packages/nx-plugin/generators/preset.json delete mode 100644 packages/create-nx-plugin/bin/detect-invoked-package-manager.ts delete mode 100644 packages/create-nx-plugin/bin/shared.ts create mode 100644 packages/create-nx-workspace/src/internal-utils/prompts.ts create mode 100644 packages/create-nx-workspace/src/internal-utils/yargs-options.ts delete mode 100644 packages/js/src/generators/library/files/lib/package.json__tmpl__ delete mode 100644 packages/nx-plugin/src/generators/plugin/files/plugin/executors.json__tmpl__ delete mode 100644 packages/nx-plugin/src/generators/plugin/files/plugin/generators.json__tmpl__ delete mode 100644 packages/nx-plugin/src/generators/plugin/files/plugin/package.json__tmpl__ create mode 100644 packages/nx-plugin/src/generators/preset/generator.spec.ts create mode 100644 packages/nx-plugin/src/generators/preset/generator.ts create mode 100644 packages/nx-plugin/src/generators/preset/schema.d.ts create mode 100644 packages/nx-plugin/src/generators/preset/schema.json diff --git a/.eslintrc.json b/.eslintrc.json index cf2e1378f703c6..c883c73986789d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -8,7 +8,8 @@ "plugins": ["@typescript-eslint", "@nrwl/nx"], "extends": [], "rules": { - "@typescript-eslint/explicit-module-boundary-types": "off" + "@typescript-eslint/explicit-module-boundary-types": "off", + "no-restricted-imports": ["error", "create-nx-workspace"] }, "overrides": [ { diff --git a/docs/generated/manifests/menus.json b/docs/generated/manifests/menus.json index 789a8e0fdc49d8..d9b7d81b05f859 100644 --- a/docs/generated/manifests/menus.json +++ b/docs/generated/manifests/menus.json @@ -5512,6 +5512,14 @@ "children": [], "isExternal": false, "disableCollapsible": false + }, + { + "id": "preset", + "path": "/packages/nx-plugin/generators/preset", + "name": "preset", + "children": [], + "isExternal": false, + "disableCollapsible": false } ], "isExternal": false, diff --git a/docs/generated/manifests/packages.json b/docs/generated/manifests/packages.json index 64b37593bc7f41..3f9f14a9f622a7 100644 --- a/docs/generated/manifests/packages.json +++ b/docs/generated/manifests/packages.json @@ -1964,6 +1964,15 @@ "originalFilePath": "/packages/nx-plugin/src/generators/lint-checks/schema.json", "path": "/packages/nx-plugin/generators/plugin-lint-checks", "type": "generator" + }, + "/packages/nx-plugin/generators/preset": { + "description": "Initializes a workspace with an nx-plugin inside of it. Use as: `create-nx-workspace --preset @nrwl/nx-plugin`.", + "file": "generated/packages/nx-plugin/generators/preset.json", + "hidden": true, + "name": "preset", + "originalFilePath": "/packages/nx-plugin/src/generators/preset/schema.json", + "path": "/packages/nx-plugin/generators/preset", + "type": "generator" } }, "path": "/packages/nx-plugin" diff --git a/docs/generated/packages-metadata.json b/docs/generated/packages-metadata.json index b23b2fd5f872c5..c9faa9146b7747 100644 --- a/docs/generated/packages-metadata.json +++ b/docs/generated/packages-metadata.json @@ -1939,6 +1939,15 @@ "originalFilePath": "/packages/nx-plugin/src/generators/lint-checks/schema.json", "path": "nx-plugin/generators/plugin-lint-checks", "type": "generator" + }, + { + "description": "Initializes a workspace with an nx-plugin inside of it. Use as: `create-nx-workspace --preset @nrwl/nx-plugin`.", + "file": "generated/packages/nx-plugin/generators/preset.json", + "hidden": true, + "name": "preset", + "originalFilePath": "/packages/nx-plugin/src/generators/preset/schema.json", + "path": "nx-plugin/generators/preset", + "type": "generator" } ], "githubRoot": "https://github.com/nrwl/nx/blob/master", diff --git a/docs/generated/packages/nx-plugin/generators/preset.json b/docs/generated/packages/nx-plugin/generators/preset.json new file mode 100644 index 00000000000000..2253d562ca9d08 --- /dev/null +++ b/docs/generated/packages/nx-plugin/generators/preset.json @@ -0,0 +1,28 @@ +{ + "name": "preset", + "factory": "./src/generators/preset/generator", + "schema": { + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "$id": "NxPluginPreset", + "title": "Generator ran by create-nx-plugin", + "description": "Initializes a workspace with an nx-plugin inside of it. Use as: `create-nx-plugin` or `create-nx-workspace --preset @nrwl/nx-plugin`.", + "type": "object", + "properties": { + "pluginName": { + "type": "string", + "description": "Plugin name", + "aliases": ["name"] + } + }, + "required": ["pluginName"], + "presets": [] + }, + "description": "Initializes a workspace with an nx-plugin inside of it. Use as: `create-nx-workspace --preset @nrwl/nx-plugin`.", + "hidden": true, + "x-use-standalone-layout": true, + "implementation": "/packages/nx-plugin/src/generators/preset/generator.ts", + "aliases": [], + "path": "/packages/nx-plugin/src/generators/preset/schema.json", + "type": "generator" +} diff --git a/e2e/utils/create-project-utils.ts b/e2e/utils/create-project-utils.ts index add78ac4917a20..f89dabaf6aa45d 100644 --- a/e2e/utils/create-project-utils.ts +++ b/e2e/utils/create-project-utils.ts @@ -235,7 +235,7 @@ export function runCreatePlugin( } create-nx-plugin@${getPublishedVersion()} ${name}`; if (pluginName) { - command += ` --pluginName=${pluginName}`; + command += ` --pluginName=${pluginName} --no-nxCloud`; } if (packageManager && !useDetectedPm) { diff --git a/e2e/workspace-create/src/create-nx-plugin.test.ts b/e2e/workspace-create/src/create-nx-plugin.test.ts index 1412b9e7101fcb..4611479b83609a 100644 --- a/e2e/workspace-create/src/create-nx-plugin.test.ts +++ b/e2e/workspace-create/src/create-nx-plugin.test.ts @@ -6,6 +6,7 @@ import { uniq, runCreatePlugin, cleanupProject, + tmpProjPath, } from '@nrwl/e2e/utils'; describe('create-nx-plugin', () => { @@ -13,8 +14,7 @@ describe('create-nx-plugin', () => { afterEach(() => cleanupProject()); - // TODO: Re-enable to work with pnpm - xit('should be able to create a plugin repo and run plugin e2e', () => { + it('should be able to create a plugin repo and run plugin e2e', () => { const wsName = uniq('ws-plugin'); const pluginName = uniq('plugin'); @@ -26,10 +26,11 @@ describe('create-nx-plugin', () => { checkFilesExist( 'package.json', packageManagerLockFile[packageManager], - `packages/${pluginName}/package.json`, - `packages/${pluginName}/project.json` + `project.json`, + `generators.json`, + `executors.json` ); - expect(() => runCLI(`e2e ${pluginName}-e2e`)).not.toThrow(); + expect(() => runCLI(`e2e e2e`)).not.toThrow(); }); }); diff --git a/packages/create-nx-plugin/.eslintrc.json b/packages/create-nx-plugin/.eslintrc.json index 5354eb31ef7048..02cba1766ad6d8 100644 --- a/packages/create-nx-plugin/.eslintrc.json +++ b/packages/create-nx-plugin/.eslintrc.json @@ -1,6 +1,16 @@ { "extends": "../../.eslintrc", - "rules": {}, + "rules": { + "no-restricted-imports": [ + "error", + "@nrwl/workspace", + "@angular-devkit/core", + "@angular-devkit/architect", + "@angular-devkit/schematics", + "nx", + "@nrwl/devkit" + ] + }, "ignorePatterns": ["!**/*"], "overrides": [ { diff --git a/packages/create-nx-plugin/bin/create-nx-plugin.ts b/packages/create-nx-plugin/bin/create-nx-plugin.ts index 0080ab2cc26aad..b3087ec81eb2c1 100644 --- a/packages/create-nx-plugin/bin/create-nx-plugin.ts +++ b/packages/create-nx-plugin/bin/create-nx-plugin.ts @@ -1,156 +1,43 @@ #!/usr/bin/env node +import chalk = require('chalk'); +import enquirer = require('enquirer'); +import yargs = require('yargs'); -// we can't import from '@nrwl/workspace' because it will require typescript import { - getPackageManagerCommand, - NxJsonConfiguration, - readJsonFile, - writeJsonFile, - output, -} from '@nrwl/devkit'; -import { execSync } from 'child_process'; -import { rmSync } from 'fs'; -import * as path from 'path'; -import { dirSync } from 'tmp'; -import { initializeGitRepo, showNxWarning } from './shared'; + determineCI, + determineDefaultBase, + determineNxCloud, + determinePackageManager, +} from 'create-nx-workspace/src/internal-utils/prompts'; import { - detectInvokedPackageManager, - PackageManager, -} from './detect-invoked-package-manager'; -import enquirer = require('enquirer'); -import yargsParser = require('yargs-parser'); + withAllPrompts, + withCI, + withGitOptions, + withNxCloud, + withOptions, + withPackageManager, +} from 'create-nx-workspace/src/internal-utils/yargs-options'; +import { createWorkspace, CreateWorkspaceOptions } from 'create-nx-workspace'; +import { output } from 'create-nx-workspace/src/utils/output'; +import { CI } from 'create-nx-workspace/src/utils/ci/ci-list'; +import type { PackageManager } from 'create-nx-workspace/src/utils/package-manager'; + +export const yargsDecorator = { + 'Options:': `${chalk.green`Options`}:`, + 'Examples:': `${chalk.green`Examples`}:`, + boolean: `${chalk.blue`boolean`}`, + count: `${chalk.blue`count`}`, + string: `${chalk.blue`string`}`, + array: `${chalk.blue`array`}`, + required: `${chalk.blue`required`}`, + 'default:': `${chalk.blue`default`}:`, + 'choices:': `${chalk.blue`choices`}:`, + 'aliases:': `${chalk.blue`aliases`}:`, +}; const nxVersion = require('../package.json').version; -const tsVersion = 'TYPESCRIPT_VERSION'; // This gets replaced with the typescript version in the root package.json during build -const prettierVersion = 'PRETTIER_VERSION'; // This gets replaced with the prettier version in the root package.json during build - -const parsedArgs = yargsParser(process.argv, { - string: ['pluginName', 'packageManager', 'importPath'], - alias: { - importPath: 'import-path', - pluginName: 'plugin-name', - packageManager: 'pm', - }, - boolean: ['help'], -}); - -function createSandbox(packageManager: string) { - console.log(`Creating a sandbox with Nx...`); - const tmpDir = dirSync().name; - writeJsonFile(path.join(tmpDir, 'package.json'), { - dependencies: { - '@nrwl/workspace': nxVersion, - nx: nxVersion, - typescript: tsVersion, - prettier: prettierVersion, - }, - license: 'MIT', - }); - - execSync(`${packageManager} install --silent --ignore-scripts`, { - cwd: tmpDir, - stdio: [0, 1, 2], - }); - - return tmpDir; -} - -function createWorkspace( - tmpDir: string, - packageManager: PackageManager, - parsedArgs: any, - name: string -) { - // Ensure to use packageManager for args - // if it's not already passed in from previous process - if (!parsedArgs.packageManager) { - parsedArgs.packageManager = packageManager; - } - - const args = [ - name, - ...process.argv.slice(parsedArgs._[2] ? 3 : 2).map((a) => `"${a}"`), - ].join(' '); - - const command = `new ${args} --preset=empty --collection=@nrwl/workspace`; - console.log(command); - - const pmc = getPackageManagerCommand(packageManager); - execSync( - `${ - pmc.exec - } nx ${command}/generators.json --nxWorkspaceRoot="${process.cwd()}"`, - { - stdio: [0, 1, 2], - cwd: tmpDir, - } - ); - execSync(`${packageManager} add -D @nrwl/nx-plugin@${nxVersion}`, { - cwd: name, - stdio: [0, 1, 2], - }); -} - -function createNxPlugin( - workspaceName, - pluginName, - packageManager, - parsedArgs: any -) { - const importPath = parsedArgs.importPath ?? `@${workspaceName}/${pluginName}`; - const command = `nx generate @nrwl/nx-plugin:plugin ${pluginName} --importPath=${importPath}`; - console.log(command); - - const pmc = getPackageManagerCommand(packageManager); - execSync(`${pmc.exec} ${command}`, { - cwd: workspaceName, - stdio: [0, 1, 2], - }); -} - -function updateWorkspace(workspaceName: string) { - const nxJsonPath = path.join(workspaceName, 'nx.json'); - const nxJson = readJsonFile(nxJsonPath); - - nxJson.workspaceLayout = { - appsDir: 'e2e', - libsDir: 'packages', - }; - - writeJsonFile(nxJsonPath, nxJson); - - rmSync(path.join(workspaceName, 'apps'), { recursive: true, force: true }); - rmSync(path.join(workspaceName, 'libs'), { recursive: true, force: true }); -} - -function determineWorkspaceName(parsedArgs: any): Promise { - const workspaceName: string = parsedArgs._[2]; - - if (workspaceName) { - return Promise.resolve(workspaceName); - } - return enquirer - .prompt([ - { - name: 'WorkspaceName', - message: `Workspace name (e.g., org name) `, - type: 'input', - }, - ]) - .then((a: { WorkspaceName: string }) => { - if (!a.WorkspaceName) { - output.error({ - title: 'Invalid workspace name', - bodyLines: [`Workspace name cannot be empty`], - }); - process.exit(1); - } - return a.WorkspaceName; - }); -} - -function determinePluginName(parsedArgs) { +function determinePluginName(parsedArgs: CreateNxPluginArguments) { if (parsedArgs.pluginName) { return Promise.resolve(parsedArgs.pluginName); } @@ -158,54 +45,112 @@ function determinePluginName(parsedArgs) { return enquirer .prompt([ { - name: 'PluginName', + name: 'pluginName', message: `Plugin name `, type: 'input', + validate: (s) => (s.length ? true : 'Name cannot be empty'), }, ]) - .then((a: { PluginName: string }) => { - if (!a.PluginName) { + .then((a: { pluginName: string }) => { + if (!a.pluginName) { output.error({ title: 'Invalid name', bodyLines: [`Name cannot be empty`], }); process.exit(1); } - return a.PluginName; + return a.pluginName; }); } -function showHelp() { - console.log(` - Usage: [options] - - Create a new Nx workspace - - Args: - - name workspace name (e.g., org name) - - Options: - - pluginName the name of the plugin to be created -`); +interface CreateNxPluginArguments { + pluginName: string; + packageManager: PackageManager; + ci: CI; + allPrompts: boolean; + nxCloud: boolean; } -if (parsedArgs.help) { - showHelp(); - process.exit(0); +export const commandsObject: yargs.Argv = yargs + .wrap(yargs.terminalWidth()) + .parserConfiguration({ + 'strip-dashed': true, + 'dot-notation': true, + }) + .command( + // this is the default and only command + '$0 [name] [options]', + 'Create a new Nx plugin workspace', + (yargs) => + withOptions( + yargs.positional('pluginName', { + describe: chalk.dim`Plugin name`, + type: 'string', + alias: ['name'], + }), + withNxCloud, + withCI, + withAllPrompts, + withPackageManager, + withGitOptions + ), + async (argv: yargs.ArgumentsCamelCase) => { + await main(argv).catch((error) => { + const { version } = require('../package.json'); + output.error({ + title: `Something went wrong! v${version}`, + }); + throw error; + }); + }, + [normalizeArgsMiddleware] + ) + .help('help', chalk.dim`Show help`) + .updateLocale(yargsDecorator) + .version( + 'version', + chalk.dim`Show version`, + nxVersion + ) as yargs.Argv; + +async function main(parsedArgs: yargs.Arguments) { + const populatedArguments: CreateNxPluginArguments & CreateWorkspaceOptions = { + ...parsedArgs, + name: parsedArgs.pluginName.includes('/') + ? parsedArgs.pluginName.split('/')[1] + : parsedArgs.pluginName, + }; + await createWorkspace('@nrwl/nx-plugin', populatedArguments); } -const packageManager: PackageManager = - parsedArgs.packageManager || detectInvokedPackageManager(); -determineWorkspaceName(parsedArgs).then((workspaceName) => { - return determinePluginName(parsedArgs).then((pluginName) => { - const tmpDir = createSandbox(packageManager); - createWorkspace(tmpDir, packageManager, parsedArgs, workspaceName); - updateWorkspace(workspaceName); - createNxPlugin(workspaceName, pluginName, packageManager, parsedArgs); - return initializeGitRepo(workspaceName).then(() => { - showNxWarning(workspaceName); +/** + * This function is used to normalize the arguments passed to the command. + * It would: + * - normalize the preset. + * @param argv user arguments + */ +async function normalizeArgsMiddleware( + argv: yargs.Arguments +): Promise { + try { + const name = await determinePluginName(argv); + const packageManager = await determinePackageManager(argv); + const defaultBase = await determineDefaultBase(argv); + const nxCloud = await determineNxCloud(argv); + const ci = await determineCI(argv, nxCloud); + + Object.assign(argv, { + name, + nxCloud, + packageManager, + defaultBase, + ci, }); - }); -}); + } catch (e) { + console.error(e); + process.exit(1); + } +} + +// Trigger Yargs +commandsObject.argv; diff --git a/packages/create-nx-plugin/bin/detect-invoked-package-manager.ts b/packages/create-nx-plugin/bin/detect-invoked-package-manager.ts deleted file mode 100644 index 3748ec252e6dcd..00000000000000 --- a/packages/create-nx-plugin/bin/detect-invoked-package-manager.ts +++ /dev/null @@ -1,32 +0,0 @@ -const packageManagerList = ['pnpm', 'yarn', 'npm'] as const; - -export type PackageManager = typeof packageManagerList[number]; - -/** - * Detects which package manager was used to invoke create-nx-{plugin|workspace} command - * based on the main Module process that invokes the command - * - npx returns 'npm' - * - pnpx returns 'pnpm' - * - yarn create returns 'yarn' - * - * Default to 'npm' - */ -export function detectInvokedPackageManager(): PackageManager { - let detectedPackageManager: PackageManager = 'npm'; - // mainModule is deprecated since Node 14, fallback for older versions - const invoker = require.main || process['mainModule']; - - // default to `npm` - if (!invoker) { - return detectedPackageManager; - } - - for (const pkgManager of packageManagerList) { - if (invoker.path.includes(pkgManager)) { - detectedPackageManager = pkgManager; - break; - } - } - - return detectedPackageManager; -} diff --git a/packages/create-nx-plugin/bin/shared.ts b/packages/create-nx-plugin/bin/shared.ts deleted file mode 100644 index 42dc250a148038..00000000000000 --- a/packages/create-nx-plugin/bin/shared.ts +++ /dev/null @@ -1,106 +0,0 @@ -import * as path from 'path'; -import { execSync, spawn, SpawnOptions } from 'child_process'; -import { output } from '@nrwl/devkit'; - -export function showNxWarning(workspaceName: string) { - try { - const pathToRunNxCommand = path.resolve(process.cwd(), workspaceName); - execSync('nx --version', { - cwd: pathToRunNxCommand, - stdio: ['ignore', 'ignore', 'ignore'], - }); - } catch { - // no nx found - output.addVerticalSeparator(); - output.note({ - title: `Nx CLI is not installed globally.`, - bodyLines: [ - `This means that you might have to use "yarn nx" or "npx nx" to execute commands in the workspace.`, - `Run "yarn global add nx" or "npm install -g nx" to be able to execute command directly.`, - ], - }); - } -} - -/* - * Because we don't want to depend on @nrwl/workspace - * we duplicate the helper functions from @nrwl/workspace in this file. - */ -export function deduceDefaultBase(): string { - const nxDefaultBase = 'main'; - try { - return ( - execSync('git config --get init.defaultBranch').toString().trim() || - nxDefaultBase - ); - } catch { - return nxDefaultBase; - } -} - -function checkGitVersion(): string | null { - try { - let gitVersionOutput = execSync('git --version').toString().trim(); - return gitVersionOutput.match(/[0-9]+\.[0-9]+\.+[0-9]+/)[0]; - } catch { - return null; - } -} - -/* - * Because we don't want to depend on create-nx-workspace - * we duplicate the helper functions from create-nx-workspace in this file. - */ -export async function initializeGitRepo(directory: string) { - const execute = (args: ReadonlyArray, ignoreErrorStream = false) => { - const errorStream = ignoreErrorStream ? 'ignore' : process.stderr; - const spawnOptions: SpawnOptions = { - stdio: [process.stdin, 'ignore', errorStream], - shell: true, - cwd: directory, - env: process.env, - }; - return new Promise((resolve, reject) => { - spawn('git', args, spawnOptions).on('close', (code) => { - if (code === 0) { - resolve(); - } else { - reject(code); - } - }); - }); - }; - const gitVersion = checkGitVersion(); - if (!gitVersion) { - return; - } - const insideRepo = await execute( - ['rev-parse', '--is-inside-work-tree'], - true - ).then( - () => true, - () => false - ); - if (insideRepo) { - output.log({ - title: - 'Directory is already under version control. Skipping initialization of git.', - }); - return; - } - const defaultBase = deduceDefaultBase(); - const [gitMajor, gitMinor] = gitVersion.split('.'); - - if (+gitMajor > 2 || (+gitMajor === 2 && +gitMinor >= 28)) { - await execute(['init', '-b', defaultBase]); - } else { - await execute(['init']); - await execute(['checkout', '-b', defaultBase]); // Git < 2.28 doesn't support -b on git init. - } - await execute(['add', '.']); - const message = 'Initial commit'; - await execute(['commit', `-m "${message}"`]); - output.log({ - title: 'Successfully initialized git.', - }); -} diff --git a/packages/create-nx-plugin/package.json b/packages/create-nx-plugin/package.json index 0bd1ef8e98ef83..df0f020b07ec7f 100644 --- a/packages/create-nx-plugin/package.json +++ b/packages/create-nx-plugin/package.json @@ -29,11 +29,10 @@ }, "homepage": "https://nx.dev", "dependencies": { - "@nrwl/devkit": "file:../devkit", + "create-nx-workspace": "file:../create-nx-workspace", + "chalk": "^4.1.0", "enquirer": "~2.3.6", - "nx": "file:../nx", - "tmp": "~0.2.1", - "yargs-parser": "21.1.1" + "yargs": "^17.6.2" }, "publishConfig": { "access": "public" diff --git a/packages/create-nx-workspace/bin/create-nx-workspace.ts b/packages/create-nx-workspace/bin/create-nx-workspace.ts index cedb373b58bd21..4d17dd28e88576 100644 --- a/packages/create-nx-workspace/bin/create-nx-workspace.ts +++ b/packages/create-nx-workspace/bin/create-nx-workspace.ts @@ -2,20 +2,11 @@ import * as enquirer from 'enquirer'; import * as yargs from 'yargs'; import * as chalk from 'chalk'; -import { ciList } from '../src/utils/ci/ci-list'; import { CreateWorkspaceOptions } from '../src/create-workspace-options'; import { createWorkspace } from '../src/create-workspace'; import { isKnownPreset, Preset } from '../src/utils/preset/preset'; import { presetOptions } from '../src/utils/preset/preset-options'; -import { messages } from '../src/utils/nx/ab-testing'; import { output } from '../src/utils/output'; -import { deduceDefaultBase } from '../src/utils/git/default-base'; -import { stringifyCollection } from '../src/utils/string-utils'; -import { - detectInvokedPackageManager, - PackageManager, - packageManagerList, -} from '../src/utils/package-manager'; import { nxVersion } from '../src/utils/nx/nx-version'; import { pointToTutorialAndCourse } from '../src/utils/preset/point-to-tutorial-and-course'; @@ -23,6 +14,20 @@ import { yargsDecorator } from './decorator'; import { getThirdPartyPreset } from '../src/utils/preset/get-third-party-preset'; import { Framework, frameworkList } from './types/framework-list'; import { Bundler, bundlerList } from './types/bundler-list'; +import { + determineCI, + determineDefaultBase, + determineNxCloud, + determinePackageManager, +} from '../src/internal-utils/prompts'; +import { + withAllPrompts, + withCI, + withGitOptions, + withNxCloud, + withOptions, + withPackageManager, +} from '../src/internal-utils/yargs-options'; interface Arguments extends CreateWorkspaceOptions { preset: string; @@ -46,103 +51,64 @@ export const commandsObject: yargs.Argv = yargs '$0 [name] [options]', 'Create a new Nx workspace', (yargs) => - yargs - .option('name', { - describe: chalk.dim`Workspace name (e.g. org name)`, - type: 'string', - }) - .option('preset', { - describe: chalk.dim`Customizes the initial content of your workspace. Default presets include: [${Object.values( - Preset - ) - .map((p) => `"${p}"`) - .join( - ', ' - )}]. To build your own see https://nx.dev/packages/nx-plugin#preset`, - type: 'string', - }) - .option('appName', { - describe: chalk.dim`The name of the application when a preset with pregenerated app is selected`, - type: 'string', - }) - .option('interactive', { - describe: chalk.dim`Enable interactive mode with presets`, - type: 'boolean', - default: true, - }) - .option('style', { - describe: chalk.dim`Style option to be used when a preset with pregenerated app is selected`, - type: 'string', - }) - .option('standaloneApi', { - describe: chalk.dim`Use Standalone Components if generating an Angular app`, - type: 'boolean', - }) - .option('routing', { - describe: chalk.dim`Add a routing setup when a preset with pregenerated app is selected`, - type: 'boolean', - }) - .option('bundler', { - describe: chalk.dim`Bundler to be used to build the application`, - choices: bundlerList, - type: 'string', - }) - .option('framework', { - describe: chalk.dim`Framework option to be used when the node-server preset is selected`, - choices: frameworkList, - type: 'string', - }) - .option('docker', { - describe: chalk.dim`Generate a Dockerfile with your node-server`, - type: 'boolean', - }) - .option('nxCloud', { - describe: chalk.dim(messages.getPromptMessage('nxCloudCreation')), - type: 'boolean', - }) - .option('ci', { - describe: chalk.dim`Generate a CI workflow file`, - choices: ciList, - defaultDescription: '', - type: 'string', - }) - .option('allPrompts', { - alias: 'a', - describe: chalk.dim`Show all prompts`, - type: 'boolean', - default: false, - }) - .option('packageManager', { - alias: 'pm', - describe: chalk.dim`Package manager to use`, - choices: [...packageManagerList].sort(), - defaultDescription: 'npm', - type: 'string', - }) - .option('defaultBase', { - defaultDescription: 'main', - describe: chalk.dim`Default base to use for new projects`, - type: 'string', - }) - .option('skipGit', { - describe: chalk.dim`Skip initializing a git repository`, - type: 'boolean', - default: false, - alias: 'g', - }) - .option('commit.name', { - describe: chalk.dim`Name of the committer`, - type: 'string', - }) - .option('commit.email', { - describe: chalk.dim`E-mail of the committer`, - type: 'string', - }) - .option('commit.message', { - describe: chalk.dim`Commit message`, - type: 'string', - default: 'Initial commit', - }), + withOptions( + yargs + .option('name', { + describe: chalk.dim`Workspace name (e.g. org name)`, + type: 'string', + }) + .option('preset', { + describe: chalk.dim`Customizes the initial content of your workspace. Default presets include: [${Object.values( + Preset + ) + .map((p) => `"${p}"`) + .join( + ', ' + )}]. To build your own see https://nx.dev/packages/nx-plugin#preset`, + type: 'string', + }) + .option('appName', { + describe: chalk.dim`The name of the application when a preset with pregenerated app is selected`, + type: 'string', + }) + .option('interactive', { + describe: chalk.dim`Enable interactive mode with presets`, + type: 'boolean', + default: true, + }) + .option('style', { + describe: chalk.dim`Style option to be used when a preset with pregenerated app is selected`, + type: 'string', + }) + .option('standaloneApi', { + describe: chalk.dim`Use Standalone Components if generating an Angular app`, + type: 'boolean', + }) + .option('routing', { + describe: chalk.dim`Add a routing setup when a preset with pregenerated app is selected`, + type: 'boolean', + }) + .option('bundler', { + describe: chalk.dim`Bundler to be used to build the application`, + choices: bundlerList, + type: 'string', + }) + .option('framework', { + describe: chalk.dim`Framework option to be used when the node-server preset is selected`, + choices: frameworkList, + type: 'string', + }) + .option('docker', { + describe: chalk.dim`Generate a Dockerfile with your node-server`, + type: 'boolean', + }), + withNxCloud, + withCI, + withAllPrompts, + withPackageManager, + withGitOptions + ), + async (argv: yargs.ArgumentsCamelCase) => { await main(argv).catch((error) => { const { version } = require('../package.json'); @@ -404,77 +370,6 @@ async function determineMonorepoStyle(): Promise { return a.MonorepoStyle; } -async function determinePackageManager( - parsedArgs: yargs.Arguments -): Promise { - const packageManager: string = parsedArgs.packageManager; - - if (packageManager) { - if (packageManagerList.includes(packageManager as PackageManager)) { - return Promise.resolve(packageManager as PackageManager); - } - output.error({ - title: 'Invalid package manager', - bodyLines: [ - `Package manager must be one of ${stringifyCollection([ - ...packageManagerList, - ])}`, - ], - }); - process.exit(1); - } - - if (parsedArgs.allPrompts) { - return enquirer - .prompt<{ packageManager: PackageManager }>([ - { - name: 'packageManager', - message: `Which package manager to use `, - initial: 'npm' as any, - type: 'autocomplete', - choices: [ - { name: 'npm', message: 'NPM' }, - { name: 'yarn', message: 'Yarn' }, - { name: 'pnpm', message: 'PNPM' }, - ], - }, - ]) - .then((a) => a.packageManager); - } - - return Promise.resolve(detectInvokedPackageManager()); -} - -async function determineDefaultBase( - parsedArgs: yargs.Arguments -): Promise { - if (parsedArgs.defaultBase) { - return Promise.resolve(parsedArgs.defaultBase); - } - if (parsedArgs.allPrompts) { - return enquirer - .prompt<{ DefaultBase: string }>([ - { - name: 'DefaultBase', - message: `Main branch name `, - initial: `main`, - type: 'input', - }, - ]) - .then((a) => { - if (!a.DefaultBase) { - output.error({ - title: 'Invalid branch name', - bodyLines: [`Branch name cannot be empty`], - }); - process.exit(1); - } - return a.DefaultBase; - }); - } - return Promise.resolve(deduceDefaultBase()); -} - async function determinePreset(parsedArgs: any): Promise { if (parsedArgs.preset) { if (Object.values(Preset).indexOf(parsedArgs.preset) === -1) { @@ -832,79 +727,3 @@ async function determineBundler( return Promise.resolve(parsedArgs.bundler); } - -async function determineNxCloud( - parsedArgs: yargs.Arguments -): Promise { - if (parsedArgs.nxCloud === undefined) { - return enquirer - .prompt<{ NxCloud: 'Yes' | 'No' }>([ - { - name: 'NxCloud', - message: messages.getPromptMessage('nxCloudCreation'), - type: 'autocomplete', - choices: [ - { - name: 'Yes', - hint: 'I want faster builds', - }, - - { - name: 'No', - }, - ], - initial: 'Yes' as any, - }, - ]) - .then((a) => a.NxCloud === 'Yes'); - } else { - return parsedArgs.nxCloud; - } -} - -async function determineCI( - parsedArgs: yargs.Arguments, - nxCloud: boolean -): Promise { - if (!nxCloud) { - if (parsedArgs.ci) { - output.warn({ - title: 'Invalid CI value', - bodyLines: [ - `CI option only works when Nx Cloud is enabled.`, - `The value provided will be ignored.`, - ], - }); - } - return ''; - } - - if (parsedArgs.ci) { - return parsedArgs.ci; - } - - if (parsedArgs.allPrompts) { - return ( - enquirer - .prompt<{ CI: string }>([ - { - name: 'CI', - message: `CI workflow file to generate? `, - type: 'autocomplete', - initial: '' as any, - choices: [ - { message: 'none', name: '' }, - { message: 'GitHub Actions', name: 'github' }, - { message: 'Circle CI', name: 'circleci' }, - { message: 'Azure DevOps', name: 'azure' }, - ], - }, - ]) - // enquirer ignores name and value if they are falsy and takes - // first field that has a truthy value, so wee need to explicitly - // check for none - .then((a: { CI: string }) => (a.CI !== 'none' ? a.CI : '')) - ); - } - return ''; -} diff --git a/packages/create-nx-workspace/index.ts b/packages/create-nx-workspace/index.ts index 285217767a4876..0256bf8eb20688 100644 --- a/packages/create-nx-workspace/index.ts +++ b/packages/create-nx-workspace/index.ts @@ -1 +1,2 @@ export * from './src/create-workspace'; +export type { CreateWorkspaceOptions } from './src/create-workspace-options'; diff --git a/packages/create-nx-workspace/src/create-workspace.ts b/packages/create-nx-workspace/src/create-workspace.ts index 41676f263d5733..ed7d090f24f075 100644 --- a/packages/create-nx-workspace/src/create-workspace.ts +++ b/packages/create-nx-workspace/src/create-workspace.ts @@ -36,11 +36,12 @@ export async function createWorkspace( const tmpDir = await createSandbox(packageManager); + // nx new requires preset currently. We should probably make it optional. const directory = await createEmptyWorkspace( tmpDir, name, packageManager, - options + { ...options, preset } ); // If the preset is a third-party preset, we need to call createPreset to install it diff --git a/packages/create-nx-workspace/src/internal-utils/prompts.ts b/packages/create-nx-workspace/src/internal-utils/prompts.ts new file mode 100644 index 00000000000000..304fbf104c8a99 --- /dev/null +++ b/packages/create-nx-workspace/src/internal-utils/prompts.ts @@ -0,0 +1,159 @@ +import * as yargs from 'yargs'; +import { messages } from '../utils/nx/ab-testing'; +import enquirer = require('enquirer'); +import { CI } from '../utils/ci/ci-list'; +import { output } from '../utils/output'; +import { deduceDefaultBase } from '../utils/git/default-base'; +import { + detectInvokedPackageManager, + PackageManager, + packageManagerList, +} from '../utils/package-manager'; +import { stringifyCollection } from '../utils/string-utils'; + +export async function determineNxCloud( + parsedArgs: yargs.Arguments<{ nxCloud: boolean }> +): Promise { + if (parsedArgs.nxCloud === undefined) { + return enquirer + .prompt<{ NxCloud: 'Yes' | 'No' }>([ + { + name: 'NxCloud', + message: messages.getPromptMessage('nxCloudCreation'), + type: 'autocomplete', + choices: [ + { + name: 'Yes', + hint: 'I want faster builds', + }, + + { + name: 'No', + }, + ], + initial: 'Yes' as any, + }, + ]) + .then((a) => a.NxCloud === 'Yes'); + } else { + return parsedArgs.nxCloud; + } +} + +export async function determineCI( + parsedArgs: yargs.Arguments<{ ci?: CI; allPrompts?: boolean }>, + nxCloud: boolean +): Promise { + if (!nxCloud) { + if (parsedArgs.ci) { + output.warn({ + title: 'Invalid CI value', + bodyLines: [ + `CI option only works when Nx Cloud is enabled.`, + `The value provided will be ignored.`, + ], + }); + } + return ''; + } + + if (parsedArgs.ci) { + return parsedArgs.ci; + } + + if (parsedArgs.allPrompts) { + return ( + enquirer + .prompt<{ CI: string }>([ + { + name: 'CI', + message: `CI workflow file to generate? `, + type: 'autocomplete', + initial: '' as any, + choices: [ + { message: 'none', name: '' }, + { message: 'GitHub Actions', name: 'github' }, + { message: 'Circle CI', name: 'circleci' }, + { message: 'Azure DevOps', name: 'azure' }, + ], + }, + ]) + // enquirer ignores name and value if they are falsy and takes + // first field that has a truthy value, so wee need to explicitly + // check for none + .then((a: { CI: string }) => (a.CI !== 'none' ? a.CI : '')) + ); + } + return ''; +} + +export async function determineDefaultBase( + parsedArgs: yargs.Arguments<{ defaultBase?: string }> +): Promise { + if (parsedArgs.defaultBase) { + return Promise.resolve(parsedArgs.defaultBase); + } + if (parsedArgs.allPrompts) { + return enquirer + .prompt<{ DefaultBase: string }>([ + { + name: 'DefaultBase', + message: `Main branch name `, + initial: `main`, + type: 'input', + }, + ]) + .then((a) => { + if (!a.DefaultBase) { + output.error({ + title: 'Invalid branch name', + bodyLines: [`Branch name cannot be empty`], + }); + process.exit(1); + } + return a.DefaultBase; + }); + } + return Promise.resolve(deduceDefaultBase()); +} + +export async function determinePackageManager( + parsedArgs: yargs.Arguments<{ packageManager: string }> +): Promise { + const packageManager: string = parsedArgs.packageManager; + + if (packageManager) { + if (packageManagerList.includes(packageManager as PackageManager)) { + return Promise.resolve(packageManager as PackageManager); + } + output.error({ + title: 'Invalid package manager', + bodyLines: [ + `Package manager must be one of ${stringifyCollection([ + ...packageManagerList, + ])}`, + ], + }); + process.exit(1); + } + + if (parsedArgs.allPrompts) { + return enquirer + .prompt<{ packageManager: PackageManager }>([ + { + name: 'packageManager', + message: `Which package manager to use `, + initial: 'npm' as any, + type: 'autocomplete', + choices: [ + { name: 'npm', message: 'NPM' }, + { name: 'yarn', message: 'Yarn' }, + { name: 'pnpm', message: 'PNPM' }, + ], + }, + ]) + .then((a) => a.packageManager); + } + + return Promise.resolve(detectInvokedPackageManager()); +} diff --git a/packages/create-nx-workspace/src/internal-utils/yargs-options.ts b/packages/create-nx-workspace/src/internal-utils/yargs-options.ts new file mode 100644 index 00000000000000..baf639cf516de0 --- /dev/null +++ b/packages/create-nx-workspace/src/internal-utils/yargs-options.ts @@ -0,0 +1,79 @@ +import chalk = require('chalk'); +import yargs = require('yargs'); +import { CreateWorkspaceOptions } from '../create-workspace-options'; +import { ciList } from '../utils/ci/ci-list'; +import { messages } from '../utils/nx/ab-testing'; +import { packageManagerList } from '../utils/package-manager'; + +export function withNxCloud(argv: yargs.Argv) { + const result = argv.option('nxCloud', { + describe: chalk.dim(messages.getPromptMessage('nxCloudCreation')), + type: 'boolean', + }); + return result; +} + +export function withCI(argv: yargs.Argv) { + return argv.option('ci', { + describe: chalk.dim`Generate a CI workflow file`, + choices: ciList, + defaultDescription: '', + type: 'string', + }); +} + +export function withAllPrompts(argv: yargs.Argv) { + return argv.option('allPrompts', { + alias: 'a', + describe: chalk.dim`Show all prompts`, + type: 'boolean', + default: false, + }); +} + +export function withPackageManager(argv: yargs.Argv) { + return argv.option('packageManager', { + alias: 'pm', + describe: chalk.dim`Package manager to use`, + choices: [...packageManagerList].sort(), + defaultDescription: 'npm', + type: 'string', + }); +} + +export function withGitOptions(argv: yargs.Argv) { + return argv + .option('defaultBase', { + defaultDescription: 'main', + describe: chalk.dim`Default base to use for new projects`, + type: 'string', + }) + .option('skipGit', { + describe: chalk.dim`Skip initializing a git repository`, + type: 'boolean', + default: false, + alias: 'g', + }) + .option('commit.name', { + describe: chalk.dim`Name of the committer`, + type: 'string', + }) + .option('commit.email', { + describe: chalk.dim`E-mail of the committer`, + type: 'string', + }) + .option('commit.message', { + describe: chalk.dim`Commit message`, + type: 'string', + default: 'Initial commit', + }); +} + +export function withOptions( + argv: yargs.Argv, + ...options: ((argv: yargs.Argv) => yargs.Argv)[] +): any { + // Reversing the options keeps the execution order correct. + // e.g. [withCI, withGIT] should transform into withGIT(withCI) so withCI resolves first. + return options.reverse().reduce((argv, option) => option(argv), argv); +} diff --git a/packages/eslint-plugin-nx/src/rules/nx-plugin-checks.ts b/packages/eslint-plugin-nx/src/rules/nx-plugin-checks.ts index ca18cc28d731e3..87f7e660516d85 100644 --- a/packages/eslint-plugin-nx/src/rules/nx-plugin-checks.ts +++ b/packages/eslint-plugin-nx/src/rules/nx-plugin-checks.ts @@ -141,19 +141,21 @@ function normalizeOptions( options: Options[0] ): Options[0] { const base = { ...DEFAULT_OPTIONS, ...options }; + const pathPrefix = + sourceProject.data.root !== '.' ? `${sourceProject.data.root}/` : ''; return { ...base, executorsJson: base.executorsJson - ? `${sourceProject.data.root}/${base.executorsJson}` + ? `${pathPrefix}${base.executorsJson}` : undefined, generatorsJson: base.generatorsJson - ? `${sourceProject.data.root}/${base.generatorsJson}` + ? `${pathPrefix}${base.generatorsJson}` : undefined, migrationsJson: base.migrationsJson - ? `${sourceProject.data.root}/${base.migrationsJson}` + ? `${pathPrefix}${base.migrationsJson}` : undefined, packageJson: base.packageJson - ? `${sourceProject.data.root}/${base.packageJson}` + ? `${pathPrefix}${base.packageJson}` : undefined, }; } diff --git a/packages/js/src/generators/library/files/lib/package.json__tmpl__ b/packages/js/src/generators/library/files/lib/package.json__tmpl__ deleted file mode 100644 index 7ede38f8da1ed5..00000000000000 --- a/packages/js/src/generators/library/files/lib/package.json__tmpl__ +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "<%= importPath %>", - "version": "0.0.1", - "type": "commonjs" -} diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index 5a9e394d26003c..23be7456e67c49 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -35,6 +35,7 @@ import { typesNodeVersion, } from '../../utils/versions'; import jsInitGenerator from '../init/init'; +import { PackageJson } from 'nx/src/utils/package-json'; export async function libraryGenerator( tree: Tree, @@ -44,7 +45,9 @@ export async function libraryGenerator( schema.directory ); schema.directory = projectDirectory; - const libsDir = layoutDirectory ?? getWorkspaceLayout(tree).libsDir; + const libsDir = schema.rootProject + ? '.' + : layoutDirectory ?? getWorkspaceLayout(tree).libsDir; return projectGenerator(tree, schema, libsDir, join(__dirname, './files')); } @@ -235,6 +238,7 @@ export async function addLint( `${options.projectRoot}/**/*.${options.js ? 'js' : 'ts'}`, ], setParserOptionsProject: options.setParserOptionsProject, + rootProject: options.rootProject, }); } @@ -308,7 +312,25 @@ function createFiles(tree: Tree, options: NormalizedSchema, filesDir: string) { toJS(tree); } - const packageJsonPath = join(options.projectRoot, 'package.json'); + const packageJsonPath = joinPathFragments( + options.projectRoot, + 'package.json' + ); + if (tree.exists(packageJsonPath)) { + updateJson(tree, packageJsonPath, (json) => { + json.name = options.importPath; + json.version = '0.0.1'; + json.type = 'commonjs'; + return json; + }); + } else { + writeJson(tree, packageJsonPath, { + name: options.importPath, + version: '0.0.1', + type: 'commonjs', + }); + } + if (options.config === 'npm-scripts') { updateJson(tree, packageJsonPath, (json) => { json.scripts = { @@ -317,11 +339,14 @@ function createFiles(tree: Tree, options: NormalizedSchema, filesDir: string) { }; return json; }); - } else if (!options.bundler || options.bundler === 'none') { + } else if ( + (!options.bundler || options.bundler === 'none') && + !(options.projectRoot === '.') + ) { tree.delete(packageJsonPath); } - if (options.minimal) { + if (options.minimal && !(options.projectRoot === '.')) { tree.delete(join(options.projectRoot, 'README.md')); } @@ -437,6 +462,8 @@ function normalizeOptions( const name = names(options.name).fileName; const projectDirectory = options.directory ? `${names(options.directory).fileName}/${name}` + : options.rootProject + ? '.' : name; if (!options.unitTestRunner && options.bundler === 'vite') { @@ -449,7 +476,9 @@ function normalizeOptions( options.linter = Linter.EsLint; } - const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-'); + const projectName = options.rootProject + ? name + : projectDirectory.replace(new RegExp('/', 'g'), '-'); const fileName = getCaseAwareFileName({ fileName: options.simpleModuleName ? name : projectName, pascalCaseFiles: options.pascalCaseFiles, diff --git a/packages/js/src/utils/schema.d.ts b/packages/js/src/utils/schema.d.ts index a1295d7fd4a053..233a80fb9890cc 100644 --- a/packages/js/src/utils/schema.d.ts +++ b/packages/js/src/utils/schema.d.ts @@ -32,6 +32,7 @@ export interface LibraryGeneratorSchema { bundler?: Bundler; skipTypeCheck?: boolean; minimal?: boolean; + rootProject?: boolean; } export interface ExecutorOptions { diff --git a/packages/nx-plugin/generators.json b/packages/nx-plugin/generators.json index b889b233cf3b86..31c8ebbe99d89b 100644 --- a/packages/nx-plugin/generators.json +++ b/packages/nx-plugin/generators.json @@ -33,6 +33,13 @@ "factory": "./src/generators/lint-checks/generator", "schema": "./src/generators/lint-checks/schema.json", "description": "Adds linting configuration to validate common json files for nx plugins." + }, + "preset": { + "factory": "./src/generators/preset/generator", + "schema": "./src/generators/preset/schema.json", + "description": "Initializes a workspace with an nx-plugin inside of it. Use as: `create-nx-workspace --preset @nrwl/nx-plugin`.", + "hidden": true, + "x-use-standalone-layout": true } }, "schematics": { diff --git a/packages/nx-plugin/src/generators/e2e-project/e2e.ts b/packages/nx-plugin/src/generators/e2e-project/e2e.ts index 4214308eb19a51..ab576298a36f96 100644 --- a/packages/nx-plugin/src/generators/e2e-project/e2e.ts +++ b/packages/nx-plugin/src/generators/e2e-project/e2e.ts @@ -35,10 +35,13 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { const { npmScope, appsDir: defaultAppsDir } = getWorkspaceLayout(host); const appsDir = layoutDirectory ?? defaultAppsDir; - const projectName = `${options.pluginName}-e2e`; - const projectRoot = projectDirectory - ? joinPathFragments(appsDir, `${projectDirectory}-e2e`) - : joinPathFragments(appsDir, projectName); + const projectName = options.rootProject ? 'e2e' : `${options.pluginName}-e2e`; + const projectRoot = + projectDirectory && !options.rootProject + ? joinPathFragments(appsDir, `${projectDirectory}-e2e`) + : options.rootProject + ? projectName + : joinPathFragments(appsDir, projectName); const pluginPropertyName = names(options.pluginName).propertyName; return { diff --git a/packages/nx-plugin/src/generators/e2e-project/schema.d.ts b/packages/nx-plugin/src/generators/e2e-project/schema.d.ts index 9a0941e00e44bd..f9c0f5cbaac1ee 100644 --- a/packages/nx-plugin/src/generators/e2e-project/schema.d.ts +++ b/packages/nx-plugin/src/generators/e2e-project/schema.d.ts @@ -9,4 +9,5 @@ export interface Schema { minimal?: boolean; linter?: Linter; skipFormat?: boolean; + rootProject?: boolean; } diff --git a/packages/nx-plugin/src/generators/executor/executor.spec.ts b/packages/nx-plugin/src/generators/executor/executor.spec.ts index 968803b9b8aeab..e2a2e7dc26b384 100644 --- a/packages/nx-plugin/src/generators/executor/executor.spec.ts +++ b/packages/nx-plugin/src/generators/executor/executor.spec.ts @@ -107,7 +107,7 @@ describe('NxPlugin Executor Generator', () => { expect(() => tree.exists(`${libConfig.root}/executors.json`)).not.toThrow(); expect(readJson(tree, `${libConfig.root}/package.json`).executors).toBe( - 'executors.json' + './executors.json' ); }); diff --git a/packages/nx-plugin/src/generators/executor/executor.ts b/packages/nx-plugin/src/generators/executor/executor.ts index 1a9310a4acdd75..5b7b5209402a50 100644 --- a/packages/nx-plugin/src/generators/executor/executor.ts +++ b/packages/nx-plugin/src/generators/executor/executor.ts @@ -75,7 +75,7 @@ function createExecutorsJson(host: Tree, options: NormalizedSchema) { host, joinPathFragments(options.projectRoot, 'package.json'), (json) => { - json.executors ??= 'executors.json'; + json.executors ??= './executors.json'; return json; } ); diff --git a/packages/nx-plugin/src/generators/generator/generator.spec.ts b/packages/nx-plugin/src/generators/generator/generator.spec.ts index 8ef2dbe0cf40a9..0ef4a1e40098b5 100644 --- a/packages/nx-plugin/src/generators/generator/generator.spec.ts +++ b/packages/nx-plugin/src/generators/generator/generator.spec.ts @@ -111,7 +111,7 @@ describe('NxPlugin Generator Generator', () => { tree.exists(`${libConfig.root}/generators.json`) ).not.toThrow(); expect(readJson(tree, `${libConfig.root}/package.json`).generators).toBe( - 'generators.json' + './generators.json' ); }); diff --git a/packages/nx-plugin/src/generators/generator/generator.ts b/packages/nx-plugin/src/generators/generator/generator.ts index 738728565fb0d7..f3205ec67753f2 100644 --- a/packages/nx-plugin/src/generators/generator/generator.ts +++ b/packages/nx-plugin/src/generators/generator/generator.ts @@ -91,7 +91,7 @@ function createGeneratorsJson(host: Tree, options: NormalizedSchema) { host, joinPathFragments(options.projectRoot, 'package.json'), (json) => { - json.generators ??= 'generators.json'; + json.generators ??= './generators.json'; return json; } ); diff --git a/packages/nx-plugin/src/generators/migration/migration.ts b/packages/nx-plugin/src/generators/migration/migration.ts index 45f8bcf4b8ae98..cb4f101d400bdb 100644 --- a/packages/nx-plugin/src/generators/migration/migration.ts +++ b/packages/nx-plugin/src/generators/migration/migration.ts @@ -15,7 +15,12 @@ import type { Schema } from './schema'; import * as path from 'path'; import { addMigrationJsonChecks } from '../lint-checks/generator'; import type { Linter as EsLint } from 'eslint'; -import { PackageJson, readNxMigrateConfig } from 'nx/src/utils/package-json'; +import { + NxMigrationsConfiguration, + PackageJson, + PackageJsonTargetConfiguration, + readNxMigrateConfig, +} from 'nx/src/utils/package-json'; interface NormalizedSchema extends Schema { projectRoot: string; projectSourceRoot: string; @@ -98,19 +103,25 @@ function updateMigrationsJson(host: Tree, options: NormalizedSchema) { } function updatePackageJson(host: Tree, options: NormalizedSchema) { - updateJson(host, path.join(options.projectRoot, 'package.json'), (json) => { - if (!json['nx-migrations'] || !json['nx-migrations'].migrations) { - if (json['nx-migrations']) { - json['nx-migrations'].migrations = './migrations.json'; - } else { - json['nx-migrations'] = { + updateJson( + host, + path.join(options.projectRoot, 'package.json'), + (json) => { + const migrationKey = json['ng-update'] ? 'ng-update' : 'nx-migrations'; + const preexistingValue = json[migrationKey]; + if (typeof preexistingValue === 'string') { + return json; + } else if (!json[migrationKey]) { + json[migrationKey] = { migrations: './migrations.json', }; + } else if (preexistingValue.migrations) { + preexistingValue.migrations = './migrations.json'; } - } - return json; - }); + return json; + } + ); } function updateWorkspaceJson(host: Tree, options: NormalizedSchema) { diff --git a/packages/nx-plugin/src/generators/plugin/files/plugin/executors.json__tmpl__ b/packages/nx-plugin/src/generators/plugin/files/plugin/executors.json__tmpl__ deleted file mode 100644 index b64f99d988d2c4..00000000000000 --- a/packages/nx-plugin/src/generators/plugin/files/plugin/executors.json__tmpl__ +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema", - "executors": {} -} diff --git a/packages/nx-plugin/src/generators/plugin/files/plugin/generators.json__tmpl__ b/packages/nx-plugin/src/generators/plugin/files/plugin/generators.json__tmpl__ deleted file mode 100644 index ad73df570caa3c..00000000000000 --- a/packages/nx-plugin/src/generators/plugin/files/plugin/generators.json__tmpl__ +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema", - "name": "<%= name %>", - "version": "0.0.1", - "generators": {} -} diff --git a/packages/nx-plugin/src/generators/plugin/files/plugin/package.json__tmpl__ b/packages/nx-plugin/src/generators/plugin/files/plugin/package.json__tmpl__ deleted file mode 100644 index 0364fd3a35c560..00000000000000 --- a/packages/nx-plugin/src/generators/plugin/files/plugin/package.json__tmpl__ +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "<%= npmPackageName %>", - "version": "0.0.1", - "main": "src/index.js", - "generators": "./generators.json", - "executors": "./executors.json" -} diff --git a/packages/nx-plugin/src/generators/plugin/plugin.spec.ts b/packages/nx-plugin/src/generators/plugin/plugin.spec.ts index 1c46f9dc17ff85..964adc85bd9ff2 100644 --- a/packages/nx-plugin/src/generators/plugin/plugin.spec.ts +++ b/packages/nx-plugin/src/generators/plugin/plugin.spec.ts @@ -138,11 +138,7 @@ describe('NxPlugin Plugin Generator', () => { it('should not create generator and executor files for minimal setups', async () => { await pluginGenerator(tree, getSchema({ name: 'myPlugin', minimal: true })); - [ - 'libs/my-plugin/project.json', - 'libs/my-plugin/generators.json', - 'libs/my-plugin/executors.json', - ].forEach((path) => expect(tree.exists(path)).toBeTruthy()); + expect(tree.exists('libs/my-plugin/project.json')).toBeTruthy(); [ 'libs/my-plugin/src/generators/my-plugin/schema.d.ts', @@ -156,25 +152,6 @@ describe('NxPlugin Plugin Generator', () => { 'libs/my-plugin/src/executors/build/schema.json', 'libs/my-plugin/src/executors/build/schema.d.ts', ].forEach((path) => expect(tree.exists(path)).toBeFalsy()); - - expect(tree.read('libs/my-plugin/generators.json', 'utf-8')) - .toMatchInlineSnapshot(` - "{ - \\"$schema\\": \\"http://json-schema.org/schema\\", - \\"name\\": \\"my-plugin\\", - \\"version\\": \\"0.0.1\\", - \\"generators\\": {} - } - " - `); - expect(tree.read('libs/my-plugin/executors.json', 'utf-8')) - .toMatchInlineSnapshot(` - "{ - \\"$schema\\": \\"http://json-schema.org/schema\\", - \\"executors\\": {} - } - " - `); }); describe('--unitTestRunner', () => { diff --git a/packages/nx-plugin/src/generators/plugin/plugin.ts b/packages/nx-plugin/src/generators/plugin/plugin.ts index 14c2c70dc94ca3..6853eb0e8ad3b0 100644 --- a/packages/nx-plugin/src/generators/plugin/plugin.ts +++ b/packages/nx-plugin/src/generators/plugin/plugin.ts @@ -53,7 +53,7 @@ async function addFiles(host: Tree, options: NormalizedSchema) { }); } -function updateWorkspaceJson(host: Tree, options: NormalizedSchema) { +function updatePluginConfig(host: Tree, options: NormalizedSchema) { const project = readProjectConfiguration(host, options.name); if (project.targets.build) { @@ -100,13 +100,14 @@ export async function pluginGenerator(host: Tree, schema: Schema) { addDependenciesToPackageJson( host, - {}, { '@nrwl/devkit': nxVersion, + tslib: tsLibVersion, + }, + { '@nrwl/jest': nxVersion, '@nrwl/js': nxVersion, '@swc-node/register': swcNodeVersion, - tslib: tsLibVersion, } ); @@ -115,7 +116,7 @@ export async function pluginGenerator(host: Tree, schema: Schema) { addSwcDependencies(host); await addFiles(host, options); - updateWorkspaceJson(host, options); + updatePluginConfig(host, options); if (options.e2eTestRunner !== 'none') { await e2eProjectGenerator(host, { @@ -125,6 +126,7 @@ export async function pluginGenerator(host: Tree, schema: Schema) { npmPackageName: options.npmPackageName, minimal: options.minimal ?? false, skipFormat: true, + rootProject: options.rootProject, }); } diff --git a/packages/nx-plugin/src/generators/plugin/schema.d.ts b/packages/nx-plugin/src/generators/plugin/schema.d.ts index b2dfbd3596dfca..2596b29095cd07 100644 --- a/packages/nx-plugin/src/generators/plugin/schema.d.ts +++ b/packages/nx-plugin/src/generators/plugin/schema.d.ts @@ -14,4 +14,5 @@ export interface Schema { setParserOptionsProject?: boolean; compiler: 'swc' | 'tsc'; minimal?: boolean; + rootProject?: boolean; } diff --git a/packages/nx-plugin/src/generators/plugin/utils/normalize-schema.ts b/packages/nx-plugin/src/generators/plugin/utils/normalize-schema.ts index 72fae51491bfd6..9585280bfcf38c 100644 --- a/packages/nx-plugin/src/generators/plugin/utils/normalize-schema.ts +++ b/packages/nx-plugin/src/generators/plugin/utils/normalize-schema.ts @@ -31,9 +31,13 @@ export function normalizeOptions( const name = names(options.name).fileName; const fullProjectDirectory = projectDirectory ? `${names(projectDirectory).fileName}/${name}` + : options.rootProject + ? '.' : name; - const projectName = fullProjectDirectory.replace(new RegExp('/', 'g'), '-'); + const projectName = options.rootProject + ? name + : fullProjectDirectory.replace(new RegExp('/', 'g'), '-'); const fileName = projectName; const projectRoot = joinPathFragments(libsDir, fullProjectDirectory); diff --git a/packages/nx-plugin/src/generators/preset/generator.spec.ts b/packages/nx-plugin/src/generators/preset/generator.spec.ts new file mode 100644 index 00000000000000..838442e39a9912 --- /dev/null +++ b/packages/nx-plugin/src/generators/preset/generator.spec.ts @@ -0,0 +1,30 @@ +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { + Tree, + readProjectConfiguration, + readJson, + readNxJson, +} from '@nrwl/devkit'; + +import generator from './generator'; +import { PackageJson } from 'nx/src/utils/package-json'; + +describe('preset generator', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); + }); + + it('should create a plugin', async () => { + await generator(tree, { + pluginName: 'my-plugin', + }); + const config = readProjectConfiguration(tree, 'my-plugin'); + expect(config).toBeDefined(); + const packageJson = readJson(tree, 'package.json'); + expect(packageJson.generators).toEqual('./generators.json'); + expect(packageJson.executors).toEqual('./executors.json'); + expect(readNxJson(tree).npmScope).not.toBeDefined(); + }); +}); diff --git a/packages/nx-plugin/src/generators/preset/generator.ts b/packages/nx-plugin/src/generators/preset/generator.ts new file mode 100644 index 00000000000000..066746ac9b2b68 --- /dev/null +++ b/packages/nx-plugin/src/generators/preset/generator.ts @@ -0,0 +1,45 @@ +import { + Tree, + readJson, + joinPathFragments, + updateJson, + updateNxJson, + readNxJson, +} from '@nrwl/devkit'; +import { Linter } from '@nrwl/linter'; +import { PackageJson } from 'nx/src/utils/package-json'; +import { pluginGenerator } from '../plugin/plugin'; +import { PresetGeneratorSchema } from './schema'; + +export default async function (tree: Tree, options: PresetGeneratorSchema) { + const task = await pluginGenerator(tree, { + compiler: 'tsc', + linter: Linter.EsLint, + name: options.pluginName.includes('/') + ? options.pluginName.split('/')[1] + : options.pluginName, + skipFormat: false, + skipLintChecks: false, + skipTsConfig: false, + unitTestRunner: 'jest', + importPath: options.pluginName, + rootProject: true, + }); + + removeNpmScope(tree); + moveNxPluginToDevDeps(tree); + + return task; +} + +function removeNpmScope(tree: Tree) { + updateNxJson(tree, { ...readNxJson(tree), npmScope: undefined }); +} +function moveNxPluginToDevDeps(tree: Tree) { + updateJson(tree, 'package.json', (json) => { + const nxPluginEntry = json.dependencies['@nrwl/nx-plugin']; + delete json.dependencies['@nrwl/nx-plugin']; + json.devDependencies['@nrwl/nx-plugin'] = nxPluginEntry; + return json; + }); +} diff --git a/packages/nx-plugin/src/generators/preset/schema.d.ts b/packages/nx-plugin/src/generators/preset/schema.d.ts new file mode 100644 index 00000000000000..bff8df3c05dfbc --- /dev/null +++ b/packages/nx-plugin/src/generators/preset/schema.d.ts @@ -0,0 +1,3 @@ +export interface PresetGeneratorSchema { + pluginName: string; +} diff --git a/packages/nx-plugin/src/generators/preset/schema.json b/packages/nx-plugin/src/generators/preset/schema.json new file mode 100644 index 00000000000000..bc224d8091d579 --- /dev/null +++ b/packages/nx-plugin/src/generators/preset/schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "$id": "NxPluginPreset", + "title": "Generator ran by create-nx-plugin", + "description": "Initializes a workspace with an nx-plugin inside of it. Use as: `create-nx-plugin` or `create-nx-workspace --preset @nrwl/nx-plugin`.", + "type": "object", + "properties": { + "pluginName": { + "type": "string", + "description": "Plugin name", + "aliases": ["name"] + } + }, + "required": ["pluginName"] +} diff --git a/packages/nx/src/command-line/new.ts b/packages/nx/src/command-line/new.ts index 1311916f4aa2a9..6e2d47852e71b7 100644 --- a/packages/nx/src/command-line/new.ts +++ b/packages/nx/src/command-line/new.ts @@ -13,31 +13,34 @@ function removeSpecialFlags(generatorOptions: { [p: string]: any }): void { export async function newWorkspace(cwd: string, args: { [k: string]: any }) { const ws = new Workspaces(null); - return handleErrors(false, async () => { - const isInteractive = args.interactive; - const { normalizedGeneratorName, schema, implementationFactory } = - ws.readGenerator('@nrwl/workspace/generators.json', 'new'); - removeSpecialFlags(args); - const combinedOpts = await combineOptionsForGenerator( - args, - '@nrwl/workspace/generators.json', - normalizedGeneratorName, - null, - null, - schema, - isInteractive, - null, - null, - false - ); + return handleErrors( + process.env.NX_VERBOSE_LOGGING === 'true' || args.verbose, + async () => { + const isInteractive = args.interactive; + const { normalizedGeneratorName, schema, implementationFactory } = + ws.readGenerator('@nrwl/workspace/generators.json', 'new'); + removeSpecialFlags(args); + const combinedOpts = await combineOptionsForGenerator( + args, + '@nrwl/workspace/generators.json', + normalizedGeneratorName, + null, + null, + schema, + isInteractive, + null, + null, + false + ); - const host = new FsTree(cwd, false); - const implementation = implementationFactory(); - const task = await implementation(host, combinedOpts); - flushChanges(cwd, host.listChanges()); - host.lock(); - if (task) { - await task(); + const host = new FsTree(cwd, false); + const implementation = implementationFactory(); + const task = await implementation(host, combinedOpts); + flushChanges(cwd, host.listChanges()); + host.lock(); + if (task) { + await task(); + } } - }); + ); } diff --git a/packages/nx/src/plugins/js/package-json/create-package-json.ts b/packages/nx/src/plugins/js/package-json/create-package-json.ts index cc93b552bd7c5a..298a04970a3c44 100644 --- a/packages/nx/src/plugins/js/package-json/create-package-json.ts +++ b/packages/nx/src/plugins/js/package-json/create-package-json.ts @@ -77,10 +77,16 @@ export function createPackageJson( ); // for standalone projects we don't want to include all the root dependencies if (graph.nodes[projectName].data.root === '.') { - packageJson = { - name: packageJson.name, - version: packageJson.version, - }; + // TODO: We should probably think more on this - Nx can't always + // detect all external dependencies, and there's not a way currently + // to tell Nx that we need one of these deps. For non-standalone projects + // we tell people to add it to the package.json of the project, and we + // merge it. For standalone, this pattern doesn't work because of this piece of code. + // It breaks expectations, but also, I don't know another way around it currently. + // If Nx doesn't pick up a dep, say some css lib that is only imported in a .scss file, + // we need to be able to tell it to keep that dep in the generated package.json. + delete packageJson.dependencies; + delete packageJson.devDependencies; } } catch (e) {} } diff --git a/tsconfig.base.json b/tsconfig.base.json index 6a85841df63c81..88456cbebb59b8 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -107,6 +107,8 @@ "@nrwl/workspace": ["packages/workspace"], "@nrwl/workspace/*": ["packages/workspace/*"], "@nrwl/workspace/testing": ["packages/workspace/testing"], + "create-nx-workspace": ["packages/create-nx-workspace/index.ts"], + "create-nx-workspace/*": ["packages/create-nx-workspace/*"], "nx": ["packages/nx"], "nx-dev/ui-primitives": ["nx-dev/ui-primitives/src/index.ts"], "nx/*": ["packages/nx/*"]