feat(nx-plugin): reuse utilities from create-nx-workspace for create-nx-plugin (#15743)
This commit is contained in:
parent
39646cfa9a
commit
6e7234c1aa
@ -8,7 +8,8 @@
|
|||||||
"plugins": ["@typescript-eslint", "@nrwl/nx"],
|
"plugins": ["@typescript-eslint", "@nrwl/nx"],
|
||||||
"extends": [],
|
"extends": [],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/explicit-module-boundary-types": "off"
|
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||||
|
"no-restricted-imports": ["error", "create-nx-workspace"]
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5512,6 +5512,14 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
"disableCollapsible": false
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "preset",
|
||||||
|
"path": "/packages/nx-plugin/generators/preset",
|
||||||
|
"name": "preset",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
|
|||||||
@ -1964,6 +1964,15 @@
|
|||||||
"originalFilePath": "/packages/nx-plugin/src/generators/lint-checks/schema.json",
|
"originalFilePath": "/packages/nx-plugin/src/generators/lint-checks/schema.json",
|
||||||
"path": "/packages/nx-plugin/generators/plugin-lint-checks",
|
"path": "/packages/nx-plugin/generators/plugin-lint-checks",
|
||||||
"type": "generator"
|
"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"
|
"path": "/packages/nx-plugin"
|
||||||
|
|||||||
@ -1939,6 +1939,15 @@
|
|||||||
"originalFilePath": "/packages/nx-plugin/src/generators/lint-checks/schema.json",
|
"originalFilePath": "/packages/nx-plugin/src/generators/lint-checks/schema.json",
|
||||||
"path": "nx-plugin/generators/plugin-lint-checks",
|
"path": "nx-plugin/generators/plugin-lint-checks",
|
||||||
"type": "generator"
|
"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",
|
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||||
|
|||||||
28
docs/generated/packages/nx-plugin/generators/preset.json
Normal file
28
docs/generated/packages/nx-plugin/generators/preset.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
@ -235,7 +235,7 @@ export function runCreatePlugin(
|
|||||||
} create-nx-plugin@${getPublishedVersion()} ${name}`;
|
} create-nx-plugin@${getPublishedVersion()} ${name}`;
|
||||||
|
|
||||||
if (pluginName) {
|
if (pluginName) {
|
||||||
command += ` --pluginName=${pluginName}`;
|
command += ` --pluginName=${pluginName} --no-nxCloud`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packageManager && !useDetectedPm) {
|
if (packageManager && !useDetectedPm) {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
uniq,
|
uniq,
|
||||||
runCreatePlugin,
|
runCreatePlugin,
|
||||||
cleanupProject,
|
cleanupProject,
|
||||||
|
tmpProjPath,
|
||||||
} from '@nrwl/e2e/utils';
|
} from '@nrwl/e2e/utils';
|
||||||
|
|
||||||
describe('create-nx-plugin', () => {
|
describe('create-nx-plugin', () => {
|
||||||
@ -13,8 +14,7 @@ describe('create-nx-plugin', () => {
|
|||||||
|
|
||||||
afterEach(() => cleanupProject());
|
afterEach(() => cleanupProject());
|
||||||
|
|
||||||
// TODO: Re-enable to work with pnpm
|
it('should be able to create a plugin repo and run plugin e2e', () => {
|
||||||
xit('should be able to create a plugin repo and run plugin e2e', () => {
|
|
||||||
const wsName = uniq('ws-plugin');
|
const wsName = uniq('ws-plugin');
|
||||||
const pluginName = uniq('plugin');
|
const pluginName = uniq('plugin');
|
||||||
|
|
||||||
@ -26,10 +26,11 @@ describe('create-nx-plugin', () => {
|
|||||||
checkFilesExist(
|
checkFilesExist(
|
||||||
'package.json',
|
'package.json',
|
||||||
packageManagerLockFile[packageManager],
|
packageManagerLockFile[packageManager],
|
||||||
`packages/${pluginName}/package.json`,
|
`project.json`,
|
||||||
`packages/${pluginName}/project.json`
|
`generators.json`,
|
||||||
|
`executors.json`
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(() => runCLI(`e2e ${pluginName}-e2e`)).not.toThrow();
|
expect(() => runCLI(`e2e e2e`)).not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,16 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../.eslintrc",
|
"extends": "../../.eslintrc",
|
||||||
"rules": {},
|
"rules": {
|
||||||
|
"no-restricted-imports": [
|
||||||
|
"error",
|
||||||
|
"@nrwl/workspace",
|
||||||
|
"@angular-devkit/core",
|
||||||
|
"@angular-devkit/architect",
|
||||||
|
"@angular-devkit/schematics",
|
||||||
|
"nx",
|
||||||
|
"@nrwl/devkit"
|
||||||
|
]
|
||||||
|
},
|
||||||
"ignorePatterns": ["!**/*"],
|
"ignorePatterns": ["!**/*"],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,156 +1,43 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
import chalk = require('chalk');
|
||||||
// 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';
|
|
||||||
import {
|
|
||||||
detectInvokedPackageManager,
|
|
||||||
PackageManager,
|
|
||||||
} from './detect-invoked-package-manager';
|
|
||||||
import enquirer = require('enquirer');
|
import enquirer = require('enquirer');
|
||||||
import yargsParser = require('yargs-parser');
|
import yargs = require('yargs');
|
||||||
|
|
||||||
|
import {
|
||||||
|
determineCI,
|
||||||
|
determineDefaultBase,
|
||||||
|
determineNxCloud,
|
||||||
|
determinePackageManager,
|
||||||
|
} from 'create-nx-workspace/src/internal-utils/prompts';
|
||||||
|
import {
|
||||||
|
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 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, {
|
function determinePluginName(parsedArgs: CreateNxPluginArguments) {
|
||||||
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<NxJsonConfiguration>(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<string> {
|
|
||||||
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) {
|
|
||||||
if (parsedArgs.pluginName) {
|
if (parsedArgs.pluginName) {
|
||||||
return Promise.resolve(parsedArgs.pluginName);
|
return Promise.resolve(parsedArgs.pluginName);
|
||||||
}
|
}
|
||||||
@ -158,54 +45,112 @@ function determinePluginName(parsedArgs) {
|
|||||||
return enquirer
|
return enquirer
|
||||||
.prompt([
|
.prompt([
|
||||||
{
|
{
|
||||||
name: 'PluginName',
|
name: 'pluginName',
|
||||||
message: `Plugin name `,
|
message: `Plugin name `,
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
validate: (s) => (s.length ? true : 'Name cannot be empty'),
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.then((a: { PluginName: string }) => {
|
.then((a: { pluginName: string }) => {
|
||||||
if (!a.PluginName) {
|
if (!a.pluginName) {
|
||||||
output.error({
|
output.error({
|
||||||
title: 'Invalid name',
|
title: 'Invalid name',
|
||||||
bodyLines: [`Name cannot be empty`],
|
bodyLines: [`Name cannot be empty`],
|
||||||
});
|
});
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
return a.PluginName;
|
return a.pluginName;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showHelp() {
|
interface CreateNxPluginArguments {
|
||||||
console.log(`
|
pluginName: string;
|
||||||
Usage: <name> [options]
|
packageManager: PackageManager;
|
||||||
|
ci: CI;
|
||||||
Create a new Nx workspace
|
allPrompts: boolean;
|
||||||
|
nxCloud: boolean;
|
||||||
Args:
|
|
||||||
|
|
||||||
name workspace name (e.g., org name)
|
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
pluginName the name of the plugin to be created
|
|
||||||
`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsedArgs.help) {
|
export const commandsObject: yargs.Argv<CreateNxPluginArguments> = yargs
|
||||||
showHelp();
|
.wrap(yargs.terminalWidth())
|
||||||
process.exit(0);
|
.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<CreateNxPluginArguments>) => {
|
||||||
|
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<CreateNxPluginArguments>;
|
||||||
|
|
||||||
|
async function main(parsedArgs: yargs.Arguments<CreateNxPluginArguments>) {
|
||||||
|
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();
|
* This function is used to normalize the arguments passed to the command.
|
||||||
determineWorkspaceName(parsedArgs).then((workspaceName) => {
|
* It would:
|
||||||
return determinePluginName(parsedArgs).then((pluginName) => {
|
* - normalize the preset.
|
||||||
const tmpDir = createSandbox(packageManager);
|
* @param argv user arguments
|
||||||
createWorkspace(tmpDir, packageManager, parsedArgs, workspaceName);
|
*/
|
||||||
updateWorkspace(workspaceName);
|
async function normalizeArgsMiddleware(
|
||||||
createNxPlugin(workspaceName, pluginName, packageManager, parsedArgs);
|
argv: yargs.Arguments<CreateNxPluginArguments>
|
||||||
return initializeGitRepo(workspaceName).then(() => {
|
): Promise<void> {
|
||||||
showNxWarning(workspaceName);
|
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;
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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<string>, 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<void>((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.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -29,11 +29,10 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://nx.dev",
|
"homepage": "https://nx.dev",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nrwl/devkit": "file:../devkit",
|
"create-nx-workspace": "file:../create-nx-workspace",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
"enquirer": "~2.3.6",
|
"enquirer": "~2.3.6",
|
||||||
"nx": "file:../nx",
|
"yargs": "^17.6.2"
|
||||||
"tmp": "~0.2.1",
|
|
||||||
"yargs-parser": "21.1.1"
|
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@ -2,20 +2,11 @@ import * as enquirer from 'enquirer';
|
|||||||
import * as yargs from 'yargs';
|
import * as yargs from 'yargs';
|
||||||
import * as chalk from 'chalk';
|
import * as chalk from 'chalk';
|
||||||
|
|
||||||
import { ciList } from '../src/utils/ci/ci-list';
|
|
||||||
import { CreateWorkspaceOptions } from '../src/create-workspace-options';
|
import { CreateWorkspaceOptions } from '../src/create-workspace-options';
|
||||||
import { createWorkspace } from '../src/create-workspace';
|
import { createWorkspace } from '../src/create-workspace';
|
||||||
import { isKnownPreset, Preset } from '../src/utils/preset/preset';
|
import { isKnownPreset, Preset } from '../src/utils/preset/preset';
|
||||||
import { presetOptions } from '../src/utils/preset/preset-options';
|
import { presetOptions } from '../src/utils/preset/preset-options';
|
||||||
import { messages } from '../src/utils/nx/ab-testing';
|
|
||||||
import { output } from '../src/utils/output';
|
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 { nxVersion } from '../src/utils/nx/nx-version';
|
||||||
import { pointToTutorialAndCourse } from '../src/utils/preset/point-to-tutorial-and-course';
|
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 { getThirdPartyPreset } from '../src/utils/preset/get-third-party-preset';
|
||||||
import { Framework, frameworkList } from './types/framework-list';
|
import { Framework, frameworkList } from './types/framework-list';
|
||||||
import { Bundler, bundlerList } from './types/bundler-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 {
|
interface Arguments extends CreateWorkspaceOptions {
|
||||||
preset: string;
|
preset: string;
|
||||||
@ -46,103 +51,64 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
|
|||||||
'$0 [name] [options]',
|
'$0 [name] [options]',
|
||||||
'Create a new Nx workspace',
|
'Create a new Nx workspace',
|
||||||
(yargs) =>
|
(yargs) =>
|
||||||
yargs
|
withOptions(
|
||||||
.option('name', {
|
yargs
|
||||||
describe: chalk.dim`Workspace name (e.g. org name)`,
|
.option('name', {
|
||||||
type: 'string',
|
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(
|
.option('preset', {
|
||||||
Preset
|
describe: chalk.dim`Customizes the initial content of your workspace. Default presets include: [${Object.values(
|
||||||
)
|
Preset
|
||||||
.map((p) => `"${p}"`)
|
)
|
||||||
.join(
|
.map((p) => `"${p}"`)
|
||||||
', '
|
.join(
|
||||||
)}]. To build your own see https://nx.dev/packages/nx-plugin#preset`,
|
', '
|
||||||
type: 'string',
|
)}]. 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`,
|
.option('appName', {
|
||||||
type: 'string',
|
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`,
|
.option('interactive', {
|
||||||
type: 'boolean',
|
describe: chalk.dim`Enable interactive mode with presets`,
|
||||||
default: true,
|
type: 'boolean',
|
||||||
})
|
default: true,
|
||||||
.option('style', {
|
})
|
||||||
describe: chalk.dim`Style option to be used when a preset with pregenerated app is selected`,
|
.option('style', {
|
||||||
type: 'string',
|
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`,
|
.option('standaloneApi', {
|
||||||
type: 'boolean',
|
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`,
|
.option('routing', {
|
||||||
type: 'boolean',
|
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`,
|
.option('bundler', {
|
||||||
choices: bundlerList,
|
describe: chalk.dim`Bundler to be used to build the application`,
|
||||||
type: 'string',
|
choices: bundlerList,
|
||||||
})
|
type: 'string',
|
||||||
.option('framework', {
|
})
|
||||||
describe: chalk.dim`Framework option to be used when the node-server preset is selected`,
|
.option('framework', {
|
||||||
choices: frameworkList,
|
describe: chalk.dim`Framework option to be used when the node-server preset is selected`,
|
||||||
type: 'string',
|
choices: frameworkList,
|
||||||
})
|
type: 'string',
|
||||||
.option('docker', {
|
})
|
||||||
describe: chalk.dim`Generate a Dockerfile with your node-server`,
|
.option('docker', {
|
||||||
type: 'boolean',
|
describe: chalk.dim`Generate a Dockerfile with your node-server`,
|
||||||
})
|
type: 'boolean',
|
||||||
.option('nxCloud', {
|
}),
|
||||||
describe: chalk.dim(messages.getPromptMessage('nxCloudCreation')),
|
withNxCloud,
|
||||||
type: 'boolean',
|
withCI,
|
||||||
})
|
withAllPrompts,
|
||||||
.option('ci', {
|
withPackageManager,
|
||||||
describe: chalk.dim`Generate a CI workflow file`,
|
withGitOptions
|
||||||
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',
|
|
||||||
}),
|
|
||||||
async (argv: yargs.ArgumentsCamelCase<Arguments>) => {
|
async (argv: yargs.ArgumentsCamelCase<Arguments>) => {
|
||||||
await main(argv).catch((error) => {
|
await main(argv).catch((error) => {
|
||||||
const { version } = require('../package.json');
|
const { version } = require('../package.json');
|
||||||
@ -404,77 +370,6 @@ async function determineMonorepoStyle(): Promise<string> {
|
|||||||
return a.MonorepoStyle;
|
return a.MonorepoStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function determinePackageManager(
|
|
||||||
parsedArgs: yargs.Arguments<Arguments>
|
|
||||||
): Promise<PackageManager> {
|
|
||||||
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<Arguments>
|
|
||||||
): Promise<string> {
|
|
||||||
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<Preset> {
|
async function determinePreset(parsedArgs: any): Promise<Preset> {
|
||||||
if (parsedArgs.preset) {
|
if (parsedArgs.preset) {
|
||||||
if (Object.values(Preset).indexOf(parsedArgs.preset) === -1) {
|
if (Object.values(Preset).indexOf(parsedArgs.preset) === -1) {
|
||||||
@ -832,79 +727,3 @@ async function determineBundler(
|
|||||||
|
|
||||||
return Promise.resolve(parsedArgs.bundler);
|
return Promise.resolve(parsedArgs.bundler);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function determineNxCloud(
|
|
||||||
parsedArgs: yargs.Arguments<Arguments>
|
|
||||||
): Promise<boolean> {
|
|
||||||
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<Arguments>,
|
|
||||||
nxCloud: boolean
|
|
||||||
): Promise<string> {
|
|
||||||
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 '';
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export * from './src/create-workspace';
|
export * from './src/create-workspace';
|
||||||
|
export type { CreateWorkspaceOptions } from './src/create-workspace-options';
|
||||||
|
|||||||
@ -36,11 +36,12 @@ export async function createWorkspace<T extends CreateWorkspaceOptions>(
|
|||||||
|
|
||||||
const tmpDir = await createSandbox(packageManager);
|
const tmpDir = await createSandbox(packageManager);
|
||||||
|
|
||||||
|
// nx new requires preset currently. We should probably make it optional.
|
||||||
const directory = await createEmptyWorkspace<T>(
|
const directory = await createEmptyWorkspace<T>(
|
||||||
tmpDir,
|
tmpDir,
|
||||||
name,
|
name,
|
||||||
packageManager,
|
packageManager,
|
||||||
options
|
{ ...options, preset }
|
||||||
);
|
);
|
||||||
|
|
||||||
// If the preset is a third-party preset, we need to call createPreset to install it
|
// If the preset is a third-party preset, we need to call createPreset to install it
|
||||||
|
|||||||
159
packages/create-nx-workspace/src/internal-utils/prompts.ts
Normal file
159
packages/create-nx-workspace/src/internal-utils/prompts.ts
Normal file
@ -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<boolean> {
|
||||||
|
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<string> {
|
||||||
|
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<string> {
|
||||||
|
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<PackageManager> {
|
||||||
|
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());
|
||||||
|
}
|
||||||
@ -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<T = unknown>(argv: yargs.Argv<T>) {
|
||||||
|
const result = argv.option('nxCloud', {
|
||||||
|
describe: chalk.dim(messages.getPromptMessage('nxCloudCreation')),
|
||||||
|
type: 'boolean',
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withCI<T = unknown>(argv: yargs.Argv<T>) {
|
||||||
|
return argv.option('ci', {
|
||||||
|
describe: chalk.dim`Generate a CI workflow file`,
|
||||||
|
choices: ciList,
|
||||||
|
defaultDescription: '',
|
||||||
|
type: 'string',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withAllPrompts<T = unknown>(argv: yargs.Argv<T>) {
|
||||||
|
return argv.option('allPrompts', {
|
||||||
|
alias: 'a',
|
||||||
|
describe: chalk.dim`Show all prompts`,
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withPackageManager<T = unknown>(argv: yargs.Argv<T>) {
|
||||||
|
return argv.option('packageManager', {
|
||||||
|
alias: 'pm',
|
||||||
|
describe: chalk.dim`Package manager to use`,
|
||||||
|
choices: [...packageManagerList].sort(),
|
||||||
|
defaultDescription: 'npm',
|
||||||
|
type: 'string',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withGitOptions<T = unknown>(argv: yargs.Argv<T>) {
|
||||||
|
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<T>(
|
||||||
|
argv: yargs.Argv<T>,
|
||||||
|
...options: ((argv: yargs.Argv<T>) => yargs.Argv<T>)[]
|
||||||
|
): 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);
|
||||||
|
}
|
||||||
@ -141,19 +141,21 @@ function normalizeOptions(
|
|||||||
options: Options[0]
|
options: Options[0]
|
||||||
): Options[0] {
|
): Options[0] {
|
||||||
const base = { ...DEFAULT_OPTIONS, ...options };
|
const base = { ...DEFAULT_OPTIONS, ...options };
|
||||||
|
const pathPrefix =
|
||||||
|
sourceProject.data.root !== '.' ? `${sourceProject.data.root}/` : '';
|
||||||
return {
|
return {
|
||||||
...base,
|
...base,
|
||||||
executorsJson: base.executorsJson
|
executorsJson: base.executorsJson
|
||||||
? `${sourceProject.data.root}/${base.executorsJson}`
|
? `${pathPrefix}${base.executorsJson}`
|
||||||
: undefined,
|
: undefined,
|
||||||
generatorsJson: base.generatorsJson
|
generatorsJson: base.generatorsJson
|
||||||
? `${sourceProject.data.root}/${base.generatorsJson}`
|
? `${pathPrefix}${base.generatorsJson}`
|
||||||
: undefined,
|
: undefined,
|
||||||
migrationsJson: base.migrationsJson
|
migrationsJson: base.migrationsJson
|
||||||
? `${sourceProject.data.root}/${base.migrationsJson}`
|
? `${pathPrefix}${base.migrationsJson}`
|
||||||
: undefined,
|
: undefined,
|
||||||
packageJson: base.packageJson
|
packageJson: base.packageJson
|
||||||
? `${sourceProject.data.root}/${base.packageJson}`
|
? `${pathPrefix}${base.packageJson}`
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "<%= importPath %>",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"type": "commonjs"
|
|
||||||
}
|
|
||||||
@ -35,6 +35,7 @@ import {
|
|||||||
typesNodeVersion,
|
typesNodeVersion,
|
||||||
} from '../../utils/versions';
|
} from '../../utils/versions';
|
||||||
import jsInitGenerator from '../init/init';
|
import jsInitGenerator from '../init/init';
|
||||||
|
import { PackageJson } from 'nx/src/utils/package-json';
|
||||||
|
|
||||||
export async function libraryGenerator(
|
export async function libraryGenerator(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
@ -44,7 +45,9 @@ export async function libraryGenerator(
|
|||||||
schema.directory
|
schema.directory
|
||||||
);
|
);
|
||||||
schema.directory = projectDirectory;
|
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'));
|
return projectGenerator(tree, schema, libsDir, join(__dirname, './files'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,6 +238,7 @@ export async function addLint(
|
|||||||
`${options.projectRoot}/**/*.${options.js ? 'js' : 'ts'}`,
|
`${options.projectRoot}/**/*.${options.js ? 'js' : 'ts'}`,
|
||||||
],
|
],
|
||||||
setParserOptionsProject: options.setParserOptionsProject,
|
setParserOptionsProject: options.setParserOptionsProject,
|
||||||
|
rootProject: options.rootProject,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,7 +312,25 @@ function createFiles(tree: Tree, options: NormalizedSchema, filesDir: string) {
|
|||||||
toJS(tree);
|
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<PackageJson>(tree, packageJsonPath, {
|
||||||
|
name: options.importPath,
|
||||||
|
version: '0.0.1',
|
||||||
|
type: 'commonjs',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (options.config === 'npm-scripts') {
|
if (options.config === 'npm-scripts') {
|
||||||
updateJson(tree, packageJsonPath, (json) => {
|
updateJson(tree, packageJsonPath, (json) => {
|
||||||
json.scripts = {
|
json.scripts = {
|
||||||
@ -317,11 +339,14 @@ function createFiles(tree: Tree, options: NormalizedSchema, filesDir: string) {
|
|||||||
};
|
};
|
||||||
return json;
|
return json;
|
||||||
});
|
});
|
||||||
} else if (!options.bundler || options.bundler === 'none') {
|
} else if (
|
||||||
|
(!options.bundler || options.bundler === 'none') &&
|
||||||
|
!(options.projectRoot === '.')
|
||||||
|
) {
|
||||||
tree.delete(packageJsonPath);
|
tree.delete(packageJsonPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.minimal) {
|
if (options.minimal && !(options.projectRoot === '.')) {
|
||||||
tree.delete(join(options.projectRoot, 'README.md'));
|
tree.delete(join(options.projectRoot, 'README.md'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,6 +462,8 @@ function normalizeOptions(
|
|||||||
const name = names(options.name).fileName;
|
const name = names(options.name).fileName;
|
||||||
const projectDirectory = options.directory
|
const projectDirectory = options.directory
|
||||||
? `${names(options.directory).fileName}/${name}`
|
? `${names(options.directory).fileName}/${name}`
|
||||||
|
: options.rootProject
|
||||||
|
? '.'
|
||||||
: name;
|
: name;
|
||||||
|
|
||||||
if (!options.unitTestRunner && options.bundler === 'vite') {
|
if (!options.unitTestRunner && options.bundler === 'vite') {
|
||||||
@ -449,7 +476,9 @@ function normalizeOptions(
|
|||||||
options.linter = Linter.EsLint;
|
options.linter = Linter.EsLint;
|
||||||
}
|
}
|
||||||
|
|
||||||
const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');
|
const projectName = options.rootProject
|
||||||
|
? name
|
||||||
|
: projectDirectory.replace(new RegExp('/', 'g'), '-');
|
||||||
const fileName = getCaseAwareFileName({
|
const fileName = getCaseAwareFileName({
|
||||||
fileName: options.simpleModuleName ? name : projectName,
|
fileName: options.simpleModuleName ? name : projectName,
|
||||||
pascalCaseFiles: options.pascalCaseFiles,
|
pascalCaseFiles: options.pascalCaseFiles,
|
||||||
|
|||||||
1
packages/js/src/utils/schema.d.ts
vendored
1
packages/js/src/utils/schema.d.ts
vendored
@ -32,6 +32,7 @@ export interface LibraryGeneratorSchema {
|
|||||||
bundler?: Bundler;
|
bundler?: Bundler;
|
||||||
skipTypeCheck?: boolean;
|
skipTypeCheck?: boolean;
|
||||||
minimal?: boolean;
|
minimal?: boolean;
|
||||||
|
rootProject?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExecutorOptions {
|
export interface ExecutorOptions {
|
||||||
|
|||||||
@ -33,6 +33,13 @@
|
|||||||
"factory": "./src/generators/lint-checks/generator",
|
"factory": "./src/generators/lint-checks/generator",
|
||||||
"schema": "./src/generators/lint-checks/schema.json",
|
"schema": "./src/generators/lint-checks/schema.json",
|
||||||
"description": "Adds linting configuration to validate common json files for nx plugins."
|
"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": {
|
"schematics": {
|
||||||
|
|||||||
@ -35,10 +35,13 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
|
|||||||
const { npmScope, appsDir: defaultAppsDir } = getWorkspaceLayout(host);
|
const { npmScope, appsDir: defaultAppsDir } = getWorkspaceLayout(host);
|
||||||
const appsDir = layoutDirectory ?? defaultAppsDir;
|
const appsDir = layoutDirectory ?? defaultAppsDir;
|
||||||
|
|
||||||
const projectName = `${options.pluginName}-e2e`;
|
const projectName = options.rootProject ? 'e2e' : `${options.pluginName}-e2e`;
|
||||||
const projectRoot = projectDirectory
|
const projectRoot =
|
||||||
? joinPathFragments(appsDir, `${projectDirectory}-e2e`)
|
projectDirectory && !options.rootProject
|
||||||
: joinPathFragments(appsDir, projectName);
|
? joinPathFragments(appsDir, `${projectDirectory}-e2e`)
|
||||||
|
: options.rootProject
|
||||||
|
? projectName
|
||||||
|
: joinPathFragments(appsDir, projectName);
|
||||||
const pluginPropertyName = names(options.pluginName).propertyName;
|
const pluginPropertyName = names(options.pluginName).propertyName;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -9,4 +9,5 @@ export interface Schema {
|
|||||||
minimal?: boolean;
|
minimal?: boolean;
|
||||||
linter?: Linter;
|
linter?: Linter;
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
|
rootProject?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,7 +107,7 @@ describe('NxPlugin Executor Generator', () => {
|
|||||||
|
|
||||||
expect(() => tree.exists(`${libConfig.root}/executors.json`)).not.toThrow();
|
expect(() => tree.exists(`${libConfig.root}/executors.json`)).not.toThrow();
|
||||||
expect(readJson(tree, `${libConfig.root}/package.json`).executors).toBe(
|
expect(readJson(tree, `${libConfig.root}/package.json`).executors).toBe(
|
||||||
'executors.json'
|
'./executors.json'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -75,7 +75,7 @@ function createExecutorsJson(host: Tree, options: NormalizedSchema) {
|
|||||||
host,
|
host,
|
||||||
joinPathFragments(options.projectRoot, 'package.json'),
|
joinPathFragments(options.projectRoot, 'package.json'),
|
||||||
(json) => {
|
(json) => {
|
||||||
json.executors ??= 'executors.json';
|
json.executors ??= './executors.json';
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@ -111,7 +111,7 @@ describe('NxPlugin Generator Generator', () => {
|
|||||||
tree.exists(`${libConfig.root}/generators.json`)
|
tree.exists(`${libConfig.root}/generators.json`)
|
||||||
).not.toThrow();
|
).not.toThrow();
|
||||||
expect(readJson(tree, `${libConfig.root}/package.json`).generators).toBe(
|
expect(readJson(tree, `${libConfig.root}/package.json`).generators).toBe(
|
||||||
'generators.json'
|
'./generators.json'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -91,7 +91,7 @@ function createGeneratorsJson(host: Tree, options: NormalizedSchema) {
|
|||||||
host,
|
host,
|
||||||
joinPathFragments(options.projectRoot, 'package.json'),
|
joinPathFragments(options.projectRoot, 'package.json'),
|
||||||
(json) => {
|
(json) => {
|
||||||
json.generators ??= 'generators.json';
|
json.generators ??= './generators.json';
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@ -15,7 +15,12 @@ import type { Schema } from './schema';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { addMigrationJsonChecks } from '../lint-checks/generator';
|
import { addMigrationJsonChecks } from '../lint-checks/generator';
|
||||||
import type { Linter as EsLint } from 'eslint';
|
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 {
|
interface NormalizedSchema extends Schema {
|
||||||
projectRoot: string;
|
projectRoot: string;
|
||||||
projectSourceRoot: string;
|
projectSourceRoot: string;
|
||||||
@ -98,19 +103,25 @@ function updateMigrationsJson(host: Tree, options: NormalizedSchema) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updatePackageJson(host: Tree, options: NormalizedSchema) {
|
function updatePackageJson(host: Tree, options: NormalizedSchema) {
|
||||||
updateJson(host, path.join(options.projectRoot, 'package.json'), (json) => {
|
updateJson<PackageJson>(
|
||||||
if (!json['nx-migrations'] || !json['nx-migrations'].migrations) {
|
host,
|
||||||
if (json['nx-migrations']) {
|
path.join(options.projectRoot, 'package.json'),
|
||||||
json['nx-migrations'].migrations = './migrations.json';
|
(json) => {
|
||||||
} else {
|
const migrationKey = json['ng-update'] ? 'ng-update' : 'nx-migrations';
|
||||||
json['nx-migrations'] = {
|
const preexistingValue = json[migrationKey];
|
||||||
|
if (typeof preexistingValue === 'string') {
|
||||||
|
return json;
|
||||||
|
} else if (!json[migrationKey]) {
|
||||||
|
json[migrationKey] = {
|
||||||
migrations: './migrations.json',
|
migrations: './migrations.json',
|
||||||
};
|
};
|
||||||
|
} else if (preexistingValue.migrations) {
|
||||||
|
preexistingValue.migrations = './migrations.json';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateWorkspaceJson(host: Tree, options: NormalizedSchema) {
|
function updateWorkspaceJson(host: Tree, options: NormalizedSchema) {
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://json-schema.org/schema",
|
|
||||||
"executors": {}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://json-schema.org/schema",
|
|
||||||
"name": "<%= name %>",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"generators": {}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "<%= npmPackageName %>",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"main": "src/index.js",
|
|
||||||
"generators": "./generators.json",
|
|
||||||
"executors": "./executors.json"
|
|
||||||
}
|
|
||||||
@ -138,11 +138,7 @@ describe('NxPlugin Plugin Generator', () => {
|
|||||||
it('should not create generator and executor files for minimal setups', async () => {
|
it('should not create generator and executor files for minimal setups', async () => {
|
||||||
await pluginGenerator(tree, getSchema({ name: 'myPlugin', minimal: true }));
|
await pluginGenerator(tree, getSchema({ name: 'myPlugin', minimal: true }));
|
||||||
|
|
||||||
[
|
expect(tree.exists('libs/my-plugin/project.json')).toBeTruthy();
|
||||||
'libs/my-plugin/project.json',
|
|
||||||
'libs/my-plugin/generators.json',
|
|
||||||
'libs/my-plugin/executors.json',
|
|
||||||
].forEach((path) => expect(tree.exists(path)).toBeTruthy());
|
|
||||||
|
|
||||||
[
|
[
|
||||||
'libs/my-plugin/src/generators/my-plugin/schema.d.ts',
|
'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.json',
|
||||||
'libs/my-plugin/src/executors/build/schema.d.ts',
|
'libs/my-plugin/src/executors/build/schema.d.ts',
|
||||||
].forEach((path) => expect(tree.exists(path)).toBeFalsy());
|
].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', () => {
|
describe('--unitTestRunner', () => {
|
||||||
|
|||||||
@ -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);
|
const project = readProjectConfiguration(host, options.name);
|
||||||
|
|
||||||
if (project.targets.build) {
|
if (project.targets.build) {
|
||||||
@ -100,13 +100,14 @@ export async function pluginGenerator(host: Tree, schema: Schema) {
|
|||||||
|
|
||||||
addDependenciesToPackageJson(
|
addDependenciesToPackageJson(
|
||||||
host,
|
host,
|
||||||
{},
|
|
||||||
{
|
{
|
||||||
'@nrwl/devkit': nxVersion,
|
'@nrwl/devkit': nxVersion,
|
||||||
|
tslib: tsLibVersion,
|
||||||
|
},
|
||||||
|
{
|
||||||
'@nrwl/jest': nxVersion,
|
'@nrwl/jest': nxVersion,
|
||||||
'@nrwl/js': nxVersion,
|
'@nrwl/js': nxVersion,
|
||||||
'@swc-node/register': swcNodeVersion,
|
'@swc-node/register': swcNodeVersion,
|
||||||
tslib: tsLibVersion,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -115,7 +116,7 @@ export async function pluginGenerator(host: Tree, schema: Schema) {
|
|||||||
addSwcDependencies(host);
|
addSwcDependencies(host);
|
||||||
|
|
||||||
await addFiles(host, options);
|
await addFiles(host, options);
|
||||||
updateWorkspaceJson(host, options);
|
updatePluginConfig(host, options);
|
||||||
|
|
||||||
if (options.e2eTestRunner !== 'none') {
|
if (options.e2eTestRunner !== 'none') {
|
||||||
await e2eProjectGenerator(host, {
|
await e2eProjectGenerator(host, {
|
||||||
@ -125,6 +126,7 @@ export async function pluginGenerator(host: Tree, schema: Schema) {
|
|||||||
npmPackageName: options.npmPackageName,
|
npmPackageName: options.npmPackageName,
|
||||||
minimal: options.minimal ?? false,
|
minimal: options.minimal ?? false,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
|
rootProject: options.rootProject,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,4 +14,5 @@ export interface Schema {
|
|||||||
setParserOptionsProject?: boolean;
|
setParserOptionsProject?: boolean;
|
||||||
compiler: 'swc' | 'tsc';
|
compiler: 'swc' | 'tsc';
|
||||||
minimal?: boolean;
|
minimal?: boolean;
|
||||||
|
rootProject?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,9 +31,13 @@ export function normalizeOptions(
|
|||||||
const name = names(options.name).fileName;
|
const name = names(options.name).fileName;
|
||||||
const fullProjectDirectory = projectDirectory
|
const fullProjectDirectory = projectDirectory
|
||||||
? `${names(projectDirectory).fileName}/${name}`
|
? `${names(projectDirectory).fileName}/${name}`
|
||||||
|
: options.rootProject
|
||||||
|
? '.'
|
||||||
: name;
|
: name;
|
||||||
|
|
||||||
const projectName = fullProjectDirectory.replace(new RegExp('/', 'g'), '-');
|
const projectName = options.rootProject
|
||||||
|
? name
|
||||||
|
: fullProjectDirectory.replace(new RegExp('/', 'g'), '-');
|
||||||
const fileName = projectName;
|
const fileName = projectName;
|
||||||
const projectRoot = joinPathFragments(libsDir, fullProjectDirectory);
|
const projectRoot = joinPathFragments(libsDir, fullProjectDirectory);
|
||||||
|
|
||||||
|
|||||||
30
packages/nx-plugin/src/generators/preset/generator.spec.ts
Normal file
30
packages/nx-plugin/src/generators/preset/generator.spec.ts
Normal file
@ -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<PackageJson>(tree, 'package.json');
|
||||||
|
expect(packageJson.generators).toEqual('./generators.json');
|
||||||
|
expect(packageJson.executors).toEqual('./executors.json');
|
||||||
|
expect(readNxJson(tree).npmScope).not.toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
45
packages/nx-plugin/src/generators/preset/generator.ts
Normal file
45
packages/nx-plugin/src/generators/preset/generator.ts
Normal file
@ -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<PackageJson>(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;
|
||||||
|
});
|
||||||
|
}
|
||||||
3
packages/nx-plugin/src/generators/preset/schema.d.ts
vendored
Normal file
3
packages/nx-plugin/src/generators/preset/schema.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export interface PresetGeneratorSchema {
|
||||||
|
pluginName: string;
|
||||||
|
}
|
||||||
16
packages/nx-plugin/src/generators/preset/schema.json
Normal file
16
packages/nx-plugin/src/generators/preset/schema.json
Normal file
@ -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"]
|
||||||
|
}
|
||||||
@ -13,31 +13,34 @@ function removeSpecialFlags(generatorOptions: { [p: string]: any }): void {
|
|||||||
export async function newWorkspace(cwd: string, args: { [k: string]: any }) {
|
export async function newWorkspace(cwd: string, args: { [k: string]: any }) {
|
||||||
const ws = new Workspaces(null);
|
const ws = new Workspaces(null);
|
||||||
|
|
||||||
return handleErrors(false, async () => {
|
return handleErrors(
|
||||||
const isInteractive = args.interactive;
|
process.env.NX_VERBOSE_LOGGING === 'true' || args.verbose,
|
||||||
const { normalizedGeneratorName, schema, implementationFactory } =
|
async () => {
|
||||||
ws.readGenerator('@nrwl/workspace/generators.json', 'new');
|
const isInteractive = args.interactive;
|
||||||
removeSpecialFlags(args);
|
const { normalizedGeneratorName, schema, implementationFactory } =
|
||||||
const combinedOpts = await combineOptionsForGenerator(
|
ws.readGenerator('@nrwl/workspace/generators.json', 'new');
|
||||||
args,
|
removeSpecialFlags(args);
|
||||||
'@nrwl/workspace/generators.json',
|
const combinedOpts = await combineOptionsForGenerator(
|
||||||
normalizedGeneratorName,
|
args,
|
||||||
null,
|
'@nrwl/workspace/generators.json',
|
||||||
null,
|
normalizedGeneratorName,
|
||||||
schema,
|
null,
|
||||||
isInteractive,
|
null,
|
||||||
null,
|
schema,
|
||||||
null,
|
isInteractive,
|
||||||
false
|
null,
|
||||||
);
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
const host = new FsTree(cwd, false);
|
const host = new FsTree(cwd, false);
|
||||||
const implementation = implementationFactory();
|
const implementation = implementationFactory();
|
||||||
const task = await implementation(host, combinedOpts);
|
const task = await implementation(host, combinedOpts);
|
||||||
flushChanges(cwd, host.listChanges());
|
flushChanges(cwd, host.listChanges());
|
||||||
host.lock();
|
host.lock();
|
||||||
if (task) {
|
if (task) {
|
||||||
await task();
|
await task();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,10 +77,16 @@ export function createPackageJson(
|
|||||||
);
|
);
|
||||||
// for standalone projects we don't want to include all the root dependencies
|
// for standalone projects we don't want to include all the root dependencies
|
||||||
if (graph.nodes[projectName].data.root === '.') {
|
if (graph.nodes[projectName].data.root === '.') {
|
||||||
packageJson = {
|
// TODO: We should probably think more on this - Nx can't always
|
||||||
name: packageJson.name,
|
// detect all external dependencies, and there's not a way currently
|
||||||
version: packageJson.version,
|
// 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) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,6 +107,8 @@
|
|||||||
"@nrwl/workspace": ["packages/workspace"],
|
"@nrwl/workspace": ["packages/workspace"],
|
||||||
"@nrwl/workspace/*": ["packages/workspace/*"],
|
"@nrwl/workspace/*": ["packages/workspace/*"],
|
||||||
"@nrwl/workspace/testing": ["packages/workspace/testing"],
|
"@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": ["packages/nx"],
|
||||||
"nx-dev/ui-primitives": ["nx-dev/ui-primitives/src/index.ts"],
|
"nx-dev/ui-primitives": ["nx-dev/ui-primitives/src/index.ts"],
|
||||||
"nx/*": ["packages/nx/*"]
|
"nx/*": ["packages/nx/*"]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user