fix(js): Update typescript plugin check for buildable projects (#29431)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->
Currently, we use `rootDir` to check if the project is buildable. This
might not be correct in the case where the transpiled files are inside
source.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
It should work the for projects as long as the main / exports files are
not source files.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
This commit is contained in:
Nicholas Cunningham 2025-01-09 09:03:33 -07:00 committed by GitHub
parent 325b9f6471
commit a77e3ef083
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 101 additions and 98 deletions

View File

@ -1819,7 +1819,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
// Sibling package.json // Sibling package.json
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': `{}`, 'libs/my-lib/tsconfig.json': `{}`,
'libs/my-lib/tsconfig.lib.json': `{}`, 'libs/my-lib/tsconfig.lib.json': `{"compilerOptions": {"outDir": "dist"}}`,
'libs/my-lib/tsconfig.build.json': `{}`, 'libs/my-lib/tsconfig.build.json': `{}`,
'libs/my-lib/package.json': `{}`, 'libs/my-lib/package.json': `{}`,
}); });
@ -1867,7 +1867,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
// Sibling package.json // Sibling package.json
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': `{}`, 'libs/my-lib/tsconfig.json': `{}`,
'libs/my-lib/tsconfig.lib.json': `{}`, 'libs/my-lib/tsconfig.lib.json': `{"compilerOptions": {"outDir": "dist"}}`,
'libs/my-lib/tsconfig.build.json': `{}`, 'libs/my-lib/tsconfig.build.json': `{}`,
'libs/my-lib/package.json': `{}`, 'libs/my-lib/package.json': `{}`,
}); });
@ -1917,7 +1917,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
// Sibling package.json // Sibling package.json
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': `{}`, 'libs/my-lib/tsconfig.json': `{}`,
'libs/my-lib/tsconfig.lib.json': `{"compilerOptions": {"rootDir": "src"}}`, 'libs/my-lib/tsconfig.lib.json': `{"compilerOptions": {"outDir": "dist"}}`,
'libs/my-lib/tsconfig.build.json': `{}`, 'libs/my-lib/tsconfig.build.json': `{}`,
'libs/my-lib/package.json': `{"main": "dist/index.js"}`, 'libs/my-lib/package.json': `{"main": "dist/index.js"}`,
}); });
@ -1965,7 +1965,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
"options": { "options": {
"cwd": "libs/my-lib", "cwd": "libs/my-lib",
}, },
"outputs": [], "outputs": [
"{projectRoot}/dist",
],
"syncGenerators": [ "syncGenerators": [
"@nx/js:typescript-sync", "@nx/js:typescript-sync",
], ],
@ -1978,8 +1980,8 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
// Sibling project.json // Sibling project.json
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': `{"compilerOptions": {"rootDir": "src"}}`, 'libs/my-lib/tsconfig.json': `{}`,
'libs/my-lib/tsconfig.lib.json': `{"compilerOptions": {"rootDir": "src"}}`, 'libs/my-lib/tsconfig.lib.json': `{"compilerOptions": {"outDir": "dist"}}`,
'libs/my-lib/tsconfig.build.json': `{}`, 'libs/my-lib/tsconfig.build.json': `{}`,
'libs/my-lib/project.json': `{}`, 'libs/my-lib/project.json': `{}`,
}); });
@ -2027,7 +2029,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
"options": { "options": {
"cwd": "libs/my-lib", "cwd": "libs/my-lib",
}, },
"outputs": [], "outputs": [
"{projectRoot}/dist",
],
"syncGenerators": [ "syncGenerators": [
"@nx/js:typescript-sync", "@nx/js:typescript-sync",
], ],
@ -2043,7 +2047,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
// Sibling package.json // Sibling package.json
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': `{}`, 'libs/my-lib/tsconfig.json': `{}`,
'libs/my-lib/tsconfig.lib.json': `{"compilerOptions": {"rootDir": "src"}}`, 'libs/my-lib/tsconfig.lib.json': `{"compilerOptions": {"outDir": "dist"}}`,
'libs/my-lib/tsconfig.build.json': `{}`, 'libs/my-lib/tsconfig.build.json': `{}`,
'libs/my-lib/package.json': `{ "main": "dist/index.js" }`, 'libs/my-lib/package.json': `{ "main": "dist/index.js" }`,
}); });
@ -2093,7 +2097,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
"options": { "options": {
"cwd": "libs/my-lib", "cwd": "libs/my-lib",
}, },
"outputs": [], "outputs": [
"{projectRoot}/dist",
],
"syncGenerators": [ "syncGenerators": [
"@nx/js:typescript-sync", "@nx/js:typescript-sync",
], ],
@ -2110,7 +2116,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': `{}`, 'libs/my-lib/tsconfig.json': `{}`,
'libs/my-lib/tsconfig.lib.json': `{}`, 'libs/my-lib/tsconfig.lib.json': `{}`,
'libs/my-lib/tsconfig.build.json': `{"compilerOptions": {"rootDir": "src"}}`, 'libs/my-lib/tsconfig.build.json': `{"compilerOptions": {"outDir": "dist"}}`,
'libs/my-lib/project.json': `{}`, 'libs/my-lib/project.json': `{}`,
}); });
expect( expect(
@ -2159,7 +2165,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
"options": { "options": {
"cwd": "libs/my-lib", "cwd": "libs/my-lib",
}, },
"outputs": [], "outputs": [
"{projectRoot}/dist",
],
"syncGenerators": [ "syncGenerators": [
"@nx/js:typescript-sync", "@nx/js:typescript-sync",
], ],
@ -2193,7 +2201,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { compilerOptions: {
rootDir: 'src', outDir: 'dist',
}, },
include: ['src/**/*.ts'], include: ['src/**/*.ts'],
exclude: ['src/**/*.spec.ts'], exclude: ['src/**/*.spec.ts'],
@ -2247,7 +2255,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
"options": { "options": {
"cwd": "libs/my-lib", "cwd": "libs/my-lib",
}, },
"outputs": [], "outputs": [
"{projectRoot}/dist",
],
"syncGenerators": [ "syncGenerators": [
"@nx/js:typescript-sync", "@nx/js:typescript-sync",
], ],
@ -2272,7 +2282,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
extends: '../../tsconfig.foo.json', extends: '../../tsconfig.foo.json',
include: ['src/**/*.ts'], include: ['src/**/*.ts'],
compilerOptions: { compilerOptions: {
rootDir: 'src', outDir: 'dist',
}, },
}), }),
'libs/my-lib/package.json': `{"main": "dist/index.js"}`, 'libs/my-lib/package.json': `{"main": "dist/index.js"}`,
@ -2326,7 +2336,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
"options": { "options": {
"cwd": "libs/my-lib", "cwd": "libs/my-lib",
}, },
"outputs": [], "outputs": [
"{projectRoot}/dist",
],
"syncGenerators": [ "syncGenerators": [
"@nx/js:typescript-sync", "@nx/js:typescript-sync",
], ],
@ -2351,7 +2363,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
extends: '../../tsconfig.foo.json', extends: '../../tsconfig.foo.json',
compilerOptions: { compilerOptions: {
rootDir: 'src', outDir: 'dist',
}, },
include: ['src/**/*.ts'], include: ['src/**/*.ts'],
}), }),
@ -2412,7 +2424,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
"options": { "options": {
"cwd": "libs/my-lib", "cwd": "libs/my-lib",
}, },
"outputs": [], "outputs": [
"{projectRoot}/dist",
],
"syncGenerators": [ "syncGenerators": [
"@nx/js:typescript-sync", "@nx/js:typescript-sync",
], ],
@ -2428,7 +2442,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': '{}', 'libs/my-lib/tsconfig.json': '{}',
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { rootDir: 'src' }, compilerOptions: { outDir: 'dist' },
include: ['src/**/*.ts'], include: ['src/**/*.ts'],
exclude: ['src/**/foo.ts'], // should be ignored because a referenced internal project includes this same pattern exclude: ['src/**/foo.ts'], // should be ignored because a referenced internal project includes this same pattern
references: [ references: [
@ -2494,7 +2508,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
"options": { "options": {
"cwd": "libs/my-lib", "cwd": "libs/my-lib",
}, },
"outputs": [], "outputs": [
"{projectRoot}/dist",
],
"syncGenerators": [ "syncGenerators": [
"@nx/js:typescript-sync", "@nx/js:typescript-sync",
], ],
@ -2511,7 +2527,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': '{}', 'libs/my-lib/tsconfig.json': '{}',
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { rootDir: 'src' }, // rootDir is required to determine if the project is buildable compilerOptions: { outDir: 'dist' }, // outDir is required to determine if the project is buildable
include: ['src/**/*.ts'], include: ['src/**/*.ts'],
exclude: ['src/**/foo.ts'], // should be ignored exclude: ['src/**/foo.ts'], // should be ignored
references: [{ path: './tsconfig.other.json' }], references: [{ path: './tsconfig.other.json' }],
@ -2548,7 +2564,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': '{}', 'libs/my-lib/tsconfig.json': '{}',
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { rootDir: 'src' }, compilerOptions: { outDir: 'dist' },
include: ['**/*.ts'], include: ['**/*.ts'],
exclude: ['**/foo.ts'], // should be ignored exclude: ['**/foo.ts'], // should be ignored
references: [{ path: './tsconfig.other.json' }], references: [{ path: './tsconfig.other.json' }],
@ -2584,7 +2600,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': '{}', 'libs/my-lib/tsconfig.json': '{}',
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { rootDir: 'src' }, // rooDir is required to determine if the project is buildable compilerOptions: { outDir: 'dist' }, // outDir is required to determine if the project is buildable
include: ['src/**/*.ts'], include: ['src/**/*.ts'],
exclude: ['src/**/foo.ts'], // should be ignored exclude: ['src/**/foo.ts'], // should be ignored
references: [{ path: './tsconfig.other.json' }], references: [{ path: './tsconfig.other.json' }],
@ -2620,7 +2636,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': '{}', 'libs/my-lib/tsconfig.json': '{}',
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { rootDir: 'src' }, compilerOptions: { outDir: 'dist' },
include: ['src/**/*.ts'], include: ['src/**/*.ts'],
exclude: ['src/**/foo.ts'], // should be ignored exclude: ['src/**/foo.ts'], // should be ignored
references: [{ path: './tsconfig.other.json' }], references: [{ path: './tsconfig.other.json' }],
@ -2656,7 +2672,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.json': '{}', 'libs/my-lib/tsconfig.json': '{}',
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { rootDir: 'src' }, compilerOptions: { outDir: 'dist' },
include: ['src/**/*.ts'], include: ['src/**/*.ts'],
exclude: [ exclude: [
'src/**/foo.ts', // should be ignored 'src/**/foo.ts', // should be ignored
@ -2696,7 +2712,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
it('should fall back to named inputs when not using include', async () => { it('should fall back to named inputs when not using include', async () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { rootDir: 'src' }, compilerOptions: { outDir: 'dist' },
files: ['main.ts'], files: ['main.ts'],
}), }),
'libs/my-lib/tsconfig.json': `{}`, 'libs/my-lib/tsconfig.json': `{}`,
@ -2746,19 +2762,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
"cwd": "libs/my-lib", "cwd": "libs/my-lib",
}, },
"outputs": [ "outputs": [
"{projectRoot}/**/*.js", "{projectRoot}/dist",
"{projectRoot}/**/*.cjs",
"{projectRoot}/**/*.mjs",
"{projectRoot}/**/*.jsx",
"{projectRoot}/**/*.js.map",
"{projectRoot}/**/*.jsx.map",
"{projectRoot}/**/*.d.ts",
"{projectRoot}/**/*.d.cts",
"{projectRoot}/**/*.d.mts",
"{projectRoot}/**/*.d.ts.map",
"{projectRoot}/**/*.d.cts.map",
"{projectRoot}/**/*.d.mts.map",
"{projectRoot}/tsconfig.lib.tsbuildinfo",
], ],
"syncGenerators": [ "syncGenerators": [
"@nx/js:typescript-sync", "@nx/js:typescript-sync",
@ -2778,12 +2782,11 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { compilerOptions: {
outFile: '../../dist/libs/my-lib/index.js', outFile: '../../dist/libs/my-lib/index.js',
rootDir: 'src',
}, },
files: ['main.ts'], files: ['main.ts'],
}), }),
'libs/my-lib/tsconfig.json': `{}`, 'libs/my-lib/tsconfig.json': `{}`,
'libs/my-lib/package.json': `{"main": "dist/libs/my-lib/index.js"}`, 'libs/my-lib/package.json': `{}`,
}); });
expect( expect(
await invokeCreateNodesOnMatchingFiles(context, { await invokeCreateNodesOnMatchingFiles(context, {
@ -2851,12 +2854,11 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { compilerOptions: {
outDir: '../../dist/libs/my-lib', outDir: '../../dist/libs/my-lib',
rootDir: 'src',
}, },
files: ['main.ts'], files: ['main.ts'],
}), }),
'libs/my-lib/tsconfig.json': `{}`, 'libs/my-lib/tsconfig.json': `{}`,
'libs/my-lib/package.json': `{"main": "dist/libs/my-lib/index.js"}`, 'libs/my-lib/package.json': `{"main": "../../dist/libs/my-lib/index.js"}`,
}); });
expect( expect(
await invokeCreateNodesOnMatchingFiles(context, { await invokeCreateNodesOnMatchingFiles(context, {
@ -2919,7 +2921,6 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
it('should add the inline output files when `outDir` is not defined', async () => { it('should add the inline output files when `outDir` is not defined', async () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { rootDir: 'src' },
files: ['main.ts'], files: ['main.ts'],
}), }),
'libs/my-lib/tsconfig.json': `{}`, 'libs/my-lib/tsconfig.json': `{}`,
@ -3000,7 +3001,6 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { compilerOptions: {
outFile: '../../dist/libs/my-lib/lib.js', outFile: '../../dist/libs/my-lib/lib.js',
rootDir: 'src',
}, },
files: ['main.ts'], files: ['main.ts'],
references: [{ path: './tsconfig.other.json' }], references: [{ path: './tsconfig.other.json' }],
@ -3008,11 +3008,10 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
'libs/my-lib/tsconfig.other.json': JSON.stringify({ 'libs/my-lib/tsconfig.other.json': JSON.stringify({
compilerOptions: { compilerOptions: {
outDir: '../../dist/libs/my-lib/other', outDir: '../../dist/libs/my-lib/other',
rootDir: 'src',
}, },
include: ['other/**/*.ts'], include: ['other/**/*.ts'],
}), }),
'libs/my-lib/package.json': `{"main": "dist/libs/my-lib/lib.js"}`, 'libs/my-lib/package.json': `{"main": "../../dist/libs/my-lib/lib.js"}`,
}); });
expect( expect(
await invokeCreateNodesOnMatchingFiles(context, { await invokeCreateNodesOnMatchingFiles(context, {
@ -3087,12 +3086,11 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { compilerOptions: {
outFile: '../../dist/libs/my-lib/index.js', outFile: '../../dist/libs/my-lib/index.js',
rootDir: 'src',
tsBuildInfoFile: '../../dist/libs/my-lib/my-lib.tsbuildinfo', tsBuildInfoFile: '../../dist/libs/my-lib/my-lib.tsbuildinfo',
}, },
files: ['main.ts'], files: ['main.ts'],
}), }),
'libs/my-lib/package.json': `{"main": "dist/libs/my-lib/index.js"}`, 'libs/my-lib/package.json': `{"main": "../../dist/libs/my-lib/index.js"}`,
}); });
expect( expect(
await invokeCreateNodesOnMatchingFiles(context, { await invokeCreateNodesOnMatchingFiles(context, {
@ -3159,7 +3157,6 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
'libs/my-lib/tsconfig.json': '{}', 'libs/my-lib/tsconfig.json': '{}',
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { compilerOptions: {
rootDir: 'src',
tsBuildInfoFile: '../../dist/libs/my-lib/my-lib.tsbuildinfo', tsBuildInfoFile: '../../dist/libs/my-lib/my-lib.tsbuildinfo',
}, },
files: ['main.ts'], files: ['main.ts'],
@ -3239,7 +3236,6 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
await applyFilesToTempFsAndContext(tempFs, context, { await applyFilesToTempFsAndContext(tempFs, context, {
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { compilerOptions: {
rootDir: 'src',
outDir: 'dist', outDir: 'dist',
tsBuildInfoFile: 'my-lib.tsbuildinfo', tsBuildInfoFile: 'my-lib.tsbuildinfo',
}, },
@ -3312,7 +3308,6 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
'libs/my-lib/tsconfig.lib.json': JSON.stringify({ 'libs/my-lib/tsconfig.lib.json': JSON.stringify({
compilerOptions: { compilerOptions: {
outDir: 'dist', outDir: 'dist',
rootDir: 'src',
tsBuildInfoFile: 'dist/my-lib.tsbuildinfo', tsBuildInfoFile: 'dist/my-lib.tsbuildinfo',
}, },
files: ['main.ts'], files: ['main.ts'],

View File

@ -22,6 +22,7 @@ import { existsSync, readdirSync, statSync } from 'node:fs';
import { import {
basename, basename,
dirname, dirname,
extname,
join, join,
normalize, normalize,
relative, relative,
@ -210,6 +211,11 @@ async function createNodesInternal(
projectRoot projectRoot
); );
const packageJsonPath = joinPathFragments(projectRoot, 'package.json');
const packageJson = existsSync(packageJsonPath)
? readJsonFile(packageJsonPath)
: null;
const nodeHash = hashArray([ const nodeHash = hashArray([
...[ ...[
fullConfigPath, fullConfigPath,
@ -219,6 +225,7 @@ async function createNodesInternal(
join(context.workspaceRoot, lockFileName), join(context.workspaceRoot, lockFileName),
].map(hashFile), ].map(hashFile),
hashObject(options), hashObject(options),
...(packageJson ? [hashObject(packageJson)] : []),
]); ]);
const cacheKey = `${nodeHash}_${configFilePath}`; const cacheKey = `${nodeHash}_${configFilePath}`;
@ -325,12 +332,7 @@ function buildTscTargets(
if ( if (
options.build && options.build &&
basename(configFilePath) === options.build.configName && basename(configFilePath) === options.build.configName &&
isValidPackageJsonBuildConfig( isValidPackageJsonBuildConfig(tsConfig, context.workspaceRoot, projectRoot)
tsConfig,
context.workspaceRoot,
projectRoot,
configFilePath
)
) { ) {
internalProjectReferences ??= resolveInternalProjectReferences( internalProjectReferences ??= resolveInternalProjectReferences(
tsConfig, tsConfig,
@ -618,21 +620,22 @@ function getOutputs(
} }
/** /**
* Checks whether a `package.json` file has a valid build configuration by ensuring * Validates the build configuration of a `package.json` file by ensuring that paths in the `exports`, `module`,
* that the `main`, `module`, or `exports` do not include paths from the `rootDir`. * and `main` fields reference valid output paths within the `outDir` defined in the TypeScript configuration.
* Or if `outFile` is defined, it should not be within the `rootDir`. * Priority is given to the `exports` field, specifically the `.` export if defined. If `exports` is not defined,
* the function falls back to validating `main` and `module` fields. If `outFile` is specified, it validates that the file
* is located within the output directory.
* If no `package.json` file exists, it assumes the configuration is valid.
* *
* @param tsConfig The TypeScript configuration object. * @param tsConfig The TypeScript configuration object.
* @param workspaceRoot The workspace root path. * @param workspaceRoot The workspace root path.
* @param projectRoot The project root path. * @param projectRoot The project root path.
* @param tsConfigPath The path to the TypeScript configuration file.
* @returns `true` if the package has a valid build configuration; otherwise, `false`. * @returns `true` if the package has a valid build configuration; otherwise, `false`.
*/ */
function isValidPackageJsonBuildConfig( function isValidPackageJsonBuildConfig(
tsConfig, tsConfig,
workspaceRoot: string, workspaceRoot: string,
projectRoot: string, projectRoot: string
tsConfigPath: string
): boolean { ): boolean {
if (!existsSync(joinPathFragments(projectRoot, 'package.json'))) { if (!existsSync(joinPathFragments(projectRoot, 'package.json'))) {
// If the package.json file does not exist. // If the package.json file does not exist.
@ -643,42 +646,30 @@ function isValidPackageJsonBuildConfig(
joinPathFragments(projectRoot, 'package.json') joinPathFragments(projectRoot, 'package.json')
); );
const rootDir = tsConfig.options.rootDir ?? 'src/'; const outDir = tsConfig.options.outFile
if (!tsConfig.options.rootDir) { ? dirname(tsConfig.options.outFile)
console.warn( : tsConfig.options.outDir;
`The 'rootDir' option is not set in the tsconfig file at ${tsConfigPath}. Assuming 'src/' as the root directory.` const resolvedOutDir = outDir
); ? resolve(workspaceRoot, projectRoot, outDir)
} : undefined;
const isPathWithinSrc = (path: string): boolean => { const isPathSourceFile = (path: string): boolean => {
const resolvedRootDir = resolve(workspaceRoot, projectRoot, rootDir); if (resolvedOutDir) {
const pathToCheck = resolve(workspaceRoot, projectRoot, path); const pathToCheck = resolve(workspaceRoot, projectRoot, path);
return !pathToCheck.startsWith(resolvedOutDir);
}
return pathToCheck.startsWith(resolvedRootDir); const ext = extname(path);
// Check that the file extension is a TS file extension. As the source files are in the same directory as the output files.
return ['.ts', '.tsx', '.cts', '.mts'].includes(ext);
}; };
// If `outFile` is defined, check the validity of the path.
if (tsConfig.options.outFile) {
if (isPathWithinSrc(tsConfig.options.outFile)) {
return false;
}
}
const buildPaths = ['main', 'module'];
for (const field of buildPaths) {
if (packageJson[field] && isPathWithinSrc(packageJson[field])) {
return false;
}
}
const exports = packageJson?.exports;
// Checks if the value is a path within the `src` directory. // Checks if the value is a path within the `src` directory.
const containsInvalidPath = ( const containsInvalidPath = (
value: string | Record<string, string> value: string | Record<string, string>
): boolean => { ): boolean => {
if (typeof value === 'string') { if (typeof value === 'string') {
return isPathWithinSrc(value); return isPathSourceFile(value);
} else if (typeof value === 'object') { } else if (typeof value === 'object') {
return Object.entries(value).some(([currentKey, subValue]) => { return Object.entries(value).some(([currentKey, subValue]) => {
// Skip types field // Skip types field
@ -686,7 +677,7 @@ function isValidPackageJsonBuildConfig(
return false; return false;
} }
if (typeof subValue === 'string') { if (typeof subValue === 'string') {
return isPathWithinSrc(subValue); return isPathSourceFile(subValue);
} }
return false; return false;
}); });
@ -694,17 +685,34 @@ function isValidPackageJsonBuildConfig(
return false; return false;
}; };
if (typeof exports === 'string' && isPathWithinSrc(exports)) { const exports = packageJson?.exports;
// Check the `.` export if `exports` is defined.
if (exports) {
if (typeof exports === 'string') {
return !isPathSourceFile(exports);
} else if (typeof exports === 'object' && '.' in exports) {
if (containsInvalidPath(exports['.'])) {
return false; return false;
} }
}
// Check nested exports if `exports` is an object. // Check other exports if `.` is not defined or valid.
if (typeof exports === 'object') {
for (const key in exports) { for (const key in exports) {
if (containsInvalidPath(exports[key])) { if (key !== '.' && containsInvalidPath(exports[key])) {
return false; 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; return true;