feat(core): add migration to update workspace generators to a local plugin (#12700)
This commit is contained in:
parent
f04f316271
commit
1743ff10ed
@ -5,7 +5,9 @@ description: 'Runs a workspace generator from the tools/generators directory'
|
||||
|
||||
# workspace-generator
|
||||
|
||||
Runs a workspace generator from the tools/generators directory
|
||||
**Deprecated:** Use a local plugin instead. See: https://nx.dev/deprecated/workspace-generators
|
||||
|
||||
Runs a workspace generator from the tools/generators directory
|
||||
|
||||
## Usage
|
||||
|
||||
@ -17,23 +19,45 @@ Install `nx` globally to invoke the command directly using `nx`, or use `npx nx`
|
||||
|
||||
## Options
|
||||
|
||||
### dryRun
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Default: `false`
|
||||
|
||||
Preview the changes without updating files
|
||||
|
||||
### generator
|
||||
|
||||
Type: `string`
|
||||
|
||||
Name of the generator (e.g., @nrwl/js:library, library)
|
||||
|
||||
### help
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Show help
|
||||
|
||||
### list-generators
|
||||
### interactive
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
List the available workspace-generators
|
||||
Default: `true`
|
||||
|
||||
### name
|
||||
When false disables interactive input prompts for options
|
||||
|
||||
Type: `string`
|
||||
### quiet
|
||||
|
||||
The name of your generator
|
||||
Type: `boolean`
|
||||
|
||||
Hides logs from tree operations (e.g. `CREATE package.json`)
|
||||
|
||||
### verbose
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Prints additional information about the commands (e.g., stack traces)
|
||||
|
||||
### version
|
||||
|
||||
|
||||
@ -5,7 +5,9 @@ description: 'Runs a workspace generator from the tools/generators directory'
|
||||
|
||||
# workspace-generator
|
||||
|
||||
Runs a workspace generator from the tools/generators directory
|
||||
**Deprecated:** Use a local plugin instead. See: https://nx.dev/deprecated/workspace-generators
|
||||
|
||||
Runs a workspace generator from the tools/generators directory
|
||||
|
||||
## Usage
|
||||
|
||||
@ -17,23 +19,45 @@ Install `nx` globally to invoke the command directly using `nx`, or use `npx nx`
|
||||
|
||||
## Options
|
||||
|
||||
### dryRun
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Default: `false`
|
||||
|
||||
Preview the changes without updating files
|
||||
|
||||
### generator
|
||||
|
||||
Type: `string`
|
||||
|
||||
Name of the generator (e.g., @nrwl/js:library, library)
|
||||
|
||||
### help
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Show help
|
||||
|
||||
### list-generators
|
||||
### interactive
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
List the available workspace-generators
|
||||
Default: `true`
|
||||
|
||||
### name
|
||||
When false disables interactive input prompts for options
|
||||
|
||||
Type: `string`
|
||||
### quiet
|
||||
|
||||
The name of your generator
|
||||
Type: `boolean`
|
||||
|
||||
Hides logs from tree operations (e.g. `CREATE package.json`)
|
||||
|
||||
### verbose
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Prints additional information about the commands (e.g., stack traces)
|
||||
|
||||
### version
|
||||
|
||||
|
||||
@ -7,20 +7,7 @@
|
||||
"title": "Create a custom generator",
|
||||
"description": "Create a custom generator.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Generator name.",
|
||||
"$default": { "$source": "argv", "index": 0 },
|
||||
"x-prompt": "What name would you like to use for the workspace generator?"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
}
|
||||
},
|
||||
"properties": {},
|
||||
"required": ["name"],
|
||||
"presets": []
|
||||
},
|
||||
|
||||
@ -8,6 +8,10 @@ Check the [nx-plugin guide](/packages/nx-plugin) for information on creating a n
|
||||
|
||||
## Converting workspace generators to local generators
|
||||
|
||||
{% callout type=\"info\" %}
|
||||
When migrating to Nx 16, a new workspace plugin is automatically generated in the tools folder if you already have workspace-generators.
|
||||
{% /callout %}
|
||||
|
||||
- If you don't already have a local plugin, use Nx to generate one:
|
||||
|
||||
```shell
|
||||
|
||||
@ -86,7 +86,6 @@ describe('Nx Commands', () => {
|
||||
// check for schematics
|
||||
expect(listOutput).toContain('workspace');
|
||||
expect(listOutput).toContain('library');
|
||||
expect(listOutput).toContain('workspace-generator');
|
||||
|
||||
// check for builders
|
||||
expect(listOutput).toContain('run-commands');
|
||||
|
||||
@ -756,180 +756,3 @@ describe('Workspace Tests', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('workspace-generator', () => {
|
||||
const packageManager = getSelectedPackageManager() || 'pnpm';
|
||||
const proj = uniq('workspace');
|
||||
|
||||
beforeAll(() => {
|
||||
runCreateWorkspace(proj, {
|
||||
preset: 'ts',
|
||||
packageManager,
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
let custom: string;
|
||||
let failing: string;
|
||||
|
||||
beforeEach(() => {
|
||||
custom = uniq('custom');
|
||||
failing = uniq('custom-failing');
|
||||
runCLI(`g @nrwl/workspace:workspace-generator ${custom} --no-interactive`);
|
||||
runCLI(`g @nrwl/workspace:workspace-generator ${failing} --no-interactive`);
|
||||
|
||||
checkFilesExist(
|
||||
`tools/generators/${custom}/index.ts`,
|
||||
`tools/generators/${custom}/schema.json`
|
||||
);
|
||||
checkFilesExist(
|
||||
`tools/generators/${failing}/index.ts`,
|
||||
`tools/generators/${failing}/schema.json`
|
||||
);
|
||||
});
|
||||
|
||||
it('should compile only generator files with dependencies', () => {
|
||||
const workspace = uniq('workspace');
|
||||
|
||||
updateFile(
|
||||
'tools/utils/command-line-utils.ts',
|
||||
`
|
||||
export const noop = () => {}
|
||||
`
|
||||
);
|
||||
updateFile(
|
||||
'tools/utils/logger.ts',
|
||||
`
|
||||
export const log = (...args: any[]) => console.log(...args)
|
||||
`
|
||||
);
|
||||
updateFile(
|
||||
`tools/generators/utils.ts`,
|
||||
`
|
||||
export const noop = ()=>{}
|
||||
`
|
||||
);
|
||||
updateFile(`tools/generators/${custom}/index.ts`, (content) => {
|
||||
return `
|
||||
import { log } from '../../utils/logger'; \n
|
||||
${content}
|
||||
`;
|
||||
});
|
||||
|
||||
runCLI(`workspace-generator ${custom} ${workspace} --no-interactive -d`);
|
||||
|
||||
expect(() =>
|
||||
checkFilesExist(
|
||||
`dist/out-tsc/tools/generators/${custom}/index.js`,
|
||||
`dist/out-tsc/tools/generators/utils.js`,
|
||||
`dist/out-tsc/tools/utils/logger.js`
|
||||
)
|
||||
).not.toThrow();
|
||||
expect(() =>
|
||||
checkFilesExist(`dist/out-tsc/tools/utils/utils.js`)
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it('should support workspace-specific generators', async () => {
|
||||
const json = readJson(`tools/generators/${custom}/schema.json`);
|
||||
json.properties['directory'] = {
|
||||
type: 'string',
|
||||
description: 'lib directory',
|
||||
};
|
||||
json.properties['skipTsConfig'] = {
|
||||
type: 'boolean',
|
||||
description: 'skip changes to tsconfig',
|
||||
};
|
||||
json.properties['inlineprop'] = json.properties['name'];
|
||||
json.required = ['inlineprop'];
|
||||
delete json.properties['name'];
|
||||
|
||||
updateFile(`tools/generators/${custom}/schema.json`, JSON.stringify(json));
|
||||
|
||||
const indexFile = readFile(`tools/generators/${custom}/index.ts`);
|
||||
updateFile(
|
||||
`tools/generators/${custom}/index.ts`,
|
||||
indexFile.replace(
|
||||
'name: schema.name',
|
||||
'name: schema.inlineprop, directory: schema.directory, skipTsConfig: schema.skipTsConfig'
|
||||
)
|
||||
);
|
||||
|
||||
const helpOutput = runCLI(`workspace-generator ${custom} --help`);
|
||||
expect(helpOutput).toContain(
|
||||
`workspace-generator ${custom} [inlineprop] (options)`
|
||||
);
|
||||
expect(helpOutput).toContain(`--directory`);
|
||||
expect(helpOutput).toContain(`--skipTsConfig`);
|
||||
|
||||
const workspace = uniq('workspace');
|
||||
const dryRunOutput = runCLI(
|
||||
`workspace-generator ${custom} ${workspace} --no-interactive --directory=dir --skipTsConfig=true -d`
|
||||
);
|
||||
expect(exists(`packages/dir/${workspace}/src/index.ts`)).toEqual(false);
|
||||
expect(dryRunOutput).toContain(
|
||||
`CREATE packages/dir/${workspace}/src/index.ts`
|
||||
);
|
||||
|
||||
runCLI(
|
||||
`workspace-generator ${custom} ${workspace} --no-interactive --directory=dir`
|
||||
);
|
||||
checkFilesExist(`packages/dir/${workspace}/src/index.ts`);
|
||||
|
||||
const jsonFailing = readJson(`tools/generators/${failing}/schema.json`);
|
||||
jsonFailing.properties = {};
|
||||
jsonFailing.required = [];
|
||||
updateFile(
|
||||
`tools/generators/${failing}/schema.json`,
|
||||
JSON.stringify(jsonFailing)
|
||||
);
|
||||
|
||||
updateFile(
|
||||
`tools/generators/${failing}/index.ts`,
|
||||
`
|
||||
export default function() {
|
||||
throw new Error();
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
try {
|
||||
await runCLI(`workspace-generator ${failing} --no-interactive`);
|
||||
fail(`Should exit 1 for a workspace-generator that throws an error`);
|
||||
} catch (e) {}
|
||||
|
||||
const listOutput = runCLI('workspace-generator --list-generators');
|
||||
expect(listOutput).toContain(custom);
|
||||
expect(listOutput).toContain(failing);
|
||||
}, 1000000);
|
||||
|
||||
it('should support angular devkit schematics', () => {
|
||||
const angularDevkitSchematic = uniq('angular-devkit-schematic');
|
||||
runCLI(
|
||||
`g @nrwl/workspace:workspace-generator ${angularDevkitSchematic} --no-interactive`
|
||||
);
|
||||
|
||||
const json = readJson(
|
||||
`tools/generators/${angularDevkitSchematic}/schema.json`
|
||||
);
|
||||
json.properties = {};
|
||||
json.required = [];
|
||||
delete json.cli;
|
||||
updateFile(
|
||||
`tools/generators/${angularDevkitSchematic}/schema.json`,
|
||||
JSON.stringify(json)
|
||||
);
|
||||
|
||||
updateFile(
|
||||
`tools/generators/${angularDevkitSchematic}/index.ts`,
|
||||
`
|
||||
export default function() {
|
||||
return (tree) => tree;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
runCLI(`workspace-generator ${angularDevkitSchematic} --no-interactive`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -259,9 +259,6 @@ describe('Nx Plugin', () => {
|
||||
expect(results).not.toContain(goodMigration);
|
||||
});
|
||||
|
||||
/**
|
||||
* @todo(@AgentEnder): reenable after figuring out @swc-node
|
||||
*/
|
||||
describe('local plugins', () => {
|
||||
let plugin: string;
|
||||
beforeEach(() => {
|
||||
@ -368,6 +365,28 @@ describe('Nx Plugin', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('workspace-generator', () => {
|
||||
let custom: string;
|
||||
|
||||
it('should work with generate wrapper', () => {
|
||||
custom = uniq('custom');
|
||||
const project = uniq('generated-project');
|
||||
runCLI(`g @nrwl/nx-plugin:plugin workspace-plugin --no-interactive`);
|
||||
runCLI(
|
||||
`g @nrwl/nx-plugin:generator ${custom} --project workspace-plugin --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`workspace-generator ${custom} --name ${project} --no-interactive`
|
||||
);
|
||||
expect(() => {
|
||||
checkFilesExist(
|
||||
`libs/${project}/src/index.ts`,
|
||||
`libs/${project}/project.json`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('--directory', () => {
|
||||
it('should create a plugin in the specified directory', () => {
|
||||
const plugin = uniq('plugin');
|
||||
|
||||
@ -23,8 +23,7 @@
|
||||
"error",
|
||||
"@angular-devkit/architect",
|
||||
"@angular-devkit/core",
|
||||
"@angular-devkit/schematics",
|
||||
"@nx/workspace"
|
||||
"@angular-devkit/schematics"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@ -22,6 +22,7 @@ import { addTsLibDependencies } from '@nx/js/src/utils/typescript/add-tslib-depe
|
||||
import { addSwcRegisterDependencies } from '@nx/js/src/utils/swc/add-swc-dependencies';
|
||||
|
||||
import type { Schema } from './schema';
|
||||
import { tsLibVersion } from '@nx/js/src/utils/versions';
|
||||
|
||||
const nxVersion = require('../../../package.json').version;
|
||||
|
||||
@ -87,6 +88,7 @@ export async function pluginGenerator(host: Tree, schema: Schema) {
|
||||
addDependenciesToPackageJson(
|
||||
host,
|
||||
{
|
||||
tslib: tsLibVersion,
|
||||
'@nx/devkit': nxVersion,
|
||||
},
|
||||
{
|
||||
|
||||
@ -266,16 +266,17 @@ export const commandsObject = yargs
|
||||
process.exit(0);
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* @deprecated(v17): Remove `workspace-generator in v17. Use local plugins.
|
||||
*/
|
||||
.command({
|
||||
command: 'workspace-generator [name]',
|
||||
describe: 'Runs a workspace generator from the tools/generators directory',
|
||||
deprecated:
|
||||
'Use a local plugin instead. See: https://nx.dev/deprecated/workspace-generators',
|
||||
aliases: ['workspace-schematic [name]'],
|
||||
builder: async (yargs) =>
|
||||
linkToNxDevAndExamples(
|
||||
await withWorkspaceGeneratorOptions(yargs, process.argv.slice(3)),
|
||||
'workspace-generator'
|
||||
),
|
||||
linkToNxDevAndExamples(withGenerateOptions(yargs), 'workspace-generator'),
|
||||
handler: workspaceGeneratorHandler,
|
||||
})
|
||||
.command({
|
||||
@ -824,142 +825,11 @@ function withRunOneOptions(yargs: yargs.Argv) {
|
||||
}
|
||||
}
|
||||
|
||||
type OptionArgumentDefinition = {
|
||||
type: yargs.Options['type'];
|
||||
describe?: string;
|
||||
default?: any;
|
||||
choices?: yargs.Options['type'][];
|
||||
demandOption?: boolean;
|
||||
};
|
||||
|
||||
type WorkspaceGeneratorProperties = {
|
||||
[name: string]:
|
||||
| {
|
||||
type: yargs.Options['type'];
|
||||
description?: string;
|
||||
default?: any;
|
||||
enum?: yargs.Options['type'][];
|
||||
demandOption?: boolean;
|
||||
}
|
||||
| {
|
||||
type: yargs.PositionalOptionsType;
|
||||
description?: string;
|
||||
default?: any;
|
||||
enum?: yargs.PositionalOptionsType[];
|
||||
$default: {
|
||||
$source: 'argv';
|
||||
index: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
function isPositionalProperty(
|
||||
property: WorkspaceGeneratorProperties[keyof WorkspaceGeneratorProperties]
|
||||
): property is { type: yargs.PositionalOptionsType } {
|
||||
return property['$default']?.['$source'] === 'argv';
|
||||
}
|
||||
|
||||
async function withWorkspaceGeneratorOptions(
|
||||
yargs: yargs.Argv,
|
||||
args: string[]
|
||||
) {
|
||||
// filter out only positional arguments
|
||||
args = args.filter((a) => !a.startsWith('-'));
|
||||
if (args.length) {
|
||||
// this is an actual workspace generator
|
||||
return withCustomGeneratorOptions(yargs, args[0]);
|
||||
} else {
|
||||
yargs
|
||||
.option('list-generators', {
|
||||
describe: 'List the available workspace-generators',
|
||||
type: 'boolean',
|
||||
})
|
||||
.positional('name', {
|
||||
type: 'string',
|
||||
describe: 'The name of your generator',
|
||||
});
|
||||
/**
|
||||
* Don't require `name` if only listing available
|
||||
* schematics
|
||||
*/
|
||||
if ((await yargs.argv).listGenerators !== true) {
|
||||
yargs.demandOption('name');
|
||||
}
|
||||
return yargs;
|
||||
}
|
||||
}
|
||||
|
||||
async function withCustomGeneratorOptions(
|
||||
yargs: yargs.Argv,
|
||||
generatorName: string
|
||||
) {
|
||||
const schema = (
|
||||
await import('./workspace-generators')
|
||||
).workspaceGeneratorSchema(generatorName);
|
||||
const options = [];
|
||||
const positionals = [];
|
||||
|
||||
Object.entries(
|
||||
(schema.properties ?? {}) as WorkspaceGeneratorProperties
|
||||
).forEach(([name, prop]) => {
|
||||
const option: { name: string; definition: OptionArgumentDefinition } = {
|
||||
name,
|
||||
definition: {
|
||||
describe: prop.description,
|
||||
type: prop.type,
|
||||
default: prop.default,
|
||||
choices: prop.enum,
|
||||
},
|
||||
};
|
||||
if (schema.required && schema.required.includes(name)) {
|
||||
option.definition.demandOption = true;
|
||||
}
|
||||
options.push(option);
|
||||
if (isPositionalProperty(prop)) {
|
||||
positionals.push({
|
||||
name,
|
||||
definition: {
|
||||
describe: prop.description,
|
||||
type: prop.type,
|
||||
choices: prop.enum,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let command = generatorName;
|
||||
positionals.forEach(({ name }) => {
|
||||
command += ` [${name}]`;
|
||||
});
|
||||
if (options.length) {
|
||||
command += ' (options)';
|
||||
}
|
||||
|
||||
yargs
|
||||
.command({
|
||||
// this is the default and only command
|
||||
command,
|
||||
describe: schema.description || '',
|
||||
builder: (y) => {
|
||||
options.forEach(({ name, definition }) => {
|
||||
y.option(name, definition);
|
||||
});
|
||||
positionals.forEach(({ name, definition }) => {
|
||||
y.positional(name, definition);
|
||||
});
|
||||
return linkToNxDevAndExamples(y, 'workspace-generator');
|
||||
},
|
||||
handler: workspaceGeneratorHandler,
|
||||
})
|
||||
.fail(() => void 0); // no action is needed on failure as Nx will handle it based on schema validation
|
||||
|
||||
return yargs;
|
||||
}
|
||||
|
||||
async function workspaceGeneratorHandler() {
|
||||
await (
|
||||
await import('./workspace-generators')
|
||||
).workspaceGenerators(process.argv.slice(3));
|
||||
/**
|
||||
* @deprecated(v17): Remove `workspace-generator in v17. Use local plugins.
|
||||
*/
|
||||
async function workspaceGeneratorHandler(args: yargs.Arguments) {
|
||||
await (await import('./workspace-generators')).workspaceGenerators(args);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
|
||||
@ -1,213 +1,35 @@
|
||||
import * as chalk from 'chalk';
|
||||
import { execSync } from 'child_process';
|
||||
import { readdirSync, existsSync } from 'fs';
|
||||
import { copySync, removeSync } from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as yargsParser from 'yargs-parser';
|
||||
import { workspaceRoot } from '../utils/workspace-root';
|
||||
import { fileExists } from '../utils/fileutils';
|
||||
import yargs = require('yargs');
|
||||
import { readNxJson } from '../config/configuration';
|
||||
import { NxJsonConfiguration } from '../devkit-exports';
|
||||
import { NX_PREFIX } from '../utils/logger';
|
||||
import { output } from '../utils/output';
|
||||
import type { CompilerOptions } from 'typescript';
|
||||
import { generate } from './generate';
|
||||
import { readJsonFile, writeJsonFile } from '../utils/fileutils';
|
||||
import { logger } from '../utils/logger';
|
||||
import { getPackageManagerCommand } from '../utils/package-manager';
|
||||
import { normalizePath } from '../utils/path';
|
||||
import { parserConfiguration } from './nx-commands';
|
||||
|
||||
const rootDirectory = workspaceRoot;
|
||||
const toolsDir = path.join(rootDirectory, 'tools');
|
||||
const generatorsDir = path.join(toolsDir, 'generators');
|
||||
const toolsTsConfigPath = path.join(toolsDir, 'tsconfig.tools.json');
|
||||
/**
|
||||
* Wraps `workspace-generator` to invoke `generate`.
|
||||
*
|
||||
* @deprecated(v17): Remove `workspace-generator in v17. Use local plugins.
|
||||
*/
|
||||
export async function workspaceGenerators(args: yargs.Arguments) {
|
||||
const generator = process.argv.slice(3);
|
||||
|
||||
type TsConfig = {
|
||||
extends: string;
|
||||
compilerOptions: CompilerOptions;
|
||||
files?: string[];
|
||||
include?: string[];
|
||||
exclude?: string[];
|
||||
references?: Array<{ path: string }>;
|
||||
};
|
||||
|
||||
export async function workspaceGenerators(args: string[]) {
|
||||
const outDir = compileTools();
|
||||
const collectionFile = path.join(outDir, 'workspace-generators.json');
|
||||
const parsedArgs = parseOptions(args, outDir, collectionFile);
|
||||
if (parsedArgs.listGenerators) {
|
||||
return listGenerators(collectionFile);
|
||||
} else {
|
||||
process.exitCode = await generate(process.cwd(), parsedArgs);
|
||||
}
|
||||
}
|
||||
|
||||
export function workspaceGeneratorSchema(name: string) {
|
||||
const schemaFile = path.join(generatorsDir, name, 'schema.json');
|
||||
|
||||
if (fileExists(schemaFile)) {
|
||||
return readJsonFile(schemaFile);
|
||||
} else {
|
||||
logger.error(`Cannot find schema for ${name}. Does the generator exist?`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// compile tools
|
||||
function compileTools() {
|
||||
const toolsOutDir = getToolsOutDir();
|
||||
removeSync(toolsOutDir);
|
||||
compileToolsDir(toolsOutDir);
|
||||
|
||||
const generatorsOutDir = path.join(toolsOutDir, 'generators');
|
||||
const collectionData = constructCollection();
|
||||
writeJsonFile(
|
||||
path.join(generatorsOutDir, 'workspace-generators.json'),
|
||||
collectionData
|
||||
);
|
||||
return generatorsOutDir;
|
||||
}
|
||||
|
||||
function getToolsOutDir() {
|
||||
const outDir = toolsTsConfig().compilerOptions.outDir;
|
||||
|
||||
if (!outDir) {
|
||||
logger.error(`${toolsTsConfigPath} must specify an outDir`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return path.resolve(toolsDir, outDir);
|
||||
}
|
||||
|
||||
function compileToolsDir(outDir: string) {
|
||||
copySync(generatorsDir, path.join(outDir, 'generators'));
|
||||
|
||||
const tmpTsConfigPath = createTmpTsConfig(toolsTsConfigPath, {
|
||||
include: [path.join(generatorsDir, '**/*.ts')],
|
||||
output.warn({
|
||||
title: `${NX_PREFIX} Workspace Generators are no longer supported`,
|
||||
bodyLines: [
|
||||
'Instead, Nx now supports executing generators or executors from ',
|
||||
'local plugins. To run a generator from a local plugin, ',
|
||||
'use `nx generate` like you would with any other generator.',
|
||||
'',
|
||||
'For more information, see: https://nx.dev/deprecated/workspace-generators',
|
||||
],
|
||||
});
|
||||
|
||||
const pmc = getPackageManagerCommand();
|
||||
const tsc = `${pmc.exec} tsc`;
|
||||
try {
|
||||
execSync(`${tsc} -p ${tmpTsConfigPath}`, {
|
||||
stdio: 'inherit',
|
||||
cwd: rootDirectory,
|
||||
});
|
||||
} catch {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function constructCollection() {
|
||||
const generators = {};
|
||||
const schematics = {};
|
||||
readdirSync(generatorsDir).forEach((c) => {
|
||||
const childDir = path.join(generatorsDir, c);
|
||||
if (existsSync(path.join(childDir, 'schema.json'))) {
|
||||
const generatorOrSchematic = {
|
||||
factory: `./${c}`,
|
||||
schema: `./${normalizePath(path.join(c, 'schema.json'))}`,
|
||||
description: `Schematic ${c}`,
|
||||
};
|
||||
|
||||
const { isSchematic } = readJsonFile(path.join(childDir, 'schema.json'));
|
||||
if (isSchematic) {
|
||||
schematics[c] = generatorOrSchematic;
|
||||
} else {
|
||||
generators[c] = generatorOrSchematic;
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
name: 'workspace-generators',
|
||||
version: '1.0',
|
||||
generators,
|
||||
schematics,
|
||||
};
|
||||
}
|
||||
|
||||
function toolsTsConfig(): TsConfig {
|
||||
return readJsonFile<TsConfig>(toolsTsConfigPath);
|
||||
}
|
||||
|
||||
function listGenerators(collectionFile: string) {
|
||||
try {
|
||||
const bodyLines: string[] = [];
|
||||
|
||||
const collection = readJsonFile(collectionFile);
|
||||
|
||||
bodyLines.push(chalk.bold(chalk.green('WORKSPACE GENERATORS')));
|
||||
bodyLines.push('');
|
||||
bodyLines.push(
|
||||
...Object.entries(collection.generators).map(
|
||||
([schematicName, schematicMeta]: [string, any]) => {
|
||||
return `${chalk.bold(schematicName)} : ${schematicMeta.description}`;
|
||||
}
|
||||
)
|
||||
);
|
||||
bodyLines.push('');
|
||||
|
||||
output.log({
|
||||
title: '',
|
||||
bodyLines,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.fatal(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function parseOptions(
|
||||
args: string[],
|
||||
outDir: string,
|
||||
collectionFile: string
|
||||
): { [k: string]: any } {
|
||||
const schemaPath = path.join(outDir, args[0], 'schema.json');
|
||||
let booleanProps = [];
|
||||
if (fileExists(schemaPath)) {
|
||||
const { properties } = readJsonFile(
|
||||
path.join(outDir, args[0], 'schema.json')
|
||||
);
|
||||
if (properties) {
|
||||
booleanProps = Object.keys(properties).filter(
|
||||
(key) => properties[key].type === 'boolean'
|
||||
);
|
||||
}
|
||||
}
|
||||
const parsed = yargsParser(args, {
|
||||
boolean: ['dryRun', 'listGenerators', 'interactive', ...booleanProps],
|
||||
alias: {
|
||||
dryRun: ['d'],
|
||||
listSchematics: ['l'],
|
||||
},
|
||||
default: {
|
||||
interactive: true,
|
||||
},
|
||||
configuration: parserConfiguration,
|
||||
});
|
||||
parsed['generator'] = `${collectionFile}:${parsed['_'][0]}`;
|
||||
parsed['_'] = parsed['_'].slice(1);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function createTmpTsConfig(
|
||||
tsconfigPath: string,
|
||||
updateConfig: Partial<TsConfig>
|
||||
) {
|
||||
const tmpTsConfigPath = path.join(
|
||||
path.dirname(tsconfigPath),
|
||||
'tsconfig.generated.json'
|
||||
);
|
||||
const originalTSConfig = readJsonFile<TsConfig>(tsconfigPath);
|
||||
const generatedTSConfig: TsConfig = {
|
||||
...originalTSConfig,
|
||||
...updateConfig,
|
||||
};
|
||||
process.on('exit', () => cleanupTmpTsConfigFile(tmpTsConfigPath));
|
||||
writeJsonFile(tmpTsConfigPath, generatedTSConfig);
|
||||
|
||||
return tmpTsConfigPath;
|
||||
}
|
||||
|
||||
function cleanupTmpTsConfigFile(tmpTsConfigPath: string) {
|
||||
if (tmpTsConfigPath) {
|
||||
removeSync(tmpTsConfigPath);
|
||||
}
|
||||
const nxJson: NxJsonConfiguration = readNxJson();
|
||||
const collection = nxJson.npmScope
|
||||
? `@${nxJson.npmScope}/workspace-plugin`
|
||||
: 'workspace-plugin';
|
||||
|
||||
args._ = args._.slice(1);
|
||||
args.generator = `${collection}:${generator}`;
|
||||
|
||||
return (await import('./generate')).generate(process.cwd(), args);
|
||||
}
|
||||
|
||||
@ -89,6 +89,12 @@
|
||||
"version": "16.0.0-beta.1",
|
||||
"description": "Replace @nrwl/workspace with @nx/workspace",
|
||||
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages"
|
||||
},
|
||||
"16-0-0-move-workspace-generators-into-local-plugin": {
|
||||
"version": "16.0.0-beta.3",
|
||||
"description": "Generates a plugin called 'workspace-plugin' containing your workspace generators.",
|
||||
"cli": "nx",
|
||||
"implementation": "./src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
|
||||
@ -10,17 +10,21 @@ export function createProjectConfigurationInNewDestination(
|
||||
schema: NormalizedSchema,
|
||||
projectConfig: ProjectConfiguration
|
||||
) {
|
||||
if (projectConfig.name) {
|
||||
projectConfig.name = schema.newProjectName;
|
||||
}
|
||||
projectConfig.name = schema.newProjectName;
|
||||
|
||||
// Subtle bug if project name === path, where the updated name was being overrideen.
|
||||
const { name, ...rest } = projectConfig;
|
||||
|
||||
// replace old root path with new one
|
||||
const projectString = JSON.stringify(projectConfig);
|
||||
const projectString = JSON.stringify(rest);
|
||||
const newProjectString = projectString.replace(
|
||||
new RegExp(projectConfig.root, 'g'),
|
||||
schema.relativeToRootDestination
|
||||
);
|
||||
const newProject: ProjectConfiguration = JSON.parse(newProjectString);
|
||||
const newProject: ProjectConfiguration = {
|
||||
name,
|
||||
...JSON.parse(newProjectString),
|
||||
};
|
||||
|
||||
// Create a new project with the root replaced
|
||||
addProjectConfiguration(tree, schema.newProjectName, newProject);
|
||||
|
||||
@ -11,7 +11,8 @@ export function normalizeSchema(
|
||||
const destination = schema.destination.startsWith('/')
|
||||
? normalizeSlashes(schema.destination.slice(1))
|
||||
: schema.destination;
|
||||
const newProjectName = getNewProjectName(destination);
|
||||
const newProjectName =
|
||||
schema.newProjectName ?? getNewProjectName(destination);
|
||||
const { npmScope } = getWorkspaceLayout(tree);
|
||||
|
||||
return {
|
||||
|
||||
@ -110,7 +110,9 @@ export function updateImports(
|
||||
|
||||
if (schema.updateImportPath) {
|
||||
tsConfig.compilerOptions.paths[projectRef.to] = updatedPath;
|
||||
delete tsConfig.compilerOptions.paths[projectRef.from];
|
||||
if (projectRef.from !== projectRef.to) {
|
||||
delete tsConfig.compilerOptions.paths[projectRef.from];
|
||||
}
|
||||
} else {
|
||||
tsConfig.compilerOptions.paths[projectRef.from] = updatedPath;
|
||||
}
|
||||
|
||||
@ -19,6 +19,10 @@ export function getDestination(
|
||||
schema: Schema,
|
||||
project: ProjectConfiguration
|
||||
): string {
|
||||
if (schema.destinationRelativeToRoot) {
|
||||
return schema.destination;
|
||||
}
|
||||
|
||||
const projectType = project.projectType;
|
||||
|
||||
const workspaceLayout = getWorkspaceLayout(host);
|
||||
|
||||
@ -4,10 +4,11 @@ export interface Schema {
|
||||
importPath?: string;
|
||||
updateImportPath: boolean;
|
||||
skipFormat?: boolean;
|
||||
destinationRelativeToRoot?: boolean;
|
||||
newProjectName?: string;
|
||||
}
|
||||
|
||||
export interface NormalizedSchema extends Schema {
|
||||
importPath: string;
|
||||
newProjectName: string;
|
||||
relativeToRootDestination: string;
|
||||
}
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
import { Tree, formatFiles, installPackagesTask } from '@nx/devkit';
|
||||
import { libraryGenerator } from '@nx/js';
|
||||
|
||||
export default async function(tree: Tree, schema: any) {
|
||||
await libraryGenerator(tree, {name: schema.name});
|
||||
await formatFiles(tree);
|
||||
return () => {
|
||||
installPackagesTask(tree)
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"cli": "nx",
|
||||
"$id": "<%= name %>",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Library name",
|
||||
"$default": {
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
@ -1,4 +1 @@
|
||||
export interface Schema {
|
||||
name: string;
|
||||
skipFormat: boolean;
|
||||
}
|
||||
export interface Schema {}
|
||||
|
||||
@ -4,22 +4,6 @@
|
||||
"title": "Create a custom generator",
|
||||
"description": "Create a custom generator.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Generator name.",
|
||||
"$default": {
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
},
|
||||
"x-prompt": "What name would you like to use for the workspace generator?"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
}
|
||||
},
|
||||
"properties": {},
|
||||
"required": ["name"]
|
||||
}
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import workspaceGenerator from './workspace-generator';
|
||||
|
||||
describe('workspace-generator', () => {
|
||||
it('should generate a target', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
const opts = {
|
||||
name: 'custom',
|
||||
skipFormat: true,
|
||||
};
|
||||
|
||||
await workspaceGenerator(tree, opts);
|
||||
|
||||
expect(tree.exists('tools/generators/custom/index.ts')).toBeTruthy();
|
||||
expect(tree.exists('tools/generators/custom/schema.json')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -1,42 +1,15 @@
|
||||
import { Schema } from './schema';
|
||||
import {
|
||||
Tree,
|
||||
formatFiles,
|
||||
generateFiles,
|
||||
names,
|
||||
joinPathFragments,
|
||||
addDependenciesToPackageJson,
|
||||
} from '@nx/devkit';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
import { Tree, stripIndents } from '@nx/devkit';
|
||||
|
||||
export default async function (host: Tree, schema: Schema) {
|
||||
const options = normalizeOptions(schema);
|
||||
const message = stripIndents`Workspace Generators are no longer supported. Instead,
|
||||
Nx now supports executing generators or executors from local plugins. To get
|
||||
started, install @nx/nx-plugin and run \`nx g plugin\`.
|
||||
|
||||
generateFiles(
|
||||
host,
|
||||
joinPathFragments(__dirname, 'files'),
|
||||
joinPathFragments('tools/generators', schema.name),
|
||||
options
|
||||
);
|
||||
Afterwards, or if you already have an Nx plugin, you can run
|
||||
\`nx g generator --project {my-plugin}\` to add a new generator.
|
||||
|
||||
For more information, see: https://nx.dev/deprecated/workspace-generators`;
|
||||
|
||||
const installTask = addDependenciesToPackageJson(
|
||||
host,
|
||||
{},
|
||||
{
|
||||
'@nx/devkit': nxVersion,
|
||||
// types/node is neccessary for pnpm since it's used in tsconfig and transitive
|
||||
// dependencies are not resolved correctly
|
||||
'@types/node': 'latest',
|
||||
}
|
||||
);
|
||||
|
||||
if (!schema.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
return installTask;
|
||||
}
|
||||
|
||||
function normalizeOptions(options: Schema): any {
|
||||
const name = names(options.name).fileName;
|
||||
return { ...options, name, tmpl: '' };
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
@ -0,0 +1,174 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import {
|
||||
Tree,
|
||||
readProjectConfiguration,
|
||||
readJson,
|
||||
joinPathFragments,
|
||||
GeneratorsJson,
|
||||
ProjectConfiguration,
|
||||
stripIndents,
|
||||
getProjects,
|
||||
} from '@nx/devkit';
|
||||
|
||||
import generator from './move-workspace-generators-to-local-plugin';
|
||||
|
||||
describe('move-workspace-generators-to-local-plugin', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
});
|
||||
|
||||
it('should find single workspace generator successfully', async () => {
|
||||
await workspaceGeneratorGenerator(tree, {
|
||||
name: 'my-generator',
|
||||
});
|
||||
await generator(tree);
|
||||
console.log(getProjects(tree).keys());
|
||||
const config = readProjectConfiguration(tree, 'workspace-plugin');
|
||||
expect(config.root).toEqual('tools/workspace-plugin');
|
||||
|
||||
assertValidGenerator(tree, config, 'my-generator');
|
||||
});
|
||||
|
||||
it('should convert multiple workspace generators successfully', async () => {
|
||||
const generators = [...new Array(10)].map((x) => uniq('generator'));
|
||||
for (const name of generators) {
|
||||
await workspaceGeneratorGenerator(tree, {
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
await generator(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'workspace-plugin');
|
||||
expect(config.root).toEqual('tools/workspace-plugin');
|
||||
|
||||
for (const generator of generators) {
|
||||
assertValidGenerator(tree, config, generator);
|
||||
}
|
||||
});
|
||||
|
||||
it('should be idempotent', async () => {
|
||||
const generators = [...new Array(10)].map((x) => uniq('generator'));
|
||||
for (const name of generators) {
|
||||
await workspaceGeneratorGenerator(tree, {
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
await generator(tree);
|
||||
|
||||
const generatorsJson = readJson(
|
||||
tree,
|
||||
'tools/workspace-plugin/generators.json'
|
||||
);
|
||||
|
||||
await generator(tree);
|
||||
expect(readJson(tree, 'tools/workspace-plugin/generators.json')).toEqual(
|
||||
generatorsJson
|
||||
);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'workspace-plugin');
|
||||
|
||||
for (const generator of generators) {
|
||||
assertValidGenerator(tree, config, generator);
|
||||
}
|
||||
});
|
||||
|
||||
it('should merge new generators into existing plugin', async () => {
|
||||
const generators = [...new Array(10)].map((x) => uniq('generator'));
|
||||
for (const name of generators) {
|
||||
await workspaceGeneratorGenerator(tree, {
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
await generator(tree);
|
||||
|
||||
const moreGenerators = [...new Array(5)].map((x) => uniq('generator'));
|
||||
for (const name of moreGenerators) {
|
||||
await workspaceGeneratorGenerator(tree, {
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
await generator(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'workspace-plugin');
|
||||
|
||||
for (const generator of generators.concat(moreGenerators)) {
|
||||
assertValidGenerator(tree, config, generator);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function assertValidGenerator(
|
||||
tree: Tree,
|
||||
config: ProjectConfiguration,
|
||||
generator: string
|
||||
) {
|
||||
const generatorsJson = readJson<GeneratorsJson>(
|
||||
tree,
|
||||
joinPathFragments(config.root, 'generators.json')
|
||||
);
|
||||
expect(generatorsJson.generators).toHaveProperty(generator);
|
||||
const generatorImplPath = joinPathFragments(
|
||||
config.root,
|
||||
generatorsJson.generators[generator].implementation,
|
||||
'index.ts'
|
||||
);
|
||||
expect(tree.exists(generatorImplPath)).toBeTruthy();
|
||||
const generatorSchemaPath = joinPathFragments(
|
||||
config.root,
|
||||
generatorsJson.generators[generator].schema
|
||||
);
|
||||
expect(tree.exists(generatorSchemaPath)).toBeTruthy();
|
||||
}
|
||||
|
||||
function uniq(prefix: string) {
|
||||
return `${prefix}${Math.floor(Math.random() * 10000000)}`;
|
||||
}
|
||||
|
||||
async function workspaceGeneratorGenerator(
|
||||
host: Tree,
|
||||
schema: { name: string }
|
||||
) {
|
||||
const outputDirectory = joinPathFragments('tools/generators', schema.name);
|
||||
|
||||
host.write(
|
||||
joinPathFragments(outputDirectory, 'index.ts'),
|
||||
stripIndents`import { Tree, formatFiles, installPackagesTask } from '@nx/devkit';
|
||||
import { libraryGenerator } from '@nx/workspace/generators';
|
||||
|
||||
export default async function(tree: Tree, schema: any) {
|
||||
await libraryGenerator(tree, {name: schema.name});
|
||||
await formatFiles(tree);
|
||||
return () => {
|
||||
installPackagesTask(tree)
|
||||
}
|
||||
}`
|
||||
);
|
||||
|
||||
host.write(
|
||||
joinPathFragments(outputDirectory, 'schema.json'),
|
||||
stripIndents`{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"cli": "nx",
|
||||
"$id": "<%= name %>",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Library name",
|
||||
"$default": {
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
`
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,188 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
ensurePackage,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
getWorkspaceLayout,
|
||||
joinPathFragments,
|
||||
output,
|
||||
ProjectConfiguration,
|
||||
readJson,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateJson,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
// nx-ignore-next-line
|
||||
import * as path from 'path';
|
||||
import {
|
||||
GeneratorsJson,
|
||||
GeneratorsJsonEntry,
|
||||
} from 'nx/src/config/misc-interfaces';
|
||||
import { moveGenerator } from '../../generators/move/move';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
import { PackageJson } from 'nx/src/utils/package-json';
|
||||
|
||||
const PROJECT_NAME = 'workspace-plugin';
|
||||
const DESTINATION = `tools/${PROJECT_NAME}`;
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
const tasks = [];
|
||||
if (!tree.children('tools/generators').length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let project = getProjects(tree).get(PROJECT_NAME);
|
||||
if (!project) {
|
||||
await createNewPlugin(tree);
|
||||
tasks.push(
|
||||
addDependenciesToPackageJson(
|
||||
tree,
|
||||
{},
|
||||
{
|
||||
'@nx/nx-plugin': nxVersion,
|
||||
}
|
||||
)
|
||||
);
|
||||
project = readProjectConfiguration(tree, PROJECT_NAME);
|
||||
}
|
||||
await updateExistingPlugin(tree, project);
|
||||
await formatFiles(tree);
|
||||
return () => {
|
||||
for (const task of tasks) {
|
||||
task();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Inspired by packages/nx/src/command-line/workspace-generators.ts
|
||||
function collectAndMoveGenerators(tree: Tree, destinationProjectRoot: string) {
|
||||
const generators: Record<string, GeneratorsJsonEntry> = {};
|
||||
const generatorsDir = 'tools/generators';
|
||||
const destinationDir = joinPathFragments(
|
||||
destinationProjectRoot,
|
||||
'src',
|
||||
'generators'
|
||||
);
|
||||
for (const c of tree.children('tools/generators')) {
|
||||
const childDir = path.join(generatorsDir, c);
|
||||
const schemaPath = joinPathFragments(childDir, 'schema.json');
|
||||
if (tree.exists(schemaPath)) {
|
||||
const schema = readJson(tree, schemaPath);
|
||||
generators[c] = {
|
||||
implementation: `./src/generators/${c}`,
|
||||
schema: `./src/generators/${joinPathFragments(c, 'schema.json')}`,
|
||||
description: schema.description ?? `Generator ${c}`,
|
||||
};
|
||||
tree.rename(childDir, joinPathFragments(destinationDir, c));
|
||||
}
|
||||
}
|
||||
return generators;
|
||||
}
|
||||
|
||||
async function createNewPlugin(tree: Tree) {
|
||||
ensurePackage('@nx/nx-plugin', nxVersion);
|
||||
const { pluginGenerator } =
|
||||
// nx-ignore-next-line
|
||||
require('@nx/nx-plugin/src/generators/plugin/plugin');
|
||||
|
||||
// nx-ignore-next-line
|
||||
const { Linter } = ensurePackage('@nx/linter', nxVersion);
|
||||
|
||||
const { npmScope } = getWorkspaceLayout(tree);
|
||||
const importPath = npmScope ? `${npmScope}/${PROJECT_NAME}` : PROJECT_NAME;
|
||||
|
||||
await pluginGenerator(tree, {
|
||||
minimal: true,
|
||||
name: PROJECT_NAME,
|
||||
importPath: importPath,
|
||||
skipTsConfig: false,
|
||||
compiler: 'tsc',
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: true,
|
||||
skipLintChecks: false,
|
||||
unitTestRunner: 'jest',
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
getCreateGeneratorsJson()(
|
||||
tree,
|
||||
readProjectConfiguration(tree, PROJECT_NAME).root,
|
||||
PROJECT_NAME
|
||||
);
|
||||
await moveGeneratedPlugin(tree, DESTINATION, importPath);
|
||||
}
|
||||
|
||||
function moveGeneratedPlugin(
|
||||
tree: Tree,
|
||||
destination: string,
|
||||
importPath: string
|
||||
) {
|
||||
const config = readProjectConfiguration(tree, PROJECT_NAME);
|
||||
if (config.root !== DESTINATION) {
|
||||
return moveGenerator(tree, {
|
||||
destination,
|
||||
projectName: PROJECT_NAME,
|
||||
newProjectName: PROJECT_NAME,
|
||||
updateImportPath: true,
|
||||
destinationRelativeToRoot: true,
|
||||
importPath: importPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateExistingPlugin(tree: Tree, project: ProjectConfiguration) {
|
||||
const packageJson = readJson<PackageJson>(
|
||||
tree,
|
||||
joinPathFragments(project.root, 'package.json')
|
||||
);
|
||||
const defaultGeneratorsPath = joinPathFragments(
|
||||
project.root,
|
||||
'generators.json'
|
||||
);
|
||||
let generatorsJsonPath =
|
||||
packageJson.generators ||
|
||||
packageJson.schematics ||
|
||||
tree.exists(defaultGeneratorsPath)
|
||||
? defaultGeneratorsPath
|
||||
: null;
|
||||
if (!generatorsJsonPath) {
|
||||
getCreateGeneratorsJson()(
|
||||
tree,
|
||||
readProjectConfiguration(tree, PROJECT_NAME).root,
|
||||
PROJECT_NAME
|
||||
);
|
||||
generatorsJsonPath = defaultGeneratorsPath;
|
||||
}
|
||||
updateJson<GeneratorsJson>(tree, generatorsJsonPath, (json) => {
|
||||
const generators = collectAndMoveGenerators(tree, project.root);
|
||||
json.generators ??= {};
|
||||
for (const generator in generators) {
|
||||
if (json.generators[generator]) {
|
||||
output.warn({
|
||||
title: `Generator ${generator} already exists in ${project.name}`,
|
||||
bodyLines: [
|
||||
'Since you have a generator with the same name in your plugin, the generator from workspace-generators has been discarded.',
|
||||
],
|
||||
});
|
||||
} else {
|
||||
json.generators[generator] = generators[generator];
|
||||
}
|
||||
}
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
function getCreateGeneratorsJson(): (
|
||||
host: Tree,
|
||||
projectRoot: string,
|
||||
projectName: string,
|
||||
skipLintChecks?: boolean,
|
||||
skipFormat?: boolean
|
||||
) => Promise<void> {
|
||||
// We cant use `as typeof import('@nx/nx-plugin/src/generators/generator/generator');` here
|
||||
// because it will cause a typescript error at build time.
|
||||
const { createGeneratorsJson } =
|
||||
// nx-ignore-next-line
|
||||
require('@nx/nx-plugin/src/generators/generator/generator');
|
||||
return createGeneratorsJson;
|
||||
}
|
||||
@ -192,6 +192,16 @@ const IGNORE_MATCHES_BY_FILE: Record<string, string[]> = {
|
||||
'../../packages/angular/src/migrations/update-12-3-0/update-storybook.ts'
|
||||
),
|
||||
],
|
||||
'@nx/nx-plugin': [
|
||||
join(
|
||||
__dirname,
|
||||
'../../packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.spec.ts'
|
||||
),
|
||||
join(
|
||||
__dirname,
|
||||
'../../packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.ts'
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
export default async function getMissingDependencies(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user