@nx/js
Version:
215 lines (214 loc) • 10.1 kB
JavaScript
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));
}
;