feat(core): update create-nx-plugin to generate cli library (#15994)
This commit is contained in:
parent
16e4061b5d
commit
840048480f
@ -5579,6 +5579,14 @@
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "create-package",
|
||||
"path": "/packages/nx-plugin/generators/create-package",
|
||||
"name": "create-package",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "e2e-project",
|
||||
"path": "/packages/nx-plugin/generators/e2e-project",
|
||||
|
||||
@ -1810,6 +1810,15 @@
|
||||
"path": "/packages/nx-plugin/generators/plugin",
|
||||
"type": "generator"
|
||||
},
|
||||
"/packages/nx-plugin/generators/create-package": {
|
||||
"description": "Create a package which can be used by npx to create a new workspace",
|
||||
"file": "generated/packages/nx-plugin/generators/create-package.json",
|
||||
"hidden": false,
|
||||
"name": "create-package",
|
||||
"originalFilePath": "/packages/nx-plugin/src/generators/create-package/schema.json",
|
||||
"path": "/packages/nx-plugin/generators/create-package",
|
||||
"type": "generator"
|
||||
},
|
||||
"/packages/nx-plugin/generators/e2e-project": {
|
||||
"description": "Create a E2E application for a Nx Plugin.",
|
||||
"file": "generated/packages/nx-plugin/generators/e2e-project.json",
|
||||
|
||||
@ -1788,6 +1788,15 @@
|
||||
"path": "nx-plugin/generators/plugin",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Create a package which can be used by npx to create a new workspace",
|
||||
"file": "generated/packages/nx-plugin/generators/create-package.json",
|
||||
"hidden": false,
|
||||
"name": "create-package",
|
||||
"originalFilePath": "/packages/nx-plugin/src/generators/create-package/schema.json",
|
||||
"path": "nx-plugin/generators/create-package",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Create a E2E application for a Nx Plugin.",
|
||||
"file": "generated/packages/nx-plugin/generators/e2e-project.json",
|
||||
|
||||
@ -117,7 +117,8 @@
|
||||
"runInBand": {
|
||||
"alias": "i",
|
||||
"description": "Run all tests serially in the current process (rather than creating a worker pool of child processes that run tests). This is sometimes useful for debugging, but such use cases are pretty rare. Useful for CI. (https://jestjs.io/docs/cli#--runinband)",
|
||||
"type": "boolean"
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"showConfig": {
|
||||
"description": "Print your Jest config and then exits. (https://jestjs.io/docs/en/cli#--showconfig)",
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "create-package",
|
||||
"factory": "./src/generators/create-package/create-package",
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"cli": "nx",
|
||||
"$id": "NxPluginCreatePackage",
|
||||
"title": "Create a framework package",
|
||||
"description": "Create a framework package that uses Nx CLI.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The package name of cli, e.g. `create-framework-package`. Note this must be a valid NPM name to be published.",
|
||||
"$default": { "$source": "argv", "index": 0 },
|
||||
"x-priority": "important"
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The name of the generator project.",
|
||||
"alias": "p",
|
||||
"$default": { "$source": "projectName" },
|
||||
"x-prompt": "What is the name of the project for the generator?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the app is placed."
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
"description": "Add tags to the library (used for linting).",
|
||||
"alias": "t"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"compiler": {
|
||||
"type": "string",
|
||||
"enum": ["tsc", "swc"],
|
||||
"default": "tsc",
|
||||
"description": "The compiler used by the build and test targets."
|
||||
},
|
||||
"e2eTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for end to end (E2E) tests.",
|
||||
"default": "jest"
|
||||
}
|
||||
},
|
||||
"required": ["name", "project"],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Create a package which can be used by npx to create a new workspace",
|
||||
"implementation": "/packages/nx-plugin/src/generators/create-package/create-package.ts",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/nx-plugin/src/generators/create-package/schema.json",
|
||||
"type": "generator"
|
||||
}
|
||||
@ -49,6 +49,12 @@
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not add an eslint configuration for plugin json files."
|
||||
},
|
||||
"skipFormat": {
|
||||
"type": "boolean",
|
||||
"description": "Skip formatting files.",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
}
|
||||
},
|
||||
"required": ["project", "name"],
|
||||
|
||||
@ -13,6 +13,10 @@
|
||||
"type": "string",
|
||||
"description": "Plugin name",
|
||||
"aliases": ["name"]
|
||||
},
|
||||
"createPackageName": {
|
||||
"type": "string",
|
||||
"description": "Name of package which creates a workspace"
|
||||
}
|
||||
},
|
||||
"required": ["pluginName"],
|
||||
|
||||
@ -92,6 +92,7 @@
|
||||
"enum": ["cypress", "jest", "detox", "none"]
|
||||
}
|
||||
},
|
||||
"required": ["preset", "name"],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Create application in an empty workspace.",
|
||||
|
||||
@ -5,7 +5,6 @@ import {
|
||||
createFile,
|
||||
expectTestsPass,
|
||||
getPackageManagerCommand,
|
||||
killPorts,
|
||||
newProject,
|
||||
readJson,
|
||||
readProjectConfig,
|
||||
@ -410,4 +409,31 @@ describe('Nx Plugin', () => {
|
||||
expect(pluginProject.tags).toEqual(['e2etag', 'e2ePackage']);
|
||||
}, 90000);
|
||||
});
|
||||
|
||||
it('should be able to generate a create-package plugin ', async () => {
|
||||
const plugin = uniq('plugin');
|
||||
const createAppName = `create-${plugin}-app`;
|
||||
runCLI(`generate @nrwl/nx-plugin:plugin ${plugin}`);
|
||||
runCLI(
|
||||
`generate @nrwl/nx-plugin:create-package ${createAppName} --project=${plugin}`
|
||||
);
|
||||
|
||||
const buildResults = runCLI(`build ${createAppName}`);
|
||||
expect(buildResults).toContain('Done compiling TypeScript files');
|
||||
|
||||
checkFilesExist(
|
||||
`libs/${plugin}/src/generators/preset`,
|
||||
`libs/${createAppName}`,
|
||||
`dist/libs/${createAppName}/bin/index.js`
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error when run create-package for an invalid plugin ', async () => {
|
||||
const plugin = uniq('plugin');
|
||||
expect(() =>
|
||||
runCLI(
|
||||
`generate @nrwl/nx-plugin:create-package ${plugin} --project=invalid-plugin`
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@ -21,6 +21,7 @@ describe('create-nx-plugin', () => {
|
||||
|
||||
runCreatePlugin(pluginName, {
|
||||
packageManager,
|
||||
extraArgs: `--createPackageName='false'`,
|
||||
});
|
||||
|
||||
checkFilesExist(
|
||||
@ -31,7 +32,7 @@ describe('create-nx-plugin', () => {
|
||||
|
||||
runCLI(`build ${pluginName}`);
|
||||
|
||||
checkFilesExist(`dist/package.json`);
|
||||
checkFilesExist(`dist/package.json`, `dist/src/index.js`);
|
||||
|
||||
runCLI(
|
||||
`generate @nrwl/nx-plugin:generator ${generatorName} --project=${pluginName}`
|
||||
@ -48,4 +49,22 @@ describe('create-nx-plugin', () => {
|
||||
`dist/executors.json`
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to create a repo with create workspace cli', () => {
|
||||
const pluginName = uniq('plugin');
|
||||
|
||||
runCreatePlugin(pluginName, {
|
||||
packageManager,
|
||||
extraArgs: `--createPackageName=create-${pluginName}-package`,
|
||||
});
|
||||
|
||||
runCLI(`build ${pluginName}`);
|
||||
checkFilesExist(`dist/package.json`, `dist/generators.json`);
|
||||
|
||||
runCLI(`build create-${pluginName}-package`);
|
||||
checkFilesExist(`dist/create-${pluginName}-package/bin/index.js`);
|
||||
|
||||
expect(() => runCLI(`e2e e2e`)).not.toThrow();
|
||||
expect(() => runCLI(`e2e create-${pluginName}-package-e2e`)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@ -43,34 +43,44 @@ export const yargsDecorator = {
|
||||
|
||||
const nxVersion = require('../package.json').version;
|
||||
|
||||
function determinePluginName(parsedArgs: CreateNxPluginArguments) {
|
||||
async function determinePluginName(
|
||||
parsedArgs: CreateNxPluginArguments
|
||||
): Promise<string> {
|
||||
if (parsedArgs.pluginName) {
|
||||
return Promise.resolve(parsedArgs.pluginName);
|
||||
return parsedArgs.pluginName;
|
||||
}
|
||||
|
||||
return enquirer
|
||||
.prompt([
|
||||
const results = await enquirer.prompt<{ pluginName: string }>([
|
||||
{
|
||||
name: 'pluginName',
|
||||
message: `Plugin name `,
|
||||
type: 'input',
|
||||
validate: (s) => (s.length ? true : 'Name cannot be empty'),
|
||||
validate: (s_1) => (s_1.length ? true : 'Plugin name cannot be empty'),
|
||||
},
|
||||
])
|
||||
.then((a: { pluginName: string }) => {
|
||||
if (!a.pluginName) {
|
||||
output.error({
|
||||
title: 'Invalid name',
|
||||
bodyLines: [`Name cannot be empty`],
|
||||
});
|
||||
process.exit(1);
|
||||
]);
|
||||
return results.pluginName;
|
||||
}
|
||||
|
||||
async function determineCreatePackageName(
|
||||
parsedArgs: CreateNxPluginArguments
|
||||
): Promise<string> {
|
||||
if (parsedArgs.createPackageName) {
|
||||
return parsedArgs.createPackageName;
|
||||
}
|
||||
return a.pluginName;
|
||||
});
|
||||
|
||||
const results = await enquirer.prompt<{ createPackageName: string }>([
|
||||
{
|
||||
name: 'createPackageName',
|
||||
message: `Create a package which can be used by npx to create a new workspace (Leave blank to not create this package)`,
|
||||
type: 'input',
|
||||
},
|
||||
]);
|
||||
return results.createPackageName;
|
||||
}
|
||||
|
||||
interface CreateNxPluginArguments {
|
||||
pluginName: string;
|
||||
createPackageName?: string;
|
||||
packageManager: PackageManager;
|
||||
ci: CI;
|
||||
allPrompts: boolean;
|
||||
@ -89,10 +99,15 @@ export const commandsObject: yargs.Argv<CreateNxPluginArguments> = yargs
|
||||
'Create a new Nx plugin workspace',
|
||||
(yargs) =>
|
||||
withOptions(
|
||||
yargs.positional('pluginName', {
|
||||
yargs
|
||||
.positional('pluginName', {
|
||||
describe: chalk.dim`Plugin name`,
|
||||
type: 'string',
|
||||
alias: ['name'],
|
||||
})
|
||||
.option('createPackageName', {
|
||||
describe: 'Name of the CLI package to create workspace with plugin',
|
||||
type: 'string',
|
||||
}),
|
||||
withNxCloud,
|
||||
withCI,
|
||||
@ -164,19 +179,21 @@ async function normalizeArgsMiddleware(
|
||||
argv: yargs.Arguments<CreateNxPluginArguments>
|
||||
): Promise<void> {
|
||||
try {
|
||||
const name = await determinePluginName(argv);
|
||||
const pluginName = await determinePluginName(argv);
|
||||
const createPackageName = await determineCreatePackageName(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,
|
||||
pluginName,
|
||||
createPackageName,
|
||||
nxCloud,
|
||||
packageManager,
|
||||
defaultBase,
|
||||
ci,
|
||||
});
|
||||
} as Partial<CreateNxPluginArguments & CreateWorkspaceOptions>);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
|
||||
@ -221,6 +221,7 @@ async function normalizeArgsMiddleware(
|
||||
} else if (monorepoStyle === 'node-standalone') {
|
||||
preset = Preset.NodeStandalone;
|
||||
} else {
|
||||
// when choose integrated monorepo, further prompt for preset
|
||||
preset = await determinePreset(argv);
|
||||
}
|
||||
} else if (argv.preset === 'react') {
|
||||
|
||||
@ -27,8 +27,6 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/nrwl/nx/issues"
|
||||
},
|
||||
"main": "./index.js",
|
||||
"typings": "./index.d.ts",
|
||||
"homepage": "https://nx.dev",
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.0",
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"build-base": {
|
||||
"executor": "@nrwl/js:tsc",
|
||||
"options": {
|
||||
"main": "packages/create-nx-workspace/bin/create-nx-workspace.ts",
|
||||
"main": "packages/create-nx-workspace/index.ts",
|
||||
"assets": [
|
||||
{
|
||||
"input": "packages/create-nx-workspace",
|
||||
|
||||
@ -39,7 +39,9 @@ export async function createPreset<T extends CreateWorkspaceOptions>(
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NX_VERBOSE_LOGGING !== 'true') {
|
||||
if (
|
||||
!(process.env.NX_VERBOSE_LOGGING === 'true' || args.includes('--verbose'))
|
||||
) {
|
||||
args = '--quiet ' + args;
|
||||
}
|
||||
const command = `g ${preset}:preset ${args}`;
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { PackageManager } from './utils/package-manager';
|
||||
import { CI } from './utils/ci/ci-list';
|
||||
|
||||
export interface CreateWorkspaceOptions {
|
||||
name: string; // Workspace name (e.g. org name)
|
||||
packageManager: PackageManager; // Package manager to use
|
||||
nxCloud: boolean; // Enable Nx Cloud
|
||||
presetVersion?: string; // Version of the preset to use
|
||||
/**
|
||||
* @description Enable interactive mode with presets
|
||||
* @default true
|
||||
@ -60,7 +60,7 @@ export async function createWorkspace<T extends CreateWorkspaceOptions>(
|
||||
nxCloud && nxCloudInstallRes?.code === 0
|
||||
);
|
||||
}
|
||||
if (!skipGit) {
|
||||
if (!skipGit && commit) {
|
||||
try {
|
||||
await initializeGitRepo(directory, { defaultBase, commit });
|
||||
} catch (e) {
|
||||
|
||||
@ -5,18 +5,19 @@ import { isKnownPreset } from './preset';
|
||||
/**
|
||||
* This function is used to check if a preset is a third party preset.
|
||||
* @param preset
|
||||
* @returns null if the preset is a known Nx preset or preset does not exist, the normalized preset otherwise.
|
||||
* @returns null if the preset is a known Nx preset or preset does not exist, the package name of preset otherwise.
|
||||
*/
|
||||
export async function getThirdPartyPreset(
|
||||
preset?: string
|
||||
): Promise<string | null> {
|
||||
if (preset && !isKnownPreset(preset)) {
|
||||
// extract the package name from the preset
|
||||
const packageName = preset.match(/.+@/)
|
||||
? preset[0] + preset.substring(1).split('@')[0]
|
||||
: preset;
|
||||
const validateResult = validateNpmPackage(packageName);
|
||||
if (validateResult.validForNewPackages) {
|
||||
return Promise.resolve(preset);
|
||||
return Promise.resolve(packageName);
|
||||
} else {
|
||||
//! Error here
|
||||
output.error({
|
||||
|
||||
@ -2,7 +2,7 @@ import { addDependenciesToPackageJson, Tree } from '@nx/devkit';
|
||||
import { tsLibVersion } from '../versions';
|
||||
|
||||
export function addTsLibDependencies(tree: Tree) {
|
||||
addDependenciesToPackageJson(
|
||||
return addDependenciesToPackageJson(
|
||||
tree,
|
||||
{
|
||||
tslib: tsLibVersion,
|
||||
|
||||
@ -8,6 +8,11 @@
|
||||
"schema": "./src/generators/plugin/schema.json",
|
||||
"description": "Create a Nx Plugin."
|
||||
},
|
||||
"create-package": {
|
||||
"factory": "./src/generators/create-package/create-package",
|
||||
"schema": "./src/generators/create-package/schema.json",
|
||||
"description": "Create a package which can be used by npx to create a new workspace"
|
||||
},
|
||||
"e2e-project": {
|
||||
"factory": "./src/generators/e2e-project/e2e",
|
||||
"schema": "./src/generators/e2e-project/schema.json",
|
||||
@ -47,6 +52,11 @@
|
||||
"schema": "./src/generators/plugin/schema.json",
|
||||
"description": "Create a Nx Plugin."
|
||||
},
|
||||
"create-package": {
|
||||
"factory": "./src/generators/create-package/create-package#createPackageSchematic",
|
||||
"schema": "./src/generators/create-package/schema.json",
|
||||
"description": "Create a package which can be used by npx to create a new workspace"
|
||||
},
|
||||
"e2e-project": {
|
||||
"factory": "./src/generators/e2e-project/e2e#e2eProjectSchematic",
|
||||
"schema": "./src/generators/e2e-project/schema.json",
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export * from './src/generators/create-package/create-package';
|
||||
export * from './src/generators/e2e-project/e2e';
|
||||
export * from './src/generators/executor/executor';
|
||||
export * from './src/generators/generator/generator';
|
||||
|
||||
@ -128,7 +128,8 @@
|
||||
"runInBand": {
|
||||
"alias": "i",
|
||||
"description": "Run all tests serially in the current process (rather than creating a worker pool of child processes that run tests). This is sometimes useful for debugging, but such use cases are pretty rare. Useful for CI. (https://jestjs.io/docs/cli#--runinband)",
|
||||
"type": "boolean"
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"showConfig": {
|
||||
"description": "Print your Jest config and then exits. (https://jestjs.io/docs/en/cli#--showconfig)",
|
||||
|
||||
@ -0,0 +1,119 @@
|
||||
import {
|
||||
joinPathFragments,
|
||||
readJson,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/linter';
|
||||
import { PackageJson } from 'nx/src/utils/package-json';
|
||||
import pluginGenerator from '../plugin/plugin';
|
||||
import { createPackageGenerator } from './create-package';
|
||||
import { CreatePackageSchema } from './schema';
|
||||
|
||||
const getSchema: (
|
||||
overrides?: Partial<CreatePackageSchema>
|
||||
) => CreatePackageSchema = (overrides = {}) => ({
|
||||
name: 'create-a-workspace',
|
||||
project: 'my-plugin',
|
||||
compiler: 'tsc',
|
||||
skipTsConfig: false,
|
||||
skipFormat: false,
|
||||
skipLintChecks: false,
|
||||
linter: Linter.EsLint,
|
||||
unitTestRunner: 'jest',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe('NxPlugin Create Package Generator', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
await pluginGenerator(tree, {
|
||||
name: 'my-plugin',
|
||||
compiler: 'tsc',
|
||||
skipTsConfig: false,
|
||||
skipFormat: false,
|
||||
skipLintChecks: false,
|
||||
linter: Linter.EsLint,
|
||||
unitTestRunner: 'jest',
|
||||
});
|
||||
});
|
||||
|
||||
it('should update the project.json file', async () => {
|
||||
await createPackageGenerator(tree, getSchema());
|
||||
const project = readProjectConfiguration(tree, 'create-a-workspace');
|
||||
expect(project.root).toEqual('libs/create-a-workspace');
|
||||
expect(project.sourceRoot).toEqual('libs/create-a-workspace/bin');
|
||||
expect(project.targets.build).toEqual({
|
||||
executor: '@nx/js:tsc',
|
||||
outputs: ['{options.outputPath}'],
|
||||
options: {
|
||||
outputPath: 'dist/libs/create-a-workspace',
|
||||
tsConfig: 'libs/create-a-workspace/tsconfig.lib.json',
|
||||
main: 'libs/create-a-workspace/bin/index.ts',
|
||||
assets: ['libs/create-a-workspace/*.md'],
|
||||
updateBuildableProjectDepsInPackageJson: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should place the create-package plugin in a directory', async () => {
|
||||
await createPackageGenerator(
|
||||
tree,
|
||||
getSchema({
|
||||
directory: 'plugins',
|
||||
} as Partial<CreatePackageSchema>)
|
||||
);
|
||||
const project = readProjectConfiguration(
|
||||
tree,
|
||||
'plugins-create-a-workspace'
|
||||
);
|
||||
expect(project.root).toEqual('libs/plugins/create-a-workspace');
|
||||
});
|
||||
|
||||
it('should specify tsc as compiler', async () => {
|
||||
await createPackageGenerator(
|
||||
tree,
|
||||
getSchema({
|
||||
compiler: 'tsc',
|
||||
})
|
||||
);
|
||||
|
||||
const { build } = readProjectConfiguration(
|
||||
tree,
|
||||
'create-a-workspace'
|
||||
).targets;
|
||||
|
||||
expect(build.executor).toEqual('@nx/js:tsc');
|
||||
});
|
||||
|
||||
it('should specify swc as compiler', async () => {
|
||||
await createPackageGenerator(
|
||||
tree,
|
||||
getSchema({
|
||||
compiler: 'swc',
|
||||
})
|
||||
);
|
||||
|
||||
const { build } = readProjectConfiguration(
|
||||
tree,
|
||||
'create-a-workspace'
|
||||
).targets;
|
||||
|
||||
expect(build.executor).toEqual('@nx/js:swc');
|
||||
});
|
||||
|
||||
it("should use name as default for the package.json's name", async () => {
|
||||
await createPackageGenerator(tree, getSchema());
|
||||
|
||||
const { root } = readProjectConfiguration(tree, 'create-a-workspace');
|
||||
const { name } = readJson<PackageJson>(
|
||||
tree,
|
||||
joinPathFragments(root, 'package.json')
|
||||
);
|
||||
|
||||
expect(name).toEqual('create-a-workspace');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,211 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
generateFiles,
|
||||
readJson,
|
||||
convertNxGenerator,
|
||||
formatFiles,
|
||||
updateProjectConfiguration,
|
||||
updateJson,
|
||||
GeneratorCallback,
|
||||
runTasksInSerial,
|
||||
joinPathFragments,
|
||||
} from '@nx/devkit';
|
||||
import { libraryGenerator as jsLibraryGenerator } from '@nx/js';
|
||||
import { nxVersion } from 'nx/src/utils/versions';
|
||||
import generatorGenerator from '../generator/generator';
|
||||
import { CreatePackageSchema } from './schema';
|
||||
import { NormalizedSchema, normalizeSchema } from './utils/normalize-schema';
|
||||
import e2eProjectGenerator from '../e2e-project/e2e';
|
||||
import { hasGenerator } from '../../utils/has-generator';
|
||||
|
||||
export async function createPackageGenerator(
|
||||
host: Tree,
|
||||
schema: CreatePackageSchema
|
||||
) {
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
const options = normalizeSchema(host, schema);
|
||||
const pluginPackageName = await addPresetGenerator(host, options);
|
||||
|
||||
const installTask = addDependenciesToPackageJson(
|
||||
host,
|
||||
{
|
||||
'create-nx-workspace': nxVersion,
|
||||
},
|
||||
{}
|
||||
);
|
||||
tasks.push(installTask);
|
||||
|
||||
await createCliPackage(host, options, pluginPackageName);
|
||||
if (options.e2eTestRunner !== 'none') {
|
||||
tasks.push(await addE2eProject(host, options));
|
||||
}
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
return runTasksInSerial(...tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a preset generator to the plugin if it doesn't exist
|
||||
* @param host
|
||||
* @param schema
|
||||
* @returns package name of the plugin
|
||||
*/
|
||||
async function addPresetGenerator(
|
||||
host: Tree,
|
||||
schema: NormalizedSchema
|
||||
): Promise<string> {
|
||||
const { root: projectRoot } = readProjectConfiguration(host, schema.project);
|
||||
if (!hasGenerator(host, schema.project, 'preset')) {
|
||||
await generatorGenerator(host, {
|
||||
name: 'preset',
|
||||
project: schema.project,
|
||||
unitTestRunner: schema.unitTestRunner,
|
||||
skipFormat: true,
|
||||
});
|
||||
}
|
||||
|
||||
return readJson(host, joinPathFragments(projectRoot, 'package.json'))?.name;
|
||||
}
|
||||
|
||||
async function createCliPackage(
|
||||
host: Tree,
|
||||
options: NormalizedSchema,
|
||||
pluginPackageName: string
|
||||
) {
|
||||
await jsLibraryGenerator(host, {
|
||||
...options,
|
||||
rootProject: false,
|
||||
config: 'project',
|
||||
publishable: true,
|
||||
bundler: options.bundler,
|
||||
importPath: options.name,
|
||||
skipFormat: true,
|
||||
skipTsConfig: true,
|
||||
});
|
||||
|
||||
host.delete(joinPathFragments(options.projectRoot, 'src'));
|
||||
|
||||
// Add the bin entry to the package.json
|
||||
updateJson(
|
||||
host,
|
||||
joinPathFragments(options.projectRoot, 'package.json'),
|
||||
(packageJson) => {
|
||||
packageJson.bin = {
|
||||
[options.name]: './bin/index.js',
|
||||
};
|
||||
packageJson.dependencies = {
|
||||
'create-nx-workspace': nxVersion,
|
||||
};
|
||||
return packageJson;
|
||||
}
|
||||
);
|
||||
|
||||
// update project build target to use the bin entry
|
||||
const projectConfiguration = readProjectConfiguration(
|
||||
host,
|
||||
options.projectName
|
||||
);
|
||||
projectConfiguration.sourceRoot = joinPathFragments(
|
||||
options.projectRoot,
|
||||
'bin'
|
||||
);
|
||||
projectConfiguration.targets.build.options.main = joinPathFragments(
|
||||
options.projectRoot,
|
||||
'bin/index.ts'
|
||||
);
|
||||
projectConfiguration.targets.build.options.updateBuildableProjectDepsInPackageJson =
|
||||
false;
|
||||
projectConfiguration.implicitDependencies = [options.project];
|
||||
updateProjectConfiguration(host, options.projectName, projectConfiguration);
|
||||
|
||||
// Add bin files to tsconfg.lib.json
|
||||
updateJson(
|
||||
host,
|
||||
joinPathFragments(options.projectRoot, 'tsconfig.lib.json'),
|
||||
(tsConfig) => {
|
||||
tsConfig.include.push('bin/**/*.ts');
|
||||
return tsConfig;
|
||||
}
|
||||
);
|
||||
|
||||
generateFiles(
|
||||
host,
|
||||
joinPathFragments(__dirname, './files/create-framework-package'),
|
||||
options.projectRoot,
|
||||
{
|
||||
...options,
|
||||
preset: pluginPackageName,
|
||||
tmpl: '',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a test file to plugin e2e project
|
||||
* @param host
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
async function addE2eProject(host: Tree, options: NormalizedSchema) {
|
||||
const pluginProjectConfiguration = readProjectConfiguration(
|
||||
host,
|
||||
options.project
|
||||
);
|
||||
const pluginOutputPath =
|
||||
pluginProjectConfiguration.targets.build.options.outputPath;
|
||||
|
||||
const cliProjectConfiguration = readProjectConfiguration(
|
||||
host,
|
||||
options.projectName
|
||||
);
|
||||
const cliOutputPath =
|
||||
cliProjectConfiguration.targets.build.options.outputPath;
|
||||
|
||||
const e2eTask = await e2eProjectGenerator(host, {
|
||||
pluginName: options.projectName,
|
||||
projectDirectory: options.projectDirectory,
|
||||
pluginOutputPath,
|
||||
npmPackageName: options.name,
|
||||
skipFormat: true,
|
||||
rootProject: false,
|
||||
});
|
||||
|
||||
const e2eProjectConfiguration = readProjectConfiguration(
|
||||
host,
|
||||
`${options.projectName}-e2e`
|
||||
);
|
||||
e2eProjectConfiguration.targets.e2e.dependsOn = ['^build'];
|
||||
updateProjectConfiguration(
|
||||
host,
|
||||
e2eProjectConfiguration.name,
|
||||
e2eProjectConfiguration
|
||||
);
|
||||
|
||||
// delete the default e2e test file
|
||||
host.delete(e2eProjectConfiguration.sourceRoot);
|
||||
|
||||
generateFiles(
|
||||
host,
|
||||
joinPathFragments(__dirname, './files/e2e'),
|
||||
e2eProjectConfiguration.sourceRoot,
|
||||
{
|
||||
...options,
|
||||
pluginOutputPath,
|
||||
cliOutputPath,
|
||||
tmpl: '',
|
||||
}
|
||||
);
|
||||
|
||||
return e2eTask;
|
||||
}
|
||||
|
||||
export default createPackageGenerator;
|
||||
export const createPackageSchematic = convertNxGenerator(
|
||||
createPackageGenerator
|
||||
);
|
||||
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { createWorkspace } from 'create-nx-workspace';
|
||||
|
||||
async function main() {
|
||||
const name = process.argv[2]; // TODO: use libraries like yargs or enquirer to set your workspace name
|
||||
if (!name) {
|
||||
throw new Error('Please provide a name for the workspace');
|
||||
}
|
||||
|
||||
console.log(`Creating the workspace: ${name}`);
|
||||
|
||||
// TODO: update below to customize the workspace
|
||||
const { directory } = await createWorkspace('<%= preset %>', {
|
||||
name,
|
||||
nxCloud: false,
|
||||
packageManager: 'npm',
|
||||
// This assumes "<%= preset %>" and "<%= projectName %>" are at the same version
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
presetVersion: require('../package.json').version,
|
||||
});
|
||||
|
||||
console.log(`Successfully created the workspace: ${directory}.`);
|
||||
}
|
||||
|
||||
main();
|
||||
@ -0,0 +1,30 @@
|
||||
import {
|
||||
uniq,
|
||||
runCreatePackageCli,
|
||||
removeTmpProject,
|
||||
} from '@nx/nx-plugin/testing';
|
||||
|
||||
describe('<%= name %> e2e', () => {
|
||||
const project = uniq('<%= name %>');
|
||||
let createPackageResult;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Create project using CLI command
|
||||
createPackageResult = await runCreatePackageCli(
|
||||
project,
|
||||
{
|
||||
pluginLibraryBuildPath: '<%= pluginOutputPath %>',
|
||||
createPackageLibraryBuildPath: '<%= cliOutputPath %>',
|
||||
}
|
||||
);
|
||||
}, 240_000);
|
||||
|
||||
afterAll(() => {
|
||||
// Remove the generated project from the file system
|
||||
removeTmpProject(project);
|
||||
});
|
||||
|
||||
it('should create project using <%= name %>', () => {
|
||||
expect(createPackageResult).toContain('Successfully created');
|
||||
});
|
||||
});
|
||||
17
packages/nx-plugin/src/generators/create-package/schema.d.ts
vendored
Normal file
17
packages/nx-plugin/src/generators/create-package/schema.d.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
import type { Linter } from '@nx/linter';
|
||||
|
||||
export interface CreatePackageSchema {
|
||||
name: string;
|
||||
project: string;
|
||||
|
||||
// options to create cli package, passed to js library generator
|
||||
directory?: string;
|
||||
skipFormat: boolean;
|
||||
tags?: string;
|
||||
unitTestRunner: 'jest' | 'none';
|
||||
linter: Linter;
|
||||
compiler: 'swc' | 'tsc';
|
||||
|
||||
// options to create e2e project, passed to e2e project generator
|
||||
e2eTestRunner?: 'jest' | 'none';
|
||||
}
|
||||
69
packages/nx-plugin/src/generators/create-package/schema.json
Normal file
69
packages/nx-plugin/src/generators/create-package/schema.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"cli": "nx",
|
||||
"$id": "NxPluginCreatePackage",
|
||||
"title": "Create a framework package",
|
||||
"description": "Create a framework package that uses Nx CLI.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The package name of cli, e.g. `create-framework-package`. Note this must be a valid NPM name to be published.",
|
||||
"$default": {
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
},
|
||||
"x-priority": "important"
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The name of the generator project.",
|
||||
"alias": "p",
|
||||
"$default": {
|
||||
"$source": "projectName"
|
||||
},
|
||||
"x-prompt": "What is the name of the project for the generator?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the app is placed."
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
"description": "Add tags to the library (used for linting).",
|
||||
"alias": "t"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"compiler": {
|
||||
"type": "string",
|
||||
"enum": ["tsc", "swc"],
|
||||
"default": "tsc",
|
||||
"description": "The compiler used by the build and test targets."
|
||||
},
|
||||
"e2eTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for end to end (E2E) tests.",
|
||||
"default": "jest"
|
||||
}
|
||||
},
|
||||
"required": ["name", "project"]
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
import {
|
||||
extractLayoutDirectory,
|
||||
getWorkspaceLayout,
|
||||
joinPathFragments,
|
||||
names,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { CreatePackageSchema } from '../schema';
|
||||
|
||||
export interface NormalizedSchema extends CreatePackageSchema {
|
||||
bundler: 'swc' | 'tsc';
|
||||
libsDir: string;
|
||||
projectName: string;
|
||||
projectRoot: string;
|
||||
projectDirectory: string;
|
||||
}
|
||||
|
||||
export function normalizeSchema(
|
||||
host: Tree,
|
||||
schema: CreatePackageSchema
|
||||
): NormalizedSchema {
|
||||
const { layoutDirectory, projectDirectory } = extractLayoutDirectory(
|
||||
schema.directory
|
||||
);
|
||||
const { libsDir: defaultLibsDir } = getWorkspaceLayout(host);
|
||||
const libsDir = layoutDirectory ?? defaultLibsDir;
|
||||
const name = names(schema.name).fileName;
|
||||
const fullProjectDirectory = projectDirectory
|
||||
? `${names(projectDirectory).fileName}/${name}`
|
||||
: name;
|
||||
const projectName = fullProjectDirectory.replace(new RegExp('/', 'g'), '-');
|
||||
const projectRoot = joinPathFragments(libsDir, fullProjectDirectory);
|
||||
return {
|
||||
...schema,
|
||||
bundler: schema.compiler ?? 'tsc',
|
||||
libsDir,
|
||||
projectName,
|
||||
projectRoot,
|
||||
name,
|
||||
projectDirectory: fullProjectDirectory,
|
||||
};
|
||||
}
|
||||
@ -74,7 +74,7 @@ function updateWorkspaceConfiguration(host: Tree, options: NormalizedSchema) {
|
||||
addProjectConfiguration(host, options.projectName, {
|
||||
root: options.projectRoot,
|
||||
projectType: 'application',
|
||||
sourceRoot: `${options.projectRoot}/src`,
|
||||
sourceRoot: `${options.projectRoot}/tests`,
|
||||
targets: {
|
||||
e2e: {
|
||||
executor: '@nx/nx-plugin:e2e',
|
||||
|
||||
@ -32,6 +32,6 @@ describe('<%= pluginName %> e2e', () => {
|
||||
const generator = 'PLACEHOLDER';
|
||||
await runNxCommandAsync(`generate <%= npmPackageName %>:${generator} --name ${name}`);
|
||||
expect(() => runNxCommand('build ${proj}')).not.toThrow();
|
||||
expect(() => checkFilesExist([`dist/${name}/index.js`])).not.toThrow();
|
||||
expect(() => checkFilesExist(`dist/${name}/index.js`)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
writeJson,
|
||||
readJson,
|
||||
ExecutorsJson,
|
||||
formatFiles,
|
||||
} from '@nx/devkit';
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import type { Schema } from './schema';
|
||||
@ -176,6 +177,10 @@ export async function executorGenerator(host: Tree, schema: Schema) {
|
||||
}
|
||||
|
||||
await updateExecutorJson(host, options);
|
||||
|
||||
if (!schema.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
}
|
||||
|
||||
export default executorGenerator;
|
||||
|
||||
@ -5,4 +5,5 @@ export interface Schema {
|
||||
unitTestRunner: 'jest' | 'none';
|
||||
includeHasher: boolean;
|
||||
skipLintChecks?: boolean;
|
||||
skipFormat?: boolean;
|
||||
}
|
||||
|
||||
@ -51,6 +51,12 @@
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not add an eslint configuration for plugin json files."
|
||||
},
|
||||
"skipFormat": {
|
||||
"type": "boolean",
|
||||
"description": "Skip formatting files.",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
}
|
||||
},
|
||||
"required": ["project", "name"],
|
||||
|
||||
@ -23,7 +23,10 @@ function normalizeOptions(tree: Tree, options: <%= className %>GeneratorSchema):
|
||||
? `${names(options.directory).fileName}/${name}`
|
||||
: name;
|
||||
const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');
|
||||
const projectRoot = `${getWorkspaceLayout(tree).libsDir}/${projectDirectory}`;
|
||||
const projectRoot =
|
||||
getWorkspaceLayout(tree).libsDir === '.'
|
||||
? '.'
|
||||
: `${getWorkspaceLayout(tree).libsDir}/${projectDirectory}`;
|
||||
const parsedTags = options.tags
|
||||
? options.tags.split(',').map((s) => s.trim())
|
||||
: [];
|
||||
|
||||
@ -4,8 +4,6 @@ import {
|
||||
joinPathFragments,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import {
|
||||
convertNxGenerator,
|
||||
generateFiles,
|
||||
getWorkspaceLayout,
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/linter';
|
||||
import { PackageJson } from 'nx/src/utils/package-json';
|
||||
import { pluginGenerator } from './plugin';
|
||||
import { Schema } from './schema';
|
||||
|
||||
@ -161,7 +162,7 @@ describe('NxPlugin Plugin Generator', () => {
|
||||
await pluginGenerator(tree, getSchema());
|
||||
|
||||
const { root } = readProjectConfiguration(tree, 'my-plugin');
|
||||
const { name } = readJson<{ name: string }>(
|
||||
const { name } = readJson<PackageJson>(
|
||||
tree,
|
||||
joinPathFragments(root, 'package.json')
|
||||
);
|
||||
@ -178,7 +179,7 @@ describe('NxPlugin Plugin Generator', () => {
|
||||
await pluginGenerator(tree, getSchema());
|
||||
|
||||
const { root } = readProjectConfiguration(tree, 'my-plugin');
|
||||
const { name } = readJson<{ name: string }>(
|
||||
const { name } = readJson<PackageJson>(
|
||||
tree,
|
||||
joinPathFragments(root, 'package.json')
|
||||
);
|
||||
@ -193,7 +194,7 @@ describe('NxPlugin Plugin Generator', () => {
|
||||
);
|
||||
|
||||
const { root } = readProjectConfiguration(tree, 'my-plugin');
|
||||
const { name } = readJson<{ name: string }>(
|
||||
const { name } = readJson<PackageJson>(
|
||||
tree,
|
||||
joinPathFragments(root, 'package.json')
|
||||
);
|
||||
|
||||
@ -3,9 +3,10 @@ import {
|
||||
convertNxGenerator,
|
||||
formatFiles,
|
||||
generateFiles,
|
||||
installPackagesTask,
|
||||
GeneratorCallback,
|
||||
normalizePath,
|
||||
readProjectConfiguration,
|
||||
runTasksInSerial,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
@ -73,17 +74,22 @@ function updatePluginConfig(host: Tree, options: NormalizedSchema) {
|
||||
|
||||
export async function pluginGenerator(host: Tree, schema: Schema) {
|
||||
const options = normalizeOptions(host, schema);
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
tasks.push(
|
||||
await jsLibraryGenerator(host, {
|
||||
...schema,
|
||||
config: 'project',
|
||||
bundler: options.bundler,
|
||||
publishable: true,
|
||||
importPath: options.npmPackageName,
|
||||
skipFormat: true,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
addTsLibDependencies(host);
|
||||
tasks.push(addTsLibDependencies(host));
|
||||
|
||||
tasks.push(
|
||||
addDependenciesToPackageJson(
|
||||
host,
|
||||
{
|
||||
@ -94,17 +100,19 @@ export async function pluginGenerator(host: Tree, schema: Schema) {
|
||||
'@nx/js': nxVersion,
|
||||
'@nx/nx-plugin': nxVersion,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Ensures Swc Deps are installed to handle running
|
||||
// local plugin generators and executors
|
||||
addSwcDependencies(host);
|
||||
addSwcRegisterDependencies(host);
|
||||
tasks.push(addSwcDependencies(host));
|
||||
tasks.push(addSwcRegisterDependencies(host));
|
||||
|
||||
await addFiles(host, options);
|
||||
updatePluginConfig(host, options);
|
||||
|
||||
if (options.e2eTestRunner !== 'none') {
|
||||
tasks.push(
|
||||
await e2eProjectGenerator(host, {
|
||||
pluginName: options.name,
|
||||
projectDirectory: options.projectDirectory,
|
||||
@ -112,16 +120,19 @@ export async function pluginGenerator(host: Tree, schema: Schema) {
|
||||
npmPackageName: options.npmPackageName,
|
||||
skipFormat: true,
|
||||
rootProject: options.rootProject,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (options.linter === Linter.EsLint && !options.skipLintChecks) {
|
||||
await pluginLintCheckGenerator(host, { projectName: options.name });
|
||||
}
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
return () => installPackagesTask(host);
|
||||
return runTasksInSerial(...tasks);
|
||||
}
|
||||
|
||||
export default pluginGenerator;
|
||||
|
||||
@ -4,9 +4,9 @@ export interface Schema {
|
||||
name: string;
|
||||
directory?: string;
|
||||
importPath?: string;
|
||||
skipTsConfig: boolean;
|
||||
skipFormat: boolean;
|
||||
skipLintChecks: boolean;
|
||||
skipTsConfig?: boolean; // default is false
|
||||
skipFormat?: boolean; // default is false
|
||||
skipLintChecks?: boolean; // default is false
|
||||
e2eTestRunner?: 'jest' | 'none';
|
||||
tags?: string;
|
||||
unitTestRunner: 'jest' | 'none';
|
||||
|
||||
@ -1,34 +1,58 @@
|
||||
import { Tree, updateJson, updateNxJson, readNxJson } from '@nx/devkit';
|
||||
import {
|
||||
Tree,
|
||||
updateJson,
|
||||
updateNxJson,
|
||||
readNxJson,
|
||||
formatFiles,
|
||||
runTasksInSerial,
|
||||
GeneratorCallback,
|
||||
} from '@nx/devkit';
|
||||
import { Linter } from '@nx/linter';
|
||||
import { PackageJson } from 'nx/src/utils/package-json';
|
||||
import { pluginGenerator } from '../plugin/plugin';
|
||||
import { PresetGeneratorSchema } from './schema';
|
||||
import createPackageGenerator from '../create-package/create-package';
|
||||
|
||||
export default async function (tree: Tree, options: PresetGeneratorSchema) {
|
||||
const task = await pluginGenerator(tree, {
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
const pluginTask = await pluginGenerator(tree, {
|
||||
compiler: 'tsc',
|
||||
linter: Linter.EsLint,
|
||||
name: options.pluginName.includes('/')
|
||||
? options.pluginName.split('/')[1]
|
||||
: options.pluginName,
|
||||
skipFormat: false,
|
||||
skipLintChecks: false,
|
||||
skipTsConfig: false,
|
||||
skipFormat: true,
|
||||
unitTestRunner: 'jest',
|
||||
importPath: options.pluginName,
|
||||
rootProject: true,
|
||||
e2eTestRunner: 'jest',
|
||||
});
|
||||
tasks.push(pluginTask);
|
||||
|
||||
removeNpmScope(tree);
|
||||
moveNxPluginToDevDeps(tree);
|
||||
|
||||
return task;
|
||||
if (options.createPackageName) {
|
||||
const cliTask = await createPackageGenerator(tree, {
|
||||
name: options.createPackageName,
|
||||
project: options.pluginName,
|
||||
skipFormat: true,
|
||||
unitTestRunner: 'jest',
|
||||
linter: Linter.EsLint,
|
||||
compiler: 'tsc',
|
||||
});
|
||||
tasks.push(cliTask);
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
|
||||
return runTasksInSerial(...tasks);
|
||||
}
|
||||
|
||||
function removeNpmScope(tree: Tree) {
|
||||
updateNxJson(tree, { ...readNxJson(tree), npmScope: undefined });
|
||||
}
|
||||
|
||||
function moveNxPluginToDevDeps(tree: Tree) {
|
||||
updateJson<PackageJson>(tree, 'package.json', (json) => {
|
||||
const nxPluginEntry = json.dependencies['@nx/nx-plugin'];
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export interface PresetGeneratorSchema {
|
||||
pluginName: string;
|
||||
createPackageName?: string;
|
||||
}
|
||||
|
||||
@ -10,6 +10,10 @@
|
||||
"type": "string",
|
||||
"description": "Plugin name",
|
||||
"aliases": ["name"]
|
||||
},
|
||||
"createPackageName": {
|
||||
"type": "string",
|
||||
"description": "Name of package which creates a workspace"
|
||||
}
|
||||
},
|
||||
"required": ["pluginName"]
|
||||
|
||||
@ -11,7 +11,7 @@ import { fileExists } from './utils';
|
||||
*/
|
||||
export function runCommandAsync(
|
||||
command: string,
|
||||
opts: { silenceError?: boolean; env?: NodeJS.ProcessEnv } = {
|
||||
opts: { silenceError?: boolean; env?: NodeJS.ProcessEnv; cwd?: string } = {
|
||||
silenceError: false,
|
||||
}
|
||||
): Promise<{ stdout: string; stderr: string }> {
|
||||
@ -19,7 +19,7 @@ export function runCommandAsync(
|
||||
exec(
|
||||
command,
|
||||
{
|
||||
cwd: tmpProjPath(),
|
||||
cwd: opts.cwd ?? tmpProjPath(),
|
||||
env: { ...process.env, ...opts.env },
|
||||
},
|
||||
(err, stdout, stderr) => {
|
||||
@ -39,7 +39,7 @@ export function runCommandAsync(
|
||||
*/
|
||||
export function runNxCommandAsync(
|
||||
command: string,
|
||||
opts: { silenceError?: boolean; env?: NodeJS.ProcessEnv } = {
|
||||
opts: { silenceError?: boolean; env?: NodeJS.ProcessEnv; cwd?: string } = {
|
||||
silenceError: false,
|
||||
}
|
||||
): Promise<{ stdout: string; stderr: string }> {
|
||||
|
||||
@ -12,13 +12,13 @@ import { fileExists } from './utils';
|
||||
*/
|
||||
export function runNxCommand(
|
||||
command?: string,
|
||||
opts: { silenceError?: boolean; env?: NodeJS.ProcessEnv } = {
|
||||
opts: { silenceError?: boolean; env?: NodeJS.ProcessEnv; cwd?: string } = {
|
||||
silenceError: false,
|
||||
}
|
||||
): string {
|
||||
function _runNxCommand(c) {
|
||||
const execSyncOptions: ExecOptions = {
|
||||
cwd: tmpProjPath(),
|
||||
cwd: opts.cwd,
|
||||
env: { ...process.env, ...opts.env },
|
||||
};
|
||||
if (fileExists(tmpProjPath('package.json'))) {
|
||||
@ -50,11 +50,11 @@ export function runNxCommand(
|
||||
|
||||
export function runCommand(
|
||||
command: string,
|
||||
opts?: { env?: NodeJS.ProcessEnv }
|
||||
opts: { env?: NodeJS.ProcessEnv; cwd?: string }
|
||||
): string {
|
||||
try {
|
||||
return execSync(command, {
|
||||
cwd: tmpProjPath(),
|
||||
cwd: opts.cwd ?? tmpProjPath(),
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
env: { ...process.env, ...opts?.env },
|
||||
}).toString();
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
import { tmpFolder } from './paths';
|
||||
import { fork } from 'child_process';
|
||||
|
||||
/**
|
||||
* This function is used to run the create package CLI command.
|
||||
* It builds the plugin library and the create package library and run the create package command with for the plugin library.
|
||||
* It needs to be ran inside an Nx project. It would assume that an Nx project already exists.
|
||||
* @param projectToBeCreated project name to be created using the cli
|
||||
* @param pluginLibraryBuildPath e.g. dist/packages/my-plugin
|
||||
* @param createPackageLibraryBuildPath e.g. dist/packages/create-my-plugin-package
|
||||
* @param extraArguments extra arguments to be passed to the create package command
|
||||
* @param verbose if true, NX_VERBOSE_LOGGING will be set to true
|
||||
* @returns results for the create package command
|
||||
*/
|
||||
export function runCreatePackageCli(
|
||||
projectToBeCreated: string,
|
||||
{
|
||||
pluginLibraryBuildPath,
|
||||
createPackageLibraryBuildPath,
|
||||
extraArguments,
|
||||
verbose,
|
||||
}: {
|
||||
pluginLibraryBuildPath: string;
|
||||
createPackageLibraryBuildPath: string;
|
||||
extraArguments?: string[];
|
||||
verbose?: boolean;
|
||||
}
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const childProcess = fork(
|
||||
`${workspaceRoot}/${createPackageLibraryBuildPath}/bin/index.js`,
|
||||
[projectToBeCreated, ...(extraArguments || [])],
|
||||
{
|
||||
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
||||
env: {
|
||||
...process.env,
|
||||
[`NX_E2E_PRESET_VERSION`]: `file:${workspaceRoot}/${pluginLibraryBuildPath}`,
|
||||
// only add NX_VERBOSE_LOGGING if verbose is true
|
||||
...(verbose && { NX_VERBOSE_LOGGING: 'true' }),
|
||||
},
|
||||
cwd: tmpFolder(),
|
||||
}
|
||||
);
|
||||
|
||||
// Ensure the child process is killed when the parent exits
|
||||
process.on('exit', () => childProcess.kill());
|
||||
process.on('SIGTERM', () => childProcess.kill());
|
||||
|
||||
let allMessages = '';
|
||||
childProcess.on('message', (message) => {
|
||||
allMessages += message;
|
||||
});
|
||||
childProcess.stdout.on('data', (data) => {
|
||||
allMessages += data;
|
||||
});
|
||||
childProcess.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
childProcess.on('exit', (code) => {
|
||||
if (code === 0) {
|
||||
resolve(allMessages);
|
||||
} else {
|
||||
reject(allMessages);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function generatedPackagePath(projectToBeCreated: string) {
|
||||
return `${tmpFolder()}/${projectToBeCreated}`;
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
export * from './async-commands';
|
||||
export * from './commands';
|
||||
export * from './create-package-cli';
|
||||
export * from './paths';
|
||||
export * from './nx-project';
|
||||
export * from './utils';
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
|
||||
export function tmpFolder() {
|
||||
return `${workspaceRoot}/tmp`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The directory where the e2e workspace resides in.
|
||||
*
|
||||
@ -6,8 +12,8 @@
|
||||
*/
|
||||
export function tmpProjPath(path?: string) {
|
||||
return path
|
||||
? `${process.cwd()}/tmp/nx-e2e/proj/${path}`
|
||||
: `${process.cwd()}/tmp/nx-e2e/proj`;
|
||||
? `${tmpFolder()}/nx-e2e/proj/${path}`
|
||||
: `${tmpFolder()}/nx-e2e/proj`;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18,6 +24,6 @@ export function tmpProjPath(path?: string) {
|
||||
*/
|
||||
export function tmpBackupProjPath(path?: string) {
|
||||
return path
|
||||
? `${process.cwd()}/tmp/nx-e2e/proj-backup/${path}`
|
||||
: `${process.cwd()}/tmp/nx-e2e/proj-backup`;
|
||||
? `${workspaceRoot}/tmp/nx-e2e/proj-backup/${path}`
|
||||
: `${workspaceRoot}/tmp/nx-e2e/proj-backup`;
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
writeFileSync,
|
||||
} from 'fs-extra';
|
||||
import { dirname, isAbsolute } from 'path';
|
||||
import { tmpProjPath } from './paths';
|
||||
import { tmpFolder, tmpProjPath } from './paths';
|
||||
import { parseJson } from '@nx/devkit';
|
||||
import type { JsonParseOptions } from '@nx/devkit';
|
||||
import { directoryExists, fileExists } from 'nx/src/utils/fileutils';
|
||||
@ -135,6 +135,10 @@ export function rmDist(): void {
|
||||
removeSync(`${tmpProjPath()}/dist`);
|
||||
}
|
||||
|
||||
export function removeTmpProject(project: string): void {
|
||||
removeSync(`${tmpFolder()}/${project}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currend `cwd` in the process
|
||||
*/
|
||||
|
||||
@ -2,7 +2,7 @@ import { ModuleFederationConfig } from '@nx/devkit';
|
||||
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
|
||||
import { getModuleFederationConfig } from './utils';
|
||||
import ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
|
||||
import type { AsyncNxWebpackPlugin, NxWebpackPlugin } from '@nx/webpack';
|
||||
import type { AsyncNxWebpackPlugin } from '@nx/webpack';
|
||||
|
||||
function determineRemoteUrl(remote: string) {
|
||||
const remoteConfiguration = readCachedProjectConfiguration(remote);
|
||||
|
||||
@ -94,5 +94,6 @@
|
||||
"type": "string",
|
||||
"enum": ["cypress", "jest", "detox", "none"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["preset", "name"]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user