UNPKG

@nx/devkit

Version:

The Nx Devkit is used to customize Nx for different technologies and use cases. It contains many utility functions for reading and writing files, updating configuration, working with Abstract Syntax Trees(ASTs), and more. Learn more about [extending Nx by

135 lines (134 loc) 5.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.determineProjectNameAndRootOptions = determineProjectNameAndRootOptions; exports.resolveImportPath = resolveImportPath; exports.ensureRootProjectName = ensureRootProjectName; const enquirer_1 = require("enquirer"); const devkit_exports_1 = require("nx/src/devkit-exports"); const path_1 = require("path"); async function determineProjectNameAndRootOptions(tree, options) { // root projects must provide name option if (options.directory === '.' && !options.name) { throw new Error(`When generating a root project, you must also specify the name option.`); } const directory = (0, devkit_exports_1.normalizePath)(options.directory); const name = options.name ?? directory.match(/(@[^@/]+(\/[^@/]+)+)/)?.[1] ?? directory.substring(directory.lastIndexOf('/') + 1); validateOptions(options.name, name, options.directory); let projectSimpleName; let projectFileName; if (name.startsWith('@')) { const [_scope, ...rest] = name.split('/'); projectFileName = rest.join('-'); projectSimpleName = rest.pop(); } else { projectSimpleName = name; projectFileName = name; } let projectRoot; const relativeCwd = getRelativeCwd(); if (directory) { // append the directory to the current working directory if it doesn't start with it if (directory === relativeCwd || directory.startsWith(`${relativeCwd}/`)) { projectRoot = directory; } else { projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd, directory); } } else if (options.rootProject) { projectRoot = '.'; } else { projectRoot = relativeCwd; // append the project name to the current working directory if it doesn't end with it if (!relativeCwd.endsWith(name)) { projectRoot = (0, devkit_exports_1.joinPathFragments)(relativeCwd, name); } } if (projectRoot.startsWith('..')) { throw new Error(`The resolved project root "${projectRoot}" is outside of the workspace root "${devkit_exports_1.workspaceRoot}".`); } const importPath = options.importPath ?? resolveImportPath(tree, name, projectRoot); return { projectName: name, names: { projectSimpleName, projectFileName, }, importPath, projectRoot, }; } function resolveImportPath(tree, projectName, projectRoot) { let importPath; if (projectName.startsWith('@')) { importPath = projectName; } else { const npmScope = getNpmScope(tree); importPath = projectRoot === '.' ? (0, devkit_exports_1.readJson)(tree, 'package.json').name ?? getImportPath(npmScope, projectName) : getImportPath(npmScope, projectName); } return importPath; } async function ensureRootProjectName(options, projectType) { if (!options.name && options.directory === '.' && getRelativeCwd() === '') { const result = await (0, enquirer_1.prompt)({ type: 'input', name: 'name', message: `What do you want to name the ${projectType}?`, }); options.name = result.name; } } function validateOptions(providedName, derivedName, directory) { /** * The provided name and the derived name from the provided directory must match one of two cases: * * 1. Valid npm package names (e.g., '@scope/name' or 'name'). * 2. Names starting with a letter and can contain any character except whitespace and ':'. * * The second case is to support the legacy behavior (^[a-zA-Z].*$) with the difference * that it doesn't allow the ":" character. It was wrong to allow it because it would * conflict with the notation for tasks. */ const pattern = '(?:^@[a-zA-Z0-9-*~][a-zA-Z0-9-*._~]*\\/[a-zA-Z0-9-~][a-zA-Z0-9-._~]*|^[a-zA-Z][^:]*)$'; const validationRegex = new RegExp(pattern); if (providedName) { if (!validationRegex.test(providedName)) { throw new Error(`The name should match the pattern "${pattern}". The provided value "${providedName}" does not match.`); } } else if (!validationRegex.test(derivedName)) { throw new Error(`The derived name from the provided directory should match the pattern "${pattern}". The derived name "${derivedName}" from the provided value "${directory}" does not match.`); } } function getImportPath(npmScope, name) { return npmScope ? `${npmScope === '@' ? '' : '@'}${npmScope}/${name}` : name; } function getNpmScope(tree) { const { name } = tree.exists('package.json') ? (0, devkit_exports_1.readJson)(tree, 'package.json') : { name: null }; return name?.startsWith('@') ? name.split('/')[0].substring(1) : undefined; } /** * When running a script with the package manager (e.g. `npm run`), the package manager will * traverse the directory tree upwards until it finds a `package.json` and will set `process.cwd()` * to the folder where it found it. The actual working directory is stored in the INIT_CWD * environment variable (see here: https://docs.npmjs.com/cli/v9/commands/npm-run-script#description). */ function getCwd() { return process.env.INIT_CWD?.startsWith(devkit_exports_1.workspaceRoot) ? process.env.INIT_CWD : process.cwd(); } function getRelativeCwd() { return (0, devkit_exports_1.normalizePath)((0, path_1.relative)(devkit_exports_1.workspaceRoot, getCwd())).replace(/\/$/, ''); }