Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | 4x 4x 4x 4x 108x 106x 4x 108x 108x 108x 2x 4x 106x 106x 106x 106x 106x 26x 106x 4x 106x 106x 106x 93x 2x 91x 13x 1x 12x 106x 4x 106x 106x 3x 3x 106x 4x 26x 2x 24x 4x 20x 20x 20x 26x 4x 199x 4x 20x 19x 1x 4x 106x 4x | import { joinPathFragments, normalizePath, readJson, Tree, workspaceRoot, } from '@nx/devkit'; import { ProjectType } from '@nx/workspace'; import { normalize, relative } from 'node:path'; export type ProjectOptions = ProjectOptionsApplication | ProjectOptionsLibrary; export interface ProjectOptionsApplication { name: string; directory: string | undefined; projectType: ProjectType.Application; } export interface ProjectOptionsLibrary { name: string; directory: string | undefined; projectType: ProjectType.Library; importPath: string | undefined; } export type NormalizedProjectOptions<T extends ProjectOptions> = T extends ProjectOptionsLibrary ? NormalizedProjectOptionsLibrary : NormalizedProjectOptionsApplication; export interface NormalizedProjectOptionsApplication { /** * Normalized full project name that can contain a npm scope (e.g '@org/example'). */ projectName: string; /** * Normalized project root that represents the path to the project from the workspace root. */ projectRoot: string; /** * Normalized project name without scope. It's meant to be used when generating file names that contain the project name. */ projectFileName: string; } export interface NormalizedProjectOptionsLibrary { /** * Normalized full project name that can contain a npm scope (e.g '@org/example'). */ projectName: string; /** * Normalized project root that represents the path to the project from the workspace root. */ projectRoot: string; /** * Normalized project name without scope. It's meant to be used when generating file names that contain the project name. */ projectFileName: string; /** * Normalized import path for the project. Defines the npm package name for publishable projects. */ importPath: string; } export const normalizeProjectOptions = <T extends ProjectOptions>( tree: Tree, options: T, ): NormalizedProjectOptions<T> => { validateProjectName(options.name); return createNormalizedProjectOptions(tree, options); }; const validateProjectName = (name: string): void => { /** * Matches two types of project names: * * 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 validationRegExp = new RegExp(pattern); if (!validationRegExp.test(name)) { throw new Error( `The provided project name '${name}' is invalid. It must match the pattern '${pattern}'.`, ); } }; const createNormalizedProjectOptions = <T extends ProjectOptions>( tree: Tree, options: T, ): NormalizedProjectOptions<T> => { const projectName = options.name; const projectDirectory = getProjectDirectory(projectName, options); const projectFileName = getProjectFileName(projectName); const normalizedOptions: | NormalizedProjectOptionsApplication | NormalizedProjectOptionsLibrary = { projectName, projectFileName, projectRoot: projectDirectory, }; if (options.projectType === ProjectType.Library) { (normalizedOptions as NormalizedProjectOptionsLibrary).importPath = getProjectImportPath(tree, projectName, options); } return normalizedOptions as NormalizedProjectOptions<T>; }; const getProjectDirectory = ( projectName: string, options: ProjectOptions, ): string => { let projectDirectory: string; const relativeDirectoryUnix = options.directory ? removeTrailingSlash(normalizePath(normalize(options.directory))) : undefined; const relativeCwdUnix = removeTrailingSlash( normalizePath(normalize(relative(workspaceRoot, getCwd()))), ); if (relativeDirectoryUnix) { // If a directory has been given, it is used to determine the project directory. if ( relativeDirectoryUnix === relativeCwdUnix || relativeDirectoryUnix.startsWith(`${relativeCwdUnix}/`) ) { // If the relative CWD is part of the given directory, then use the directory as project directory. projectDirectory = relativeDirectoryUnix; } else { // Otherwise, append the given directory to the CWD and use the resulting path as project directory. projectDirectory = joinPathFragments( relativeCwdUnix, relativeDirectoryUnix, ); } } else { // If no directory has been given, determine the project directory with the help of the CWD. if (relativeCwdUnix.endsWith(projectName)) { // If the CWD does end with the project name, then use the CWD as project directory. projectDirectory = relativeCwdUnix; } else { // If the CWD doesn't end with the project name, then append the project name to the CWD and use the resulting path as project directory. projectDirectory = joinPathFragments(relativeCwdUnix, projectName); } } return projectDirectory; }; const getProjectFileName = (projectName: string): string => { let projectFileName = projectName; // If the project name contains a npm scope. if (projectName.startsWith('@')) { const projectNameWithoutScope = projectName.split('/')[1]; projectFileName = projectNameWithoutScope as string; } return projectFileName; }; const getProjectImportPath = ( tree: Tree, projectName: string, options: ProjectOptionsLibrary, ): string => { let projectImportPath: string; // If the project name contains a npm scope. if (projectName.startsWith('@')) { // Use the project name as npm package name, if no import path has been defined. projectImportPath = options.importPath ?? projectName; } else { // If the project name doesn't contain a npm scope. if (options.importPath) { // Use the import path as package name, if it is defined. projectImportPath = options.importPath; } else { const workspacePackageJsonName = getWorkspacePackageJsonName(tree); const workspacePackageJsonScope = workspacePackageJsonName?.startsWith( '@', ) ? workspacePackageJsonName?.split('/')[0] : undefined; // Append the project name to the workspace npm scope and use it as package name. // If the workspace npm scope isn't defined, then use the project name as package name. projectImportPath = workspacePackageJsonScope ? `${workspacePackageJsonScope}/${projectName}` : projectName; } } return projectImportPath; }; const removeTrailingSlash = (pathUnix: string): string => { return pathUnix.replace(/\/$/, ''); }; const getWorkspacePackageJsonName = (tree: Tree): string | undefined => { if (tree.exists('package.json')) { return readJson<{ name?: string }>(tree, 'package.json').name; } return 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). */ const getCwd = (): string => { return process.env['INIT_CWD']?.startsWith(workspaceRoot) ? process.env['INIT_CWD'] : process.cwd(); }; export default normalizeProjectOptions; |