UNPKG

@nx/js

Version:

The JS plugin for Nx contains executors and generators that provide the best experience for developing JavaScript and TypeScript projects.

194 lines (193 loc) • 9.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.addBuildAndWatchDepsTargets = addBuildAndWatchDepsTargets; exports.isValidPackageJsonBuildConfig = isValidPackageJsonBuildConfig; const devkit_1 = require("@nx/devkit"); const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); const path_1 = require("path"); const picomatch = require("picomatch"); /** * Allow uses that use incremental builds to run `nx watch-deps` to continuously build all dependencies. */ function addBuildAndWatchDepsTargets(workspaceRoot, projectRoot, targets, options, pmc) { let projectName; const projectJsonPath = (0, path_1.join)(workspaceRoot, projectRoot, 'project.json'); const packageJsonPath = (0, path_1.join)(workspaceRoot, projectRoot, 'package.json'); if ((0, node_fs_1.existsSync)(projectJsonPath)) { const projectJson = (0, devkit_1.readJsonFile)(projectJsonPath); projectName = projectJson.name; } if (!projectName && (0, node_fs_1.existsSync)(packageJsonPath)) { const packageJson = (0, devkit_1.readJsonFile)(packageJsonPath); projectName = packageJson.nx?.name ?? packageJson.name; } if (!projectName) return; if (projectName) { const buildDepsTargetName = options.buildDepsTargetName ?? 'build-deps'; targets[buildDepsTargetName] = { dependsOn: ['^build'], }; targets[options.watchDepsTargetName ?? 'watch-deps'] = { continuous: true, dependsOn: [buildDepsTargetName], command: `${pmc.exec} nx watch --projects ${projectName} --includeDependentProjects -- ${pmc.exec} nx ${buildDepsTargetName} ${projectName}`, }; } } function isValidPackageJsonBuildConfig(tsConfig, workspaceRoot, projectRoot) { const resolvedProjectPath = (0, node_path_1.isAbsolute)(projectRoot) ? (0, node_path_1.relative)(workspaceRoot, projectRoot) : projectRoot; const projectAbsolutePath = (0, node_path_1.resolve)(workspaceRoot, resolvedProjectPath); const packageJsonPath = (0, path_1.join)(projectAbsolutePath, 'package.json'); if (!(0, node_fs_1.existsSync)(packageJsonPath)) { // If the package.json file does not exist. // Assume it's valid because it would be using `project.json` instead. return true; } const packageJson = (0, devkit_1.readJsonFile)(packageJsonPath); // Check entry points against outFile (has precedence over outDir) if (tsConfig.options.outFile) { return isAnyEntryPointPointingToOutFile(packageJson, tsConfig, projectAbsolutePath, workspaceRoot); } // Check entry points against outDir const outDir = tsConfig.options.outDir; if (outDir) { return isAnyEntryPointPointingToOutDir(packageJson, outDir, workspaceRoot, projectAbsolutePath); } // Check entry points against include patterns return isAnyEntryPointPointingToNonIncludedFiles(packageJson, tsConfig, workspaceRoot, projectAbsolutePath); } const packageJsonLegacyEntryPoints = ['main', 'module']; function isAnyEntryPointPointingToOutFile(packageJson, tsConfig, projectAbsolutePath, workspaceRoot) { const outFile = (0, node_path_1.resolve)(projectAbsolutePath, tsConfig.options.outFile); const relativeToProject = (0, node_path_1.relative)(projectAbsolutePath, outFile); // If outFile is outside project root: buildable if (relativeToProject.startsWith('..')) { return true; } // If outFile is inside project root: check if entry points point to outFile const isPathPointingToOutputFile = (path) => { return normalizePath(path, workspaceRoot, projectAbsolutePath) === outFile; }; const exports = packageJson.exports; if (!exports) { // If any entry point points to the outFile, or if no entry points are // defined (Node.js falls back to `./index.js`), then it's buildable return (packageJsonLegacyEntryPoints.some((field) => packageJson[field] && isPathPointingToOutputFile(packageJson[field])) || packageJsonLegacyEntryPoints.every((field) => !packageJson[field])); } if (typeof exports === 'string') { return isPathPointingToOutputFile(exports); } const isExportsEntryPointingToOutFile = (value) => { if (typeof value === 'string') { return isPathPointingToOutputFile(value); } if (typeof value === 'object') { return Object.values(value).some((subValue) => typeof subValue === 'string' && isPathPointingToOutputFile(subValue)); } return false; }; if ('.' in exports && exports['.'] !== null) { return isExportsEntryPointingToOutFile(exports['.']); } // If any export is pointing to a path inside the outDir, then it's buildable return Object.keys(exports).some((key) => key !== '.' && isExportsEntryPointingToOutFile(exports[key])); } function isAnyEntryPointPointingToOutDir(packageJson, outDir, workspaceRoot, projectAbsolutePath) { const resolvedOutDir = (0, node_path_1.resolve)(projectAbsolutePath, outDir); const relativePath = (0, node_path_1.relative)(projectAbsolutePath, resolvedOutDir); // If outDir is outside project root: buildable if (relativePath.startsWith('..')) { return true; } const isPathInsideOutDir = (path) => { const normalizedPath = normalizePath(path, workspaceRoot, projectAbsolutePath); return !(0, node_path_1.relative)(resolvedOutDir, normalizedPath).startsWith('..'); }; const exports = packageJson.exports; if (!exports) { // If any entry point points to a path inside the outDir, or if no entry points // are defined (Node.js falls back to `./index.js`), then it's buildable return (packageJsonLegacyEntryPoints.some((field) => packageJson[field] && packageJson[field] !== './package.json' && isPathInsideOutDir(packageJson[field])) || packageJsonLegacyEntryPoints.every((field) => !packageJson[field])); } if (typeof exports === 'string') { return isPathInsideOutDir(exports); } const isExportsEntryPointingToPathInsideOutDir = (value) => { if (typeof value === 'string') { return isPathInsideOutDir(value); } if (typeof value === 'object') { return Object.values(value).some((subValue) => typeof subValue === 'string' && isPathInsideOutDir(subValue)); } return false; }; if ('.' in exports && exports['.'] !== null) { return isExportsEntryPointingToPathInsideOutDir(exports['.']); } // If any export is pointing to a path inside the outDir, then it's buildable return Object.keys(exports).some((key) => key !== '.' && key !== './package.json' && isExportsEntryPointingToPathInsideOutDir(exports[key])); } function isAnyEntryPointPointingToNonIncludedFiles(packageJson, tsConfig, workspaceRoot, projectAbsolutePath) { const isPathSourceFile = (path) => { const normalizedPath = normalizePath(path, workspaceRoot, projectAbsolutePath); const files = tsConfig.raw?.files; const include = tsConfig.raw?.include; if (Array.isArray(files)) { const match = files.find((file) => normalizePath(file, workspaceRoot, projectAbsolutePath) === normalizedPath); if (match) { return true; } } // If not matched by `files`, check `include` patterns if (!Array.isArray(include)) { // If no include patterns, TypeScript includes all TS files by default const ext = (0, node_path_1.extname)(path); const tsExtensions = ['.ts', '.tsx', '.cts', '.mts']; return tsExtensions.includes(ext); } const relativeToProject = (0, node_path_1.relative)(projectAbsolutePath, normalizedPath); return include.some((pattern) => picomatch(pattern)(relativeToProject)); }; // Check the `.` export if `exports` is defined. const exports = packageJson.exports; if (!exports) { // If any entry point doesn't point to a source file, or if no entry points // are defined (Node.js falls back to `./index.js`), then it's buildable return (packageJsonLegacyEntryPoints.some((field) => packageJson[field] && !isPathSourceFile(packageJson[field])) || packageJsonLegacyEntryPoints.every((field) => !packageJson[field])); } if (typeof exports === 'string') { return !isPathSourceFile(exports); } const isExportsEntryPointingToSource = (value) => { if (typeof value === 'string') { return isPathSourceFile(value); } if (typeof value === 'object') { // entry point point to a source file if all conditions that are not set // to `null` point to a source file return Object.values(value).every((subValue) => typeof subValue !== 'string' || isPathSourceFile(subValue)); } return false; }; if ('.' in exports && exports['.'] !== null) { return !isExportsEntryPointingToSource(exports['.']); } // If any export is not pointing to a source file, then it's buildable return Object.keys(exports).some((key) => key !== '.' && key !== './package.json' && !isExportsEntryPointingToSource(exports[key])); } function normalizePath(path, workspaceRoot, projectAbsolutePath) { return (0, node_path_1.isAbsolute)(path) ? (0, node_path_1.resolve)(workspaceRoot, path.startsWith('/') ? path.slice(1) : path) : (0, node_path_1.resolve)(projectAbsolutePath, path); }