From 78eb03f28372a722ac5922996d5c4ea985ee26ab Mon Sep 17 00:00:00 2001 From: AgentEnder Date: Fri, 17 Mar 2023 16:42:46 -0400 Subject: [PATCH] feat(nx-plugin): add nx-plugin preset generator --- 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 | 8 +- packages/create-nx-plugin/.eslintrc.json | 3 +- .../create-nx-plugin/bin/create-nx-plugin.ts | 44 ++++---- .../bin/detect-invoked-package-manager.ts | 32 ------ packages/create-nx-plugin/bin/shared.ts | 106 ------------------ packages/create-nx-plugin/package.json | 4 +- .../bin/create-nx-workspace.ts | 3 +- packages/create-nx-workspace/index.ts | 1 + .../src/create-workspace.ts | 3 +- .../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 | 6 +- 40 files changed, 321 insertions(+), 278 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 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/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 10012ce84cb7e2..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', () => { @@ -25,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 c3541797464b0b..02cba1766ad6d8 100644 --- a/packages/create-nx-plugin/.eslintrc.json +++ b/packages/create-nx-plugin/.eslintrc.json @@ -15,8 +15,7 @@ "overrides": [ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": { - } + "rules": {} }, { "files": ["*.ts", "*.tsx"], diff --git a/packages/create-nx-plugin/bin/create-nx-plugin.ts b/packages/create-nx-plugin/bin/create-nx-plugin.ts index 181819033d07e7..b3087ec81eb2c1 100644 --- a/packages/create-nx-plugin/bin/create-nx-plugin.ts +++ b/packages/create-nx-plugin/bin/create-nx-plugin.ts @@ -1,3 +1,4 @@ +#!/usr/bin/env node import chalk = require('chalk'); import enquirer = require('enquirer'); import yargs = require('yargs'); @@ -16,11 +17,10 @@ import { withOptions, withPackageManager, } from 'create-nx-workspace/src/internal-utils/yargs-options'; -import { createWorkspace } from 'create-nx-workspace'; +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 { CreateWorkspaceOptions } from 'create-nx-workspace/src/create-workspace-options'; -import { PackageManager } from 'create-nx-workspace/src/utils/package-manager'; +import type { PackageManager } from 'create-nx-workspace/src/utils/package-manager'; export const yargsDecorator = { 'Options:': `${chalk.green`Options`}:`, @@ -38,8 +38,8 @@ export const yargsDecorator = { const nxVersion = require('../package.json').version; function determinePluginName(parsedArgs: CreateNxPluginArguments) { - if (parsedArgs.name) { - return Promise.resolve(parsedArgs.name); + if (parsedArgs.pluginName) { + return Promise.resolve(parsedArgs.pluginName); } return enquirer @@ -64,8 +64,7 @@ function determinePluginName(parsedArgs: CreateNxPluginArguments) { } interface CreateNxPluginArguments { - name: string; - importPath: string; + pluginName: string; packageManager: PackageManager; ci: CI; allPrompts: boolean; @@ -82,19 +81,19 @@ export const commandsObject: yargs.Argv = yargs // this is the default and only command '$0 [name] [options]', 'Create a new Nx plugin workspace', - withOptions( - (yargs) => - yargs.option('name', { + (yargs) => + withOptions( + yargs.positional('pluginName', { describe: chalk.dim`Plugin name`, type: 'string', - alias: ['pluginName'], + alias: ['name'], }), - withNxCloud, - withCI, - withAllPrompts, - withPackageManager, - withGitOptions - ), + withNxCloud, + withCI, + withAllPrompts, + withPackageManager, + withGitOptions + ), async (argv: yargs.ArgumentsCamelCase) => { await main(argv).catch((error) => { const { version } = require('../package.json'); @@ -117,11 +116,11 @@ export const commandsObject: yargs.Argv = yargs 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 - ); + await createWorkspace('@nrwl/nx-plugin', populatedArguments); } /** @@ -152,3 +151,6 @@ async function normalizeArgsMiddleware( 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 179572c359a977..df0f020b07ec7f 100644 --- a/packages/create-nx-plugin/package.json +++ b/packages/create-nx-plugin/package.json @@ -30,7 +30,9 @@ "homepage": "https://nx.dev", "dependencies": { "create-nx-workspace": "file:../create-nx-workspace", - "yargs-parser": "21.1.1" + "chalk": "^4.1.0", + "enquirer": "~2.3.6", + "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 c9f01cf53fa738..4d17dd28e88576 100644 --- a/packages/create-nx-workspace/bin/create-nx-workspace.ts +++ b/packages/create-nx-workspace/bin/create-nx-workspace.ts @@ -15,10 +15,10 @@ 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, - determineCI, } from '../src/internal-utils/prompts'; import { withAllPrompts, @@ -74,6 +74,7 @@ export const commandsObject: yargs.Argv = yargs .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`, diff --git a/packages/create-nx-workspace/index.ts b/packages/create-nx-workspace/index.ts index 285217767a4876..44ba5f0cc1aa1e 100644 --- a/packages/create-nx-workspace/index.ts +++ b/packages/create-nx-workspace/index.ts @@ -1 +1,2 @@ export * from './src/create-workspace'; +export * 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/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 75578d9e31962b..88456cbebb59b8 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -107,11 +107,11 @@ "@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/*"], - "create-nx-workspace": ["packages/create-nx-workspace/index.ts"], - "create-nx-workspace/*": ["packages/create-nx-workspace/*"] + "nx/*": ["packages/nx/*"] } } }