UNPKG

@nx/js

Version:

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

215 lines (214 loc) • 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isUsingTypeScriptPlugin = isUsingTypeScriptPlugin; exports.isUsingTsSolutionSetup = isUsingTsSolutionSetup; exports.assertNotUsingTsSolutionSetup = assertNotUsingTsSolutionSetup; exports.findRuntimeTsConfigName = findRuntimeTsConfigName; exports.updateTsconfigFiles = updateTsconfigFiles; exports.addProjectToTsSolutionWorkspace = addProjectToTsSolutionWorkspace; exports.getProjectType = getProjectType; exports.getProjectSourceRoot = getProjectSourceRoot; const devkit_1 = require("@nx/devkit"); const posix_1 = require("node:path/posix"); const tree_1 = require("nx/src/generators/tree"); const package_manager_workspaces_1 = require("../package-manager-workspaces"); const configuration_1 = require("./configuration"); function isUsingTypeScriptPlugin(tree) { const nxJson = (0, devkit_1.readNxJson)(tree); return (nxJson?.plugins?.some((p) => typeof p === 'string' ? p === '@nx/js/typescript' : p.plugin === '@nx/js/typescript') ?? false); } function isUsingTsSolutionSetup(tree) { tree ??= new tree_1.FsTree(devkit_1.workspaceRoot, false); return ((0, package_manager_workspaces_1.isUsingPackageManagerWorkspaces)(tree) && isWorkspaceSetupWithTsSolution(tree)); } function isWorkspaceSetupWithTsSolution(tree) { if (!tree.exists('tsconfig.base.json') || !tree.exists('tsconfig.json')) { return false; } const tsconfigJson = (0, devkit_1.readJson)(tree, 'tsconfig.json'); if (tsconfigJson.extends !== './tsconfig.base.json') { return false; } /** * New setup: * - `files` is defined and set to an empty array * - `references` is defined and set to an empty array * - `include` is not defined or is set to an empty array */ if (!tsconfigJson.files || tsconfigJson.files.length > 0 || !tsconfigJson.references || !!tsconfigJson.include?.length) { return false; } const baseTsconfigJson = (0, devkit_1.readJson)(tree, 'tsconfig.base.json'); if (!baseTsconfigJson.compilerOptions || !baseTsconfigJson.compilerOptions.composite || baseTsconfigJson.compilerOptions.declaration === false) { return false; } const { compilerOptions, ...rest } = baseTsconfigJson; if (Object.keys(rest).length > 0) { return false; } return true; } function assertNotUsingTsSolutionSetup(tree, pluginName, generatorName) { if (process.env.NX_IGNORE_UNSUPPORTED_TS_SETUP === 'true' || !isUsingTsSolutionSetup(tree)) { return; } const artifactString = generatorName === 'init' ? `"@nx/${pluginName}" plugin` : `"@nx/${pluginName}:${generatorName}" generator`; devkit_1.output.error({ title: `The ${artifactString} doesn't yet support the existing TypeScript setup`, bodyLines: [ `We're working hard to support the existing TypeScript setup with the ${artifactString}. We'll soon release a new version of Nx with support for it.`, ], }); throw new Error(`The ${artifactString} doesn't yet support the existing TypeScript setup. See the error above.`); } function findRuntimeTsConfigName(projectRoot, tree) { tree ??= new tree_1.FsTree(devkit_1.workspaceRoot, false); if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.app.json'))) return 'tsconfig.app.json'; if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.lib.json'))) return 'tsconfig.lib.json'; return null; } function updateTsconfigFiles(tree, projectRoot, runtimeTsconfigFileName, compilerOptions, exclude = [], rootDir = 'src') { if (!isUsingTsSolutionSetup(tree)) { return; } const offset = (0, devkit_1.offsetFromRoot)(projectRoot); const runtimeTsconfigPath = `${projectRoot}/${runtimeTsconfigFileName}`; const specTsconfigPath = `${projectRoot}/tsconfig.spec.json`; if (tree.exists(runtimeTsconfigPath)) { (0, devkit_1.updateJson)(tree, runtimeTsconfigPath, (json) => { json.extends = (0, devkit_1.joinPathFragments)(offset, 'tsconfig.base.json'); json.compilerOptions = { ...json.compilerOptions, outDir: 'dist', rootDir, ...compilerOptions, }; if (rootDir && rootDir !== '.') { // when rootDir is different from '.', the tsbuildinfo file is output // at `<outDir>/<relative path to config from rootDir>/`, so we need // to set it explicitly to ensure it's output to the outDir // https://www.typescriptlang.org/tsconfig/#tsBuildInfoFile json.compilerOptions.tsBuildInfoFile = (0, posix_1.join)(json.compilerOptions.outDir, (0, posix_1.basename)(runtimeTsconfigFileName, '.json') + '.tsbuildinfo'); } else if (json.compilerOptions.tsBuildInfoFile) { // when rootDir is '.' or not set, it would be output to the outDir, so // we don't need to set it explicitly delete json.compilerOptions.tsBuildInfoFile; } // don't duplicate compiler options from base tsconfig json.compilerOptions = (0, configuration_1.getNeededCompilerOptionOverrides)(tree, json.compilerOptions, 'tsconfig.base.json'); const excludeSet = json.exclude ? new Set(['out-tsc', 'dist', ...json.exclude, ...exclude]) : new Set(exclude); json.exclude = Array.from(excludeSet); return json; }); } if (tree.exists(specTsconfigPath)) { (0, devkit_1.updateJson)(tree, specTsconfigPath, (json) => { json.extends = (0, devkit_1.joinPathFragments)(offset, 'tsconfig.base.json'); json.compilerOptions = { ...json.compilerOptions, ...compilerOptions, }; // don't duplicate compiler options from base tsconfig json.compilerOptions = (0, configuration_1.getNeededCompilerOptionOverrides)(tree, json.compilerOptions, 'tsconfig.base.json'); const runtimePath = `./${runtimeTsconfigFileName}`; json.references ??= []; if (!json.references.some((x) => x.path === runtimePath)) { json.references.push({ path: runtimePath }); } return json; }); } if (tree.exists('tsconfig.json')) { (0, devkit_1.updateJson)(tree, 'tsconfig.json', (json) => { const projectPath = './' + projectRoot; json.references ??= []; if (!json.references.some((x) => x.path === projectPath)) { json.references.push({ path: projectPath }); } return json; }); } } async function addProjectToTsSolutionWorkspace(tree, projectDir) { const state = (0, package_manager_workspaces_1.getProjectPackageManagerWorkspaceState)(tree, projectDir); if (state === 'included') { return; } // If dir is "libs/foo", we try to use "libs/*" but we only do it if it's // safe to do so. So, we first check if adding that pattern doesn't result // in extra projects being matched. If extra projects are matched, or the // dir is just "foo" then we add it as is. const baseDir = (0, posix_1.dirname)(projectDir); let pattern = projectDir; if (baseDir !== '.') { const patterns = (0, package_manager_workspaces_1.getPackageManagerWorkspacesPatterns)(tree); const projectsBefore = await (0, devkit_1.globAsync)(tree, patterns); patterns.push(`${baseDir}/*/package.json`); const projectsAfter = await (0, devkit_1.globAsync)(tree, patterns); if (projectsBefore.length + 1 === projectsAfter.length) { // Adding the pattern to the parent directory only results in one extra // project being matched, which is the project we're adding. It's safe // to add the pattern to the parent directory. pattern = `${baseDir}/*`; } } if (tree.exists('pnpm-workspace.yaml')) { const { load, dump } = require('@zkochan/js-yaml'); const workspaceFile = tree.read('pnpm-workspace.yaml', 'utf-8'); const yamlData = load(workspaceFile) ?? {}; yamlData.packages ??= []; if (!yamlData.packages.includes(pattern)) { yamlData.packages.push(pattern); tree.write('pnpm-workspace.yaml', dump(yamlData, { indent: 2, quotingType: '"', forceQuotes: true })); } } else { // Update package.json const packageJson = (0, devkit_1.readJson)(tree, 'package.json'); if (!packageJson.workspaces) { packageJson.workspaces = []; } if (!packageJson.workspaces.includes(pattern)) { packageJson.workspaces.push(pattern); tree.write('package.json', JSON.stringify(packageJson, null, 2)); } } } function getProjectType(tree, projectRoot, projectType) { if (projectType) return projectType; if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.lib.json'))) return 'library'; if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.app.json'))) return 'application'; // If there are no exports, assume it is an application since both buildable and non-buildable libraries have exports. const packageJsonPath = (0, devkit_1.joinPathFragments)(projectRoot, 'package.json'); const packageJson = tree.exists(packageJsonPath) ? (0, devkit_1.readJson)(tree, (0, devkit_1.joinPathFragments)(projectRoot, 'package.json')) : null; if (!packageJson?.exports) return 'application'; return 'library'; } function getProjectSourceRoot(tree, projectSourceRoot, projectRoot) { return (projectSourceRoot ?? (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, 'src')) ? (0, devkit_1.joinPathFragments)(projectRoot, 'src') : projectRoot)); }