@nx/devkit
Version:
135 lines (134 loc) • 5.67 kB
JavaScript
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(/\/$/, '');
}
;