UNPKG

@nx/js

Version:

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

186 lines (185 loc) • 8.23 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; } else if ((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 packageJsonPath = (0, path_1.join)(workspaceRoot, resolvedProjectPath, '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); const projectAbsolutePath = (0, node_path_1.resolve)(workspaceRoot, resolvedProjectPath); // Handle outFile first (has precedence over outDir) if (tsConfig.options.outFile) { const outFile = (0, node_path_1.resolve)(workspaceRoot, resolvedProjectPath, 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 isPathSourceFile = (path) => { const normalizedPath = (0, node_path_1.isAbsolute)(path) ? (0, node_path_1.resolve)(workspaceRoot, path.startsWith('/') ? path.slice(1) : path) : (0, node_path_1.resolve)(workspaceRoot, resolvedProjectPath, path); // For outFile case, check if path points to the specific outFile return normalizedPath === outFile; }; // Check if any entry points match the outFile const exports = packageJson?.exports; if (exports) { if (typeof exports === 'string') { return !isPathSourceFile(exports); } if (typeof exports === 'object' && '.' in exports) { const dotExport = exports['.']; if (typeof dotExport === 'string') { return !isPathSourceFile(dotExport); } else if (typeof dotExport === 'object') { const hasMatch = Object.entries(dotExport).some(([key, value]) => { if (key === 'types' || key === 'development') return false; return typeof value === 'string' && isPathSourceFile(value); }); return !hasMatch; } } } const buildPaths = ['main', 'module']; for (const field of buildPaths) { if (packageJson[field] && isPathSourceFile(packageJson[field])) { return false; } } return true; } // Handle outDir const outDir = tsConfig.options.outDir; let resolvedOutDir; if (outDir) { const potentialOutDir = (0, node_path_1.resolve)(workspaceRoot, resolvedProjectPath, outDir); const relativePath = (0, node_path_1.relative)(projectAbsolutePath, potentialOutDir); // If outDir is outside project root: buildable if (relativePath.startsWith('..')) { return true; } // If outDir is inside project root, then we should check entry points if (!relativePath.startsWith('..')) { resolvedOutDir = potentialOutDir; } } const isPathSourceFile = (path) => { const normalizedPath = (0, node_path_1.isAbsolute)(path) ? (0, node_path_1.resolve)(workspaceRoot, path.startsWith('/') ? path.slice(1) : path) : (0, node_path_1.resolve)(workspaceRoot, resolvedProjectPath, path); if (resolvedOutDir) { // If the path is within the outDir, we assume it's not a source file. const relativePath = (0, node_path_1.relative)(resolvedOutDir, normalizedPath); if (!relativePath.startsWith('..')) { return false; } } // If no include patterns, TypeScript includes all TS files by default const include = tsConfig.raw?.include; if (!include || !Array.isArray(include)) { const ext = (0, node_path_1.extname)(path); const tsExtensions = ['.ts', '.tsx', '.cts', '.mts']; if (tsExtensions.includes(ext)) { return true; } // If include is not defined and it's not a TS file, assume it's not a source file return false; } const projectAbsolutePath = (0, node_path_1.resolve)(workspaceRoot, resolvedProjectPath); const relativeToProject = (0, node_path_1.relative)(projectAbsolutePath, normalizedPath); for (const pattern of include) { if (picomatch(pattern)(relativeToProject)) { return true; } } return false; }; const containsInvalidPath = (value) => { if (typeof value === 'string') { return isPathSourceFile(value); } else if (typeof value === 'object') { return Object.entries(value).some(([currentKey, subValue]) => { // Skip types and development conditions if (currentKey === 'types' || currentKey === 'development') { return false; } if (typeof subValue === 'string') { return isPathSourceFile(subValue); } return false; }); } return false; }; const exports = packageJson?.exports; // Check the `.` export if `exports` is defined. if (exports) { if (typeof exports === 'string') { return !isPathSourceFile(exports); } if (typeof exports === 'object' && '.' in exports) { return !containsInvalidPath(exports['.']); } // Check other exports if `.` is not defined or valid. for (const key in exports) { if (key !== '.' && containsInvalidPath(exports[key])) { return false; } } return true; } // If `exports` is not defined, fallback to `main` and `module` fields. const buildPaths = ['main', 'module']; for (const field of buildPaths) { if (packageJson[field] && isPathSourceFile(packageJson[field])) { return false; } } return true; }