UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

208 lines (207 loc) • 9.77 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createNodesV2 = void 0; exports.buildPackageJsonWorkspacesMatcher = buildPackageJsonWorkspacesMatcher; exports.createNodeFromPackageJson = createNodeFromPackageJson; exports.buildProjectConfigurationFromPackageJson = buildProjectConfigurationFromPackageJson; exports.getGlobPatternsFromPackageManagerWorkspaces = getGlobPatternsFromPackageManagerWorkspaces; const minimatch_1 = require("minimatch"); const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); const nx_json_1 = require("../../config/nx-json"); const to_project_name_1 = require("../../config/to-project-name"); const fileutils_1 = require("../../utils/fileutils"); const globs_1 = require("../../utils/globs"); const logger_1 = require("../../utils/logger"); const output_1 = require("../../utils/output"); const package_json_1 = require("../../utils/package-json"); const path_1 = require("../../utils/path"); const plugins_1 = require("../../project-graph/plugins"); const path_2 = require("path"); const file_hasher_1 = require("../../hasher/file-hasher"); const package_json_2 = require("../../../plugins/package-json"); exports.createNodesV2 = [ (0, globs_1.combineGlobPatterns)('package.json', '**/package.json', 'project.json', '**/project.json'), (configFiles, _, context) => { const { packageJsons, projectJsonRoots } = splitConfigFiles(configFiles); const readJson = (f) => (0, fileutils_1.readJsonFile)((0, node_path_1.join)(context.workspaceRoot, f)); const isInPackageJsonWorkspaces = buildPackageJsonWorkspacesMatcher(context.workspaceRoot, readJson); const isNextToProjectJson = (packageJsonPath) => { return projectJsonRoots.has((0, node_path_1.dirname)(packageJsonPath)); }; const cache = (0, package_json_2.readPackageJsonConfigurationCache)(); return (0, plugins_1.createNodesFromFiles)((packageJsonPath, options, context) => { const isInPackageManagerWorkspaces = isInPackageJsonWorkspaces(packageJsonPath); if (!isInPackageManagerWorkspaces && !isNextToProjectJson(packageJsonPath)) { // Skip if package.json is not part of the package.json workspaces and not next to a project.json. return null; } return createNodeFromPackageJson(packageJsonPath, context.workspaceRoot, cache, isInPackageManagerWorkspaces); }, packageJsons, _, context); }, ]; function splitConfigFiles(configFiles) { const packageJsons = []; const projectJsonRoots = new Set(); for (const configFile of configFiles) { if ((0, path_2.basename)(configFile) === 'package.json') { packageJsons.push(configFile); } else { projectJsonRoots.add((0, node_path_1.dirname)(configFile)); } } return { packageJsons, projectJsonRoots }; } function buildPackageJsonWorkspacesMatcher(workspaceRoot, readJson) { const patterns = getGlobPatternsFromPackageManagerWorkspaces(workspaceRoot, readJson); const negativePatterns = patterns.filter((p) => p.startsWith('!')); const positivePatterns = patterns.filter((p) => !p.startsWith('!')); if ( // There are some negative patterns negativePatterns.length > 0 && // No positive patterns (positivePatterns.length === 0 || // Or only a single positive pattern that is the default coming from root package (positivePatterns.length === 1 && positivePatterns[0] === 'package.json'))) { positivePatterns.push('**/package.json'); } return (p) => positivePatterns.some((positive) => (0, minimatch_1.minimatch)(p, positive)) && /** * minimatch will return true if the given p is NOT excluded by the negative pattern. * * For example if the negative pattern is "!packages/vite", then the given p "packages/vite" will return false, * the given p "packages/something-else/package.json" will return true. * * Therefore, we need to ensure that every negative pattern returns true to validate that the given p is not * excluded by any of the negative patterns. */ negativePatterns.every((negative) => (0, minimatch_1.minimatch)(p, negative)); } function createNodeFromPackageJson(pkgJsonPath, workspaceRoot, cache, isInPackageManagerWorkspaces) { const json = (0, fileutils_1.readJsonFile)((0, node_path_1.join)(workspaceRoot, pkgJsonPath)); const projectRoot = (0, node_path_1.dirname)(pkgJsonPath); const hash = (0, file_hasher_1.hashObject)({ ...json, root: projectRoot, isInPackageManagerWorkspaces, }); const cached = cache[hash]; if (cached) { return { projects: { [cached.root]: cached, }, }; } const project = buildProjectConfigurationFromPackageJson(json, workspaceRoot, pkgJsonPath, (0, nx_json_1.readNxJson)(workspaceRoot), isInPackageManagerWorkspaces); cache[hash] = project; return { projects: { [project.root]: project, }, }; } function buildProjectConfigurationFromPackageJson(packageJson, workspaceRoot, packageJsonPath, nxJson, isInPackageManagerWorkspaces) { const normalizedPath = packageJsonPath.split('\\').join('/'); const projectRoot = (0, node_path_1.dirname)(normalizedPath); const siblingProjectJson = tryReadJson((0, node_path_1.join)(workspaceRoot, projectRoot, 'project.json')); if (siblingProjectJson) { for (const target of Object.keys(siblingProjectJson?.targets ?? {})) { const { executor, command, options } = siblingProjectJson.targets[target]; if ( // will use run-commands, different target command || // Either uses a different executor or runs a different script (executor && (executor !== 'nx:run-script' || options?.script !== target))) { delete packageJson.scripts?.[target]; } } } if (!packageJson.name && projectRoot === '.') { throw new Error('Nx requires the root package.json to specify a name if it is being used as an Nx project.'); } let name = packageJson.name ?? (0, to_project_name_1.toProjectName)(normalizedPath); const projectConfiguration = { root: projectRoot, sourceRoot: projectRoot, name, ...packageJson.nx, targets: (0, package_json_1.readTargetsFromPackageJson)(packageJson, nxJson), tags: (0, package_json_1.getTagsFromPackageJson)(packageJson), metadata: (0, package_json_1.getMetadataFromPackageJson)(packageJson, isInPackageManagerWorkspaces), }; if (nxJson?.workspaceLayout?.appsDir != nxJson?.workspaceLayout?.libsDir && nxJson?.workspaceLayout?.appsDir && projectRoot.startsWith(nxJson.workspaceLayout.appsDir)) { projectConfiguration.projectType = 'application'; } else if (typeof nxJson?.workspaceLayout?.libsDir !== 'undefined' && projectRoot.startsWith(nxJson.workspaceLayout.libsDir)) { projectConfiguration.projectType = 'library'; } return projectConfiguration; } /** * Get the package.json globs from package manager workspaces */ function getGlobPatternsFromPackageManagerWorkspaces(root, // allow overwriting these args so we can use them in devkit readJson = (path) => (0, fileutils_1.readJsonFile)((0, node_path_1.join)(root, path)), readYaml = (path) => (0, fileutils_1.readYamlFile)((0, node_path_1.join)(root, path)), exists = (p) => (0, node_fs_1.existsSync)((0, node_path_1.join)(root, p))) { try { const patterns = []; const packageJson = readJson('package.json'); patterns.push(...normalizePatterns(Array.isArray(packageJson.workspaces) ? packageJson.workspaces : packageJson.workspaces?.packages ?? [])); if (exists('pnpm-workspace.yaml')) { try { const { packages } = readYaml('pnpm-workspace.yaml') ?? {}; patterns.push(...normalizePatterns(packages || [])); } catch (e) { output_1.output.warn({ title: `${logger_1.NX_PREFIX} Unable to parse pnpm-workspace.yaml`, bodyLines: [e.toString()], }); } } if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, 'lerna.json'))) { try { const { packages } = readJson('lerna.json') ?? {}; patterns.push(...normalizePatterns(packages?.length > 0 ? packages : ['packages/*'])); } catch (e) { output_1.output.warn({ title: `${logger_1.NX_PREFIX} Unable to parse lerna.json`, bodyLines: [e.toString()], }); } } // Merge patterns from workspaces definitions // TODO(@AgentEnder): update logic after better way to determine root project inclusion // Include the root project return packageJson.nx ? patterns.concat('package.json') : patterns; } catch { return []; } } function normalizePatterns(patterns) { return patterns.map((pattern) => removeRelativePath(pattern.endsWith('/package.json') ? pattern : (0, path_1.joinPathFragments)(pattern, 'package.json'))); } function removeRelativePath(pattern) { return pattern.startsWith('./') ? pattern.substring(2) : pattern; } function tryReadJson(path) { try { return (0, fileutils_1.readJsonFile)(path); } catch { return null; } }