@@ -37,6 +37,11 @@ "use strict"; | ||
| const { nxArgs } = (0, command_line_utils_1.splitArgsIntoNxArgsAndOverrides)(args, 'affected', { printWarnings: false }, (0, configuration_1.readNxJson)()); | ||
| const patterns = (await getPatterns(prettier, { ...args, ...nxArgs })).map( | ||
| // prettier removes one of the \ | ||
| // prettier-ignore | ||
| (p) => `"${p.replace(/\$/g, '\\\$')}"`); | ||
| const patterns = (await getPatterns(prettier, { ...args, ...nxArgs })).map((p) => { | ||
| // On non-Windows, escape $ to prevent shell variable interpolation | ||
| // (the shell consumes one \, so \\$ becomes \$ which the shell treats as literal $) | ||
| // On Windows (cmd.exe), $ is not a special character, so escaping it would | ||
| // cause prettier to look for a file with a literal \$ in the name | ||
| // prettier-ignore | ||
| const escaped = process.platform !== 'win32' ? p.replace(/\$/g, '\\\$') : p; | ||
| return `"${escaped}"`; | ||
| }); | ||
| // Chunkify the patterns array to prevent crashing the windows terminal | ||
@@ -208,3 +213,8 @@ const chunkList = (0, chunkify_1.chunkify)(patterns); | ||
| const { packageJson, path: packageJsonPath } = (0, package_json_1.readModulePackageJson)('prettier'); | ||
| prettierPath = path.resolve(path.dirname(packageJsonPath), packageJson.bin); | ||
| const bin = packageJson.bin; | ||
| const binPath = typeof bin === 'string' ? bin : bin?.['prettier']; | ||
| if (!binPath) { | ||
| throw new Error(`Could not find prettier binary in ${packageJsonPath}`); | ||
| } | ||
| prettierPath = path.resolve(path.dirname(packageJsonPath), binPath); | ||
| return prettierPath; | ||
@@ -211,0 +221,0 @@ } |
@@ -386,3 +386,2 @@ "use strict"; | ||
| const packagesWeMayCareAbout = {}; | ||
| // TODO (v20): Remove workaround for hiding @nrwl packages when matching @nx package is found. | ||
| for (const pkg of exports.packagesWeCareAbout) { | ||
@@ -389,0 +388,0 @@ const v = readPackageVersion(pkg); |
@@ -11,2 +11,3 @@ "use strict"; | ||
| const packages_1 = require("../plugins/js/utils/packages"); | ||
| const typescript_1 = require("../plugins/js/utils/typescript"); | ||
| const path_2 = require("../utils/path"); | ||
@@ -100,3 +101,3 @@ /** | ||
| exports: localProject.metadata.js.packageExports, | ||
| }, path, { conditions: ['development'] }); | ||
| }, path, { conditions: (0, typescript_1.getRootTsConfigResolveExportsConditions)() }); | ||
| if (fromExports && fromExports.length) { | ||
@@ -103,0 +104,0 @@ for (const exportPath of fromExports) { |
@@ -33,35 +33,11 @@ import type { TsConfigOptions } from 'ts-node'; | ||
| /** | ||
| * Optionally, if swc-node and tsconfig-paths are available in the current workspace, apply the require | ||
| * register hooks so that .ts files can be used for writing custom workspace projects. | ||
| * This function registers either ts-node or swc-node to transpile TypeScript files on the fly. | ||
| * It also registers tsconfig-paths to handle path mapping based on the provided tsconfig. | ||
| * | ||
| * If ts-node and tsconfig-paths are not available, the user can still provide an index.js file in | ||
| * the root of their project and the fundamentals will still work (but | ||
| * workspace path mapping will not, for example). | ||
| * The TypeScript transpiler registration is done regardless of NX_PREFER_NODE_STRIP_TYPES. | ||
| * If you want to skip transpiler registration, it is recommended that you check `process.features.typescript`. | ||
| * | ||
| * Behavior change in v23: when the runtime exposes native TypeScript type | ||
| * stripping (`process.features.typescript`), this function skips the | ||
| * transpiler (Node handles `.ts` directly) but still registers | ||
| * `tsconfig-paths`. Path mapping is orthogonal to transpilation - callers | ||
| * relying on tsconfig `paths` for workspace alias resolution still get them. | ||
| * For loading a `.ts` file whose syntax native strip can't handle (`enum`, | ||
| * runtime `namespace`, legacy decorators, etc.), use `loadTsFile`, which | ||
| * registers swc/ts-node + tsconfig-paths on demand. To restore the legacy | ||
| * behavior (always register swc/ts-node + tsconfig-paths up front), set | ||
| * `NX_PREFER_NODE_STRIP_TYPES=false`. | ||
| * | ||
| * @returns cleanup function | ||
| */ | ||
| export declare function registerTsProject(tsConfigPath: string): () => void; | ||
| /** | ||
| * Optionally, if swc-node and tsconfig-paths are available in the current workspace, apply the require | ||
| * register hooks so that .ts files can be used for writing custom workspace projects. | ||
| * | ||
| * If ts-node and tsconfig-paths are not available, the user can still provide an index.js file in | ||
| * the root of their project and the fundamentals will still work (but | ||
| * workspace path mapping will not, for example). | ||
| * | ||
| * @returns cleanup function | ||
| * @deprecated This signature will be removed in Nx v19. You should pass the full path to the tsconfig in the first argument. | ||
| */ | ||
| export declare function registerTsProject(path: string, configFilename: string): any; | ||
| export declare function getSwcTranspiler(compilerOptions: CompilerOptions): (...args: unknown[]) => unknown; | ||
@@ -68,0 +44,0 @@ export declare function getTsNodeTranspiler(compilerOptions: CompilerOptions, tsNodeOptions?: TsConfigOptions, preferTsNode?: boolean): (...args: unknown[]) => unknown; |
@@ -174,3 +174,12 @@ "use strict"; | ||
| } | ||
| function registerTsProject(path, configFilename) { | ||
| /** | ||
| * This function registers either ts-node or swc-node to transpile TypeScript files on the fly. | ||
| * It also registers tsconfig-paths to handle path mapping based on the provided tsconfig. | ||
| * | ||
| * The TypeScript transpiler registration is done regardless of NX_PREFER_NODE_STRIP_TYPES. | ||
| * If you want to skip transpiler registration, it is recommended that you check `process.features.typescript`. | ||
| * | ||
| * @returns cleanup function | ||
| */ | ||
| function registerTsProject(tsConfigPath) { | ||
| // See explanation alongside isInvokedByTsx declaration | ||
@@ -180,20 +189,2 @@ if (isInvokedByTsx) { | ||
| } | ||
| const tsConfigPath = configFilename ? (0, path_1.join)(path, configFilename) : path; | ||
| // Under native strip we skip the transpiler (Node handles `.ts` directly) | ||
| // but still register tsconfig-paths. Path mapping is orthogonal to | ||
| // transpilation: code calling `registerTsProject` for path aliases | ||
| // (e.g. test setup files requiring `@my-org/lib`) gets nothing back if | ||
| // both are skipped. Package-manager-workspace symlinks aren't a | ||
| // universal substitute - explicit tsconfig `paths` configs still need | ||
| // runtime alias resolution. Callers needing the transpiler for | ||
| // unsupported syntax (enum, runtime namespace, legacy decorators, etc.) | ||
| // should use `loadTsFile` instead, which registers swc/ts-node + | ||
| // tsconfig-paths on demand. | ||
| if (preferNodeStripTypes) { | ||
| return registerTsConfigPaths(tsConfigPath); | ||
| } | ||
| // Legacy path: prior to v23, Nx always registered swc-node/ts-node and | ||
| // tsconfig-paths to load .ts config files. v23+ prefers Node's built-in | ||
| // type stripping; this branch only runs when the user opted out via | ||
| // NX_PREFER_NODE_STRIP_TYPES=false or when strip is unavailable. | ||
| const { compilerOptions, tsConfigRaw } = readCompilerOptions(tsConfigPath); | ||
@@ -200,0 +191,0 @@ const cleanupFunctions = [ |
@@ -16,2 +16,9 @@ import type * as ts from 'typescript'; | ||
| export declare function getRootTsConfigPath(): string | null; | ||
| export declare function getRootTsConfigCustomConditions(root?: string): string[]; | ||
| /** | ||
| * Conditions list for `resolve.exports`: workspace `customConditions` plus | ||
| * `development` as backward-compat for workspaces not yet migrated by | ||
| * `migrate-development-custom-condition` (21.5). | ||
| */ | ||
| export declare function getRootTsConfigResolveExportsConditions(root?: string): string[]; | ||
| export declare function findNodes(node: Node, kind: SyntaxKind | SyntaxKind[], max?: number): Node[]; |
@@ -9,2 +9,4 @@ "use strict"; | ||
| exports.getRootTsConfigPath = getRootTsConfigPath; | ||
| exports.getRootTsConfigCustomConditions = getRootTsConfigCustomConditions; | ||
| exports.getRootTsConfigResolveExportsConditions = getRootTsConfigResolveExportsConditions; | ||
| exports.findNodes = findNodes; | ||
@@ -78,2 +80,39 @@ const workspace_root_1 = require("../../../utils/workspace-root"); | ||
| } | ||
| const customConditionsCache = new Map(); | ||
| function getRootTsConfigCustomConditions(root = workspace_root_1.workspaceRoot) { | ||
| if (customConditionsCache.has(root)) { | ||
| return customConditionsCache.get(root); | ||
| } | ||
| // Resolve via the TypeScript API rather than a raw JSON read so that | ||
| // `customConditions` inherited through `extends` chains are honored — | ||
| // matches what TypeScript itself sees when resolving package exports. | ||
| let conditions = []; | ||
| for (const name of ['tsconfig.base.json', 'tsconfig.json']) { | ||
| const tsConfigPath = (0, path_1.join)(root, name); | ||
| if (!(0, fs_1.existsSync)(tsConfigPath)) { | ||
| continue; | ||
| } | ||
| try { | ||
| const options = readTsConfigOptions(tsConfigPath); | ||
| if (Array.isArray(options.customConditions)) { | ||
| conditions = options.customConditions.filter((c) => typeof c === 'string'); | ||
| } | ||
| } | ||
| catch { } | ||
| break; | ||
| } | ||
| customConditionsCache.set(root, conditions); | ||
| return conditions; | ||
| } | ||
| /** | ||
| * Conditions list for `resolve.exports`: workspace `customConditions` plus | ||
| * `development` as backward-compat for workspaces not yet migrated by | ||
| * `migrate-development-custom-condition` (21.5). | ||
| */ | ||
| function getRootTsConfigResolveExportsConditions(root = workspace_root_1.workspaceRoot) { | ||
| const conditions = getRootTsConfigCustomConditions(root); | ||
| return conditions.includes('development') | ||
| ? conditions | ||
| : [...conditions, 'development']; | ||
| } | ||
| function findNodes(node, kind, max = Infinity) { | ||
@@ -80,0 +119,0 @@ if (!node || max == 0) { |
| import type { ProjectConfiguration } from '../../config/workspace-json-project-json'; | ||
| type LocalPluginMatch = { | ||
| path: string; | ||
| projectConfig: ProjectConfiguration; | ||
| resolvedFile?: string; | ||
| }; | ||
| export declare function resolveNxPlugin(moduleName: string, root: string, paths: string[]): Promise<{ | ||
@@ -7,6 +12,3 @@ pluginPath: string; | ||
| }>; | ||
| export declare function resolveLocalNxPlugin(importPath: string, projects: Record<string, ProjectConfiguration>, root?: string): { | ||
| path: string; | ||
| projectConfig: ProjectConfiguration; | ||
| } | null; | ||
| export declare function resolveLocalNxPlugin(importPath: string, projects: Record<string, ProjectConfiguration>, root?: string): LocalPluginMatch | null; | ||
| export declare function getPluginPathAndName(moduleName: string, paths: string[], projects: Record<string, ProjectConfiguration>, root: string): { | ||
@@ -17,1 +19,2 @@ pluginPath: string; | ||
| }; | ||
| export {}; |
@@ -9,3 +9,5 @@ "use strict"; | ||
| const node_fs_1 = require("node:fs"); | ||
| const resolve_exports_1 = require("resolve.exports"); | ||
| const packages_1 = require("../../plugins/js/utils/packages"); | ||
| const typescript_1 = require("../../plugins/js/utils/typescript"); | ||
| const fileutils_1 = require("../../utils/fileutils"); | ||
@@ -17,17 +19,46 @@ const logger_1 = require("../../utils/logger"); | ||
| const retrieve_workspace_files_1 = require("../utils/retrieve-workspace-files"); | ||
| const TS_SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.cts', '.mts']); | ||
| let projectsWithoutInference; | ||
| let projectsWithoutInferencePromise = null; | ||
| async function resolveNxPlugin(moduleName, root, paths) { | ||
| try { | ||
| require.resolve(moduleName, { paths }); | ||
| // Default plugins (see `getDefaultPlugins` in `get-plugins.ts`) are passed | ||
| // as absolute file paths to compiled bundles inside `nx` itself; they are | ||
| // never workspace-local. Skip the project load entirely for them to avoid | ||
| // recursing through `retrieveProjectConfigurationsWithoutPluginInference`, | ||
| // which itself triggers default-plugin loading. | ||
| if (!path.isAbsolute(moduleName)) { | ||
| let resolvedFromNode; | ||
| try { | ||
| resolvedFromNode = require.resolve(moduleName, { paths }); | ||
| } | ||
| catch { } | ||
| // Load projects if Node couldn't resolve (so the local fallback can run) | ||
| // OR if Node resolved to a workspace-internal path (a symlinked workspace | ||
| // package whose source-first lookup should win over the symlinked dist). | ||
| if (!resolvedFromNode || | ||
| isWorkspaceLocalResolution(resolvedFromNode, root)) { | ||
| projectsWithoutInferencePromise ??= | ||
| (0, retrieve_workspace_files_1.retrieveProjectConfigurationsWithoutPluginInference)(root); | ||
| projectsWithoutInference ??= await projectsWithoutInferencePromise; | ||
| } | ||
| } | ||
| catch { | ||
| // If a plugin cannot be resolved, we will need projects to resolve it | ||
| projectsWithoutInferencePromise ??= | ||
| (0, retrieve_workspace_files_1.retrieveProjectConfigurationsWithoutPluginInference)(root); | ||
| projectsWithoutInference ??= await projectsWithoutInferencePromise; | ||
| } | ||
| const { pluginPath, name, shouldRegisterTSTranspiler } = getPluginPathAndName(moduleName, paths, projectsWithoutInference, root); | ||
| return { pluginPath, name, shouldRegisterTSTranspiler }; | ||
| } | ||
| /** | ||
| * Distinguishes a symlinked workspace package (where `require.resolve` | ||
| * follows the package-manager symlink into the workspace source tree) from | ||
| * a truly-installed dependency under `node_modules/`. The former needs the | ||
| * source-first lookup to bypass the dist that Node would otherwise return. | ||
| */ | ||
| function isWorkspaceLocalResolution(resolvedPath, root) { | ||
| const normalizedRoot = path.normalize(root); | ||
| const normalizedPath = path.normalize(resolvedPath); | ||
| return (normalizedPath.startsWith(normalizedRoot + path.sep) && | ||
| !normalizedPath.includes(path.sep + 'node_modules' + path.sep)); | ||
| } | ||
| function isPackageResolutionError(e) { | ||
| const code = e.code; | ||
| return (code === 'MODULE_NOT_FOUND' || code === 'ERR_PACKAGE_PATH_NOT_EXPORTED'); | ||
| } | ||
| function readPluginMainFromProjectConfiguration(plugin) { | ||
@@ -51,27 +82,40 @@ const { main } = Object.values(plugin.targets).find((x) => [ | ||
| let pluginPath; | ||
| let shouldRegisterTSTranspiler = false; | ||
| try { | ||
| pluginPath = require.resolve(moduleName, { | ||
| paths, | ||
| }); | ||
| const extension = path.extname(pluginPath); | ||
| shouldRegisterTSTranspiler = extension === '.ts'; | ||
| // Resolve local workspace plugins from source first so the workspace's | ||
| // `customConditions`/`development` exports condition wins over the built | ||
| // `dist` artifact that Node's resolver would otherwise pick up via the | ||
| // `default` condition (Node ignores TypeScript custom conditions). Skipped | ||
| // when `projects` weren't loaded — the caller already determined that the | ||
| // import isn't a workspace package. | ||
| const localPlugin = projects | ||
| ? resolveLocalNxPlugin(moduleName, projects, root) | ||
| : null; | ||
| if (localPlugin) { | ||
| pluginPath = tryResolveLocalPluginFromSource(moduleName, localPlugin, root); | ||
| if (!pluginPath && getSubpathOfLocalPackage(moduleName, localPlugin)) { | ||
| throwUnresolvableLocalPluginError(moduleName, localPlugin, root); | ||
| } | ||
| } | ||
| catch (e) { | ||
| if (e.code === 'MODULE_NOT_FOUND') { | ||
| const plugin = resolveLocalNxPlugin(moduleName, projects, root); | ||
| if (plugin) { | ||
| shouldRegisterTSTranspiler = true; | ||
| const main = readPluginMainFromProjectConfiguration(plugin.projectConfig); | ||
| pluginPath = main ? path.join(root, main) : plugin.path; | ||
| if (!pluginPath) { | ||
| try { | ||
| pluginPath = require.resolve(moduleName, { paths }); | ||
| } | ||
| catch (e) { | ||
| if (localPlugin && isPackageResolutionError(e)) { | ||
| throwUnresolvableLocalPluginError(moduleName, localPlugin, root); | ||
| } | ||
| else { | ||
| logger_1.logger.error(`Plugin listed in \`nx.json\` not found: ${moduleName}`); | ||
| if (e.code !== 'MODULE_NOT_FOUND') { | ||
| throw e; | ||
| } | ||
| } | ||
| else { | ||
| if (localPlugin) { | ||
| throwUnresolvableLocalPluginError(moduleName, localPlugin, root); | ||
| } | ||
| logger_1.logger.error(`Plugin listed in \`nx.json\` not found: ${moduleName}`); | ||
| throw e; | ||
| } | ||
| } | ||
| const ext = path.extname(pluginPath); | ||
| // Directory paths fall through to Node's `package.json` `main` resolution | ||
| // which may land on a TS file; only opt out of TS transpiler registration | ||
| // when the resolved path is unambiguously JS. | ||
| const shouldRegisterTSTranspiler = ext === '' || TS_SOURCE_EXTENSIONS.has(ext); | ||
| const packageJsonPath = path.join(pluginPath, 'package.json'); | ||
@@ -84,8 +128,100 @@ const { name } = !['.ts', '.js'].some((x) => path.extname(moduleName) === x) && // Not trying to point to a ts or js file | ||
| } | ||
| function getSubpathOfLocalPackage(moduleName, plugin) { | ||
| const packageName = plugin.projectConfig.metadata?.js?.packageName; | ||
| if (!packageName || !moduleName.startsWith(packageName + '/')) { | ||
| return null; | ||
| } | ||
| return '.' + moduleName.slice(packageName.length); | ||
| } | ||
| function tryResolveLocalPluginFromSource(moduleName, plugin, root) { | ||
| if (plugin.resolvedFile) { | ||
| return plugin.resolvedFile; | ||
| } | ||
| const subpath = getSubpathOfLocalPackage(moduleName, plugin); | ||
| if (subpath) { | ||
| return resolveSubpathFromExports(plugin.projectConfig, plugin.path, subpath, root); | ||
| } | ||
| const main = readPluginMainFromProjectConfiguration(plugin.projectConfig); | ||
| return main ? path.join(root, main) : null; | ||
| } | ||
| function throwUnresolvableLocalPluginError(moduleName, plugin, root) { | ||
| const subpath = getSubpathOfLocalPackage(moduleName, plugin); | ||
| const packageName = plugin.projectConfig.metadata?.js?.packageName; | ||
| if (subpath) { | ||
| const conditions = (0, typescript_1.getRootTsConfigCustomConditions)(root); | ||
| const exampleCondition = conditions[0] ?? 'development'; | ||
| throw new Error(`Unable to resolve local plugin "${moduleName}". The import targets ` + | ||
| `the subpath "${subpath}" of the local package "${packageName}", but ` + | ||
| `the package's "exports" entry for "${subpath}" does not declare a ` + | ||
| `resolvable source-pointing condition recognized by Nx. Add a custom ` + | ||
| `condition pointing to the source file for that subpath in ` + | ||
| `"${path.relative(root, path.join(plugin.path, 'package.json'))}", ` + | ||
| `e.g. "${subpath}": { "${exampleCondition}": "<path/to/source>" }, and ` + | ||
| `ensure the condition name is listed in ` + | ||
| `"compilerOptions.customConditions" in your root tsconfig.`); | ||
| } | ||
| throw new Error(`Unable to resolve local plugin "${moduleName}". The local package ` + | ||
| `"${packageName ?? moduleName}" does not declare a build target with ` + | ||
| `a "main" source path, and Node could not resolve it either.`); | ||
| } | ||
| function resolveSubpathFromExports(projectConfig, projectPath, subpath, root) { | ||
| const packageExports = projectConfig.metadata?.js?.packageExports; | ||
| if (!packageExports) { | ||
| return null; | ||
| } | ||
| const pkg = { | ||
| name: projectConfig.metadata.js.packageName, | ||
| exports: packageExports, | ||
| }; | ||
| try { | ||
| const matches = (0, resolve_exports_1.resolve)(pkg, subpath, { | ||
| conditions: (0, typescript_1.getRootTsConfigResolveExportsConditions)(root), | ||
| }); | ||
| if (!matches || !matches.length) { | ||
| return null; | ||
| } | ||
| // resolve.exports doesn't report which condition matched. If running with | ||
| // an empty conditions list (only `default`/`import`/`require` fallbacks) | ||
| // produces the same result, none of our source conditions matched — | ||
| // signal "no source-pointing condition" so the caller hard-fails instead | ||
| // of silently loading the package's built/dist target. | ||
| let fallbackMatches; | ||
| try { | ||
| fallbackMatches = (0, resolve_exports_1.resolve)(pkg, subpath, { conditions: [] }); | ||
| } | ||
| catch { } | ||
| if (fallbackMatches && | ||
| fallbackMatches.length && | ||
| fallbackMatches[0] === matches[0]) { | ||
| return null; | ||
| } | ||
| for (const match of matches) { | ||
| const candidate = path.join(projectPath, match); | ||
| if ((0, node_fs_1.existsSync)(candidate)) { | ||
| return candidate; | ||
| } | ||
| } | ||
| } | ||
| catch (e) { | ||
| logger_1.logger.verbose(`Failed to resolve subpath "${subpath}" of local plugin via package.json exports`, e); | ||
| } | ||
| return null; | ||
| } | ||
| function lookupLocalPlugin(importPath, projects, root = workspace_root_1.workspaceRoot) { | ||
| const projectConfig = findNxProjectForImportPath(importPath, projects, root); | ||
| if (!projectConfig) { | ||
| const match = findNxProjectForImportPath(importPath, projects, root); | ||
| if (!match) { | ||
| return null; | ||
| } | ||
| return { path: path.join(root, projectConfig.root), projectConfig }; | ||
| let resolvedFile; | ||
| if (match.tsPathFile) { | ||
| const candidate = path.join(root, match.tsPathFile); | ||
| if (path.extname(candidate) && (0, node_fs_1.existsSync)(candidate)) { | ||
| resolvedFile = candidate; | ||
| } | ||
| } | ||
| return { | ||
| path: path.join(root, match.projectConfig.root), | ||
| projectConfig: match.projectConfig, | ||
| resolvedFile, | ||
| }; | ||
| } | ||
@@ -108,3 +244,6 @@ let packageEntryPointsToProjectMap; | ||
| if (nxProject) { | ||
| return projectNameMap.get(nxProject); | ||
| return { | ||
| projectConfig: projectNameMap.get(nxProject), | ||
| tsPathFile: tsConfigPath, | ||
| }; | ||
| } | ||
@@ -120,10 +259,10 @@ } | ||
| if (packageEntryPointsToProjectMap[importPath]) { | ||
| return packageEntryPointsToProjectMap[importPath]; | ||
| return { projectConfig: packageEntryPointsToProjectMap[importPath] }; | ||
| } | ||
| const project = (0, packages_1.matchImportToWildcardEntryPointsToProjectMap)(wildcardEntryPointsToProjectMap, importPath); | ||
| if (project) { | ||
| return project; | ||
| return { projectConfig: project }; | ||
| } | ||
| logger_1.logger.verbose('Unable to find local plugin', possibleTsPaths, projectRootMappings); | ||
| throw new Error('Unable to resolve local plugin with import path ' + importPath); | ||
| return null; | ||
| } | ||
@@ -130,0 +269,0 @@ let tsconfigPaths; |
@@ -226,5 +226,5 @@ "use strict"; | ||
| canBatchTaskBeScheduled(task, batchTaskGraph) { | ||
| // task self needs to have parallelism true | ||
| // task self needs to support parallelism (undefined defaults to parallel) | ||
| // all deps have either completed or belong to the same batch | ||
| return (task.parallelism === true && | ||
| return (task.parallelism !== false && | ||
| this.taskGraph.dependencies[task.id].every((id) => this.completedTasks.has(id) || !!batchTaskGraph?.tasks[id])); | ||
@@ -252,3 +252,3 @@ } | ||
| // if all running tasks support parallelism, can only schedule task with parallelism | ||
| return this.taskGraph.tasks[taskId].parallelism === true; | ||
| return this.taskGraph.tasks[taskId].parallelism !== false; | ||
| } | ||
@@ -255,0 +255,0 @@ } |
+11
-11
| { | ||
| "name": "nx", | ||
| "version": "23.0.0-beta.16", | ||
| "version": "23.0.0-beta.17", | ||
| "private": false, | ||
@@ -173,12 +173,12 @@ "type": "commonjs", | ||
| "optionalDependencies": { | ||
| "@nx/nx-darwin-arm64": "23.0.0-beta.16", | ||
| "@nx/nx-darwin-x64": "23.0.0-beta.16", | ||
| "@nx/nx-freebsd-x64": "23.0.0-beta.16", | ||
| "@nx/nx-linux-arm-gnueabihf": "23.0.0-beta.16", | ||
| "@nx/nx-linux-arm64-gnu": "23.0.0-beta.16", | ||
| "@nx/nx-linux-arm64-musl": "23.0.0-beta.16", | ||
| "@nx/nx-linux-x64-gnu": "23.0.0-beta.16", | ||
| "@nx/nx-linux-x64-musl": "23.0.0-beta.16", | ||
| "@nx/nx-win32-arm64-msvc": "23.0.0-beta.16", | ||
| "@nx/nx-win32-x64-msvc": "23.0.0-beta.16" | ||
| "@nx/nx-darwin-arm64": "23.0.0-beta.17", | ||
| "@nx/nx-darwin-x64": "23.0.0-beta.17", | ||
| "@nx/nx-freebsd-x64": "23.0.0-beta.17", | ||
| "@nx/nx-linux-arm-gnueabihf": "23.0.0-beta.17", | ||
| "@nx/nx-linux-arm64-gnu": "23.0.0-beta.17", | ||
| "@nx/nx-linux-arm64-musl": "23.0.0-beta.17", | ||
| "@nx/nx-linux-x64-gnu": "23.0.0-beta.17", | ||
| "@nx/nx-linux-x64-musl": "23.0.0-beta.17", | ||
| "@nx/nx-win32-arm64-msvc": "23.0.0-beta.17", | ||
| "@nx/nx-win32-x64-msvc": "23.0.0-beta.17" | ||
| }, | ||
@@ -185,0 +185,0 @@ "nx-migrations": { |
| import type { Scanner } from 'typescript'; | ||
| /** | ||
| * @deprecated This is deprecated and will be removed in Nx 20. | ||
| * This was not intended to be exposed. | ||
| * Please talk to us if you need this. | ||
| */ | ||
| export declare function stripSourceCode(scanner: Scanner, contents: string): string; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.stripSourceCode = stripSourceCode; | ||
| let SyntaxKind; | ||
| /** | ||
| * @deprecated This is deprecated and will be removed in Nx 20. | ||
| * This was not intended to be exposed. | ||
| * Please talk to us if you need this. | ||
| */ | ||
| function stripSourceCode(scanner, contents) { | ||
| if (!SyntaxKind) { | ||
| SyntaxKind = require('typescript').SyntaxKind; | ||
| } | ||
| if (contents.indexOf('loadChildren') > -1) { | ||
| return contents; | ||
| } | ||
| scanner.setText(contents); | ||
| let token = scanner.scan(); | ||
| let lastNonTriviaToken = SyntaxKind.Unknown; | ||
| const statements = []; | ||
| const templateStack = []; | ||
| let ignoringLine = false; | ||
| let braceDepth = 0; | ||
| let start = null; | ||
| while (token !== SyntaxKind.EndOfFileToken) { | ||
| const currentToken = token; | ||
| const potentialStart = scanner.getStartPos(); | ||
| switch (token) { | ||
| case SyntaxKind.MultiLineCommentTrivia: | ||
| case SyntaxKind.SingleLineCommentTrivia: { | ||
| const isMultiLineCommentTrivia = token === SyntaxKind.MultiLineCommentTrivia; | ||
| const start = potentialStart + 2; | ||
| token = scanner.scan(); | ||
| const end = scanner.getStartPos() - (isMultiLineCommentTrivia ? 2 : 0); | ||
| const comment = contents.substring(start, end).trim(); | ||
| if (comment === 'nx-ignore-next-line') { | ||
| // reading till the end of the line | ||
| while (token === SyntaxKind.WhitespaceTrivia || | ||
| token === SyntaxKind.NewLineTrivia) { | ||
| token = scanner.scan(); | ||
| } | ||
| ignoringLine = true; | ||
| } | ||
| break; | ||
| } | ||
| case SyntaxKind.NewLineTrivia: { | ||
| ignoringLine = false; | ||
| token = scanner.scan(); | ||
| break; | ||
| } | ||
| case SyntaxKind.RequireKeyword: | ||
| case SyntaxKind.ImportKeyword: { | ||
| token = scanner.scan(); | ||
| if (ignoringLine) { | ||
| break; | ||
| } | ||
| while (token === SyntaxKind.WhitespaceTrivia || | ||
| token === SyntaxKind.NewLineTrivia) { | ||
| token = scanner.scan(); | ||
| } | ||
| start = potentialStart; | ||
| break; | ||
| } | ||
| case SyntaxKind.TemplateHead: { | ||
| templateStack.push(braceDepth); | ||
| braceDepth = 0; | ||
| token = scanner.scan(); | ||
| break; | ||
| } | ||
| case SyntaxKind.SlashToken: { | ||
| if (shouldRescanSlashToken(lastNonTriviaToken)) { | ||
| token = scanner.reScanSlashToken(); | ||
| } | ||
| token = scanner.scan(); | ||
| break; | ||
| } | ||
| case SyntaxKind.OpenBraceToken: { | ||
| ++braceDepth; | ||
| token = scanner.scan(); | ||
| break; | ||
| } | ||
| case SyntaxKind.CloseBraceToken: { | ||
| if (braceDepth) { | ||
| --braceDepth; | ||
| } | ||
| else if (templateStack.length) { | ||
| token = scanner.reScanTemplateToken(false); | ||
| if (token === SyntaxKind.LastTemplateToken) { | ||
| braceDepth = templateStack.pop(); | ||
| } | ||
| } | ||
| token = scanner.scan(); | ||
| break; | ||
| } | ||
| case SyntaxKind.ExportKeyword: { | ||
| token = scanner.scan(); | ||
| if (ignoringLine) { | ||
| break; | ||
| } | ||
| while (token === SyntaxKind.WhitespaceTrivia || | ||
| token === SyntaxKind.NewLineTrivia) { | ||
| token = scanner.scan(); | ||
| } | ||
| if (token === SyntaxKind.OpenBraceToken || | ||
| token === SyntaxKind.AsteriskToken || | ||
| token === SyntaxKind.TypeKeyword) { | ||
| start = potentialStart; | ||
| } | ||
| break; | ||
| } | ||
| case SyntaxKind.StringLiteral: { | ||
| if (start !== null) { | ||
| token = scanner.scan(); | ||
| if (token === SyntaxKind.CloseParenToken) { | ||
| token = scanner.scan(); | ||
| } | ||
| const end = scanner.getStartPos(); | ||
| statements.push(contents.substring(start, end)); | ||
| start = null; | ||
| } | ||
| else { | ||
| token = scanner.scan(); | ||
| } | ||
| break; | ||
| } | ||
| default: { | ||
| token = scanner.scan(); | ||
| } | ||
| } | ||
| if (currentToken > SyntaxKind.LastTriviaToken) { | ||
| lastNonTriviaToken = currentToken; | ||
| } | ||
| } | ||
| return statements.join('\n'); | ||
| } | ||
| function shouldRescanSlashToken(lastNonTriviaToken) { | ||
| switch (lastNonTriviaToken) { | ||
| case SyntaxKind.Identifier: | ||
| case SyntaxKind.StringLiteral: | ||
| case SyntaxKind.NumericLiteral: | ||
| case SyntaxKind.BigIntLiteral: | ||
| case SyntaxKind.RegularExpressionLiteral: | ||
| case SyntaxKind.ThisKeyword: | ||
| case SyntaxKind.PlusPlusToken: | ||
| case SyntaxKind.MinusMinusToken: | ||
| case SyntaxKind.CloseParenToken: | ||
| case SyntaxKind.CloseBracketToken: | ||
| case SyntaxKind.CloseBraceToken: | ||
| case SyntaxKind.TrueKeyword: | ||
| case SyntaxKind.FalseKeyword: | ||
| return false; | ||
| default: | ||
| return true; | ||
| } | ||
| } |
| import { DependencyType } from '../../../../config/project-graph'; | ||
| /** | ||
| * @deprecated This is deprecated and will be removed in Nx 20. | ||
| * This was not intended to be exposed. | ||
| * Please talk to us if you need this. | ||
| */ | ||
| export declare class TypeScriptImportLocator { | ||
| private readonly scanner; | ||
| constructor(); | ||
| fromFile(filePath: string, visitor: (importExpr: string, filePath: string, type: DependencyType) => void): void; | ||
| fromNode(filePath: string, node: any, visitor: (importExpr: string, filePath: string, type: DependencyType) => void): void; | ||
| private ignoreStatement; | ||
| private ignoreLoadChildrenDependency; | ||
| private getPropertyAssignmentName; | ||
| private getStringLiteralValue; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.TypeScriptImportLocator = void 0; | ||
| const tslib_1 = require("tslib"); | ||
| const path = tslib_1.__importStar(require("path")); | ||
| const project_graph_1 = require("../../../../config/project-graph"); | ||
| const file_utils_1 = require("../../../../project-graph/file-utils"); | ||
| const strip_source_code_1 = require("./strip-source-code"); | ||
| let tsModule; | ||
| /** | ||
| * @deprecated This is deprecated and will be removed in Nx 20. | ||
| * This was not intended to be exposed. | ||
| * Please talk to us if you need this. | ||
| */ | ||
| class TypeScriptImportLocator { | ||
| constructor() { | ||
| tsModule = require('typescript'); | ||
| this.scanner = tsModule.createScanner(tsModule.ScriptTarget.Latest, false); | ||
| } | ||
| fromFile(filePath, visitor) { | ||
| const extension = path.extname(filePath); | ||
| if (extension !== '.ts' && | ||
| extension !== '.mts' && | ||
| extension !== '.tsx' && | ||
| extension !== '.js' && | ||
| extension !== '.mjs' && | ||
| extension !== '.jsx') { | ||
| return; | ||
| } | ||
| const content = (0, file_utils_1.defaultFileRead)(filePath); | ||
| const strippedContent = (0, strip_source_code_1.stripSourceCode)(this.scanner, content); | ||
| if (strippedContent !== '') { | ||
| const tsFile = tsModule.createSourceFile(filePath, strippedContent, tsModule.ScriptTarget.Latest, true); | ||
| this.fromNode(filePath, tsFile, visitor); | ||
| } | ||
| } | ||
| fromNode(filePath, node, visitor) { | ||
| if (tsModule.isImportDeclaration(node) || | ||
| (tsModule.isExportDeclaration(node) && node.moduleSpecifier)) { | ||
| if (!this.ignoreStatement(node)) { | ||
| const imp = this.getStringLiteralValue(node.moduleSpecifier); | ||
| visitor(imp, filePath, project_graph_1.DependencyType.static); | ||
| } | ||
| return; // stop traversing downwards | ||
| } | ||
| if (tsModule.isCallExpression(node) && | ||
| node.expression.kind === tsModule.SyntaxKind.ImportKeyword && | ||
| node.arguments.length === 1 && | ||
| tsModule.isStringLiteral(node.arguments[0])) { | ||
| if (!this.ignoreStatement(node)) { | ||
| const imp = this.getStringLiteralValue(node.arguments[0]); | ||
| visitor(imp, filePath, project_graph_1.DependencyType.dynamic); | ||
| } | ||
| return; | ||
| } | ||
| if (tsModule.isCallExpression(node) && | ||
| node.expression.getText() === 'require' && | ||
| node.arguments.length === 1 && | ||
| tsModule.isStringLiteral(node.arguments[0])) { | ||
| if (!this.ignoreStatement(node)) { | ||
| const imp = this.getStringLiteralValue(node.arguments[0]); | ||
| visitor(imp, filePath, project_graph_1.DependencyType.static); | ||
| } | ||
| return; | ||
| } | ||
| if (node.kind === tsModule.SyntaxKind.PropertyAssignment) { | ||
| const name = this.getPropertyAssignmentName(node.name); | ||
| if (name === 'loadChildren') { | ||
| const init = node.initializer; | ||
| if (init.kind === tsModule.SyntaxKind.StringLiteral && | ||
| !this.ignoreLoadChildrenDependency(node.getFullText())) { | ||
| const childrenExpr = this.getStringLiteralValue(init); | ||
| visitor(childrenExpr, filePath, project_graph_1.DependencyType.dynamic); | ||
| return; // stop traversing downwards | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Continue traversing down the AST from the current node | ||
| */ | ||
| tsModule.forEachChild(node, (child) => this.fromNode(filePath, child, visitor)); | ||
| } | ||
| ignoreStatement(node) { | ||
| return (0, strip_source_code_1.stripSourceCode)(this.scanner, node.getFullText()) === ''; | ||
| } | ||
| ignoreLoadChildrenDependency(contents) { | ||
| this.scanner.setText(contents); | ||
| let token = this.scanner.scan(); | ||
| while (token !== tsModule.SyntaxKind.EndOfFileToken) { | ||
| if (token === tsModule.SyntaxKind.SingleLineCommentTrivia || | ||
| token === tsModule.SyntaxKind.MultiLineCommentTrivia) { | ||
| const start = this.scanner.getStartPos() + 2; | ||
| token = this.scanner.scan(); | ||
| const isMultiLineCommentTrivia = token === tsModule.SyntaxKind.MultiLineCommentTrivia; | ||
| const end = this.scanner.getStartPos() - (isMultiLineCommentTrivia ? 2 : 0); | ||
| const comment = contents.substring(start, end).trim(); | ||
| if (comment === 'nx-ignore-next-line') { | ||
| return true; | ||
| } | ||
| } | ||
| else { | ||
| token = this.scanner.scan(); | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| getPropertyAssignmentName(nameNode) { | ||
| switch (nameNode.kind) { | ||
| case tsModule.SyntaxKind.Identifier: | ||
| return nameNode.getText(); | ||
| case tsModule.SyntaxKind.StringLiteral: | ||
| return nameNode.text; | ||
| default: | ||
| return null; | ||
| } | ||
| } | ||
| getStringLiteralValue(node) { | ||
| return node.getText().slice(1, -1); | ||
| } | ||
| } | ||
| exports.TypeScriptImportLocator = TypeScriptImportLocator; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 3 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 147 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 3 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 147 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
16350940
-0.03%1107
-0.36%87901
-0.15%781
0.51%