diff --git a/packages/js/src/plugins/typescript/plugin.spec.ts b/packages/js/src/plugins/typescript/plugin.spec.ts index 71421d3917..ad32643e9d 100644 --- a/packages/js/src/plugins/typescript/plugin.spec.ts +++ b/packages/js/src/plugins/typescript/plugin.spec.ts @@ -796,6 +796,73 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => { `); }); + it('should add the config file and the `include` and `exclude` patterns using the "${configDir}" template', async () => { + await applyFilesToTempFsAndContext(tempFs, context, { + 'libs/my-lib/tsconfig.json': JSON.stringify({ + include: ['${configDir}/src/**/*.ts'], + exclude: ['${configDir}/src/**/foo.ts'], + // set this to keep outputs smaller + compilerOptions: { outDir: 'dist' }, + }), + 'libs/my-lib/package.json': `{}`, + }); + expect(await invokeCreateNodesOnMatchingFiles(context, {})) + .toMatchInlineSnapshot(` + { + "projects": { + "libs/my-lib": { + "projectType": "library", + "targets": { + "typecheck": { + "cache": true, + "command": "tsc --build --emitDeclarationOnly", + "dependsOn": [ + "^typecheck", + ], + "inputs": [ + "{projectRoot}/package.json", + "{projectRoot}/tsconfig.json", + "{projectRoot}/src/**/*.ts", + "!{projectRoot}/src/**/foo.ts", + "^production", + { + "externalDependencies": [ + "typescript", + ], + }, + ], + "metadata": { + "description": "Runs type-checking for the project.", + "help": { + "command": "npx tsc --build --help", + "example": { + "args": [ + "--force", + ], + }, + }, + "technologies": [ + "typescript", + ], + }, + "options": { + "cwd": "libs/my-lib", + }, + "outputs": [ + "{projectRoot}/dist/**/*.d.ts", + "{projectRoot}/dist/tsconfig.tsbuildinfo", + ], + "syncGenerators": [ + "@nx/js:typescript-sync", + ], + }, + }, + }, + }, + } + `); + }); + it('should normalize and add directories in `include` with the ts extensions', async () => { await applyFilesToTempFsAndContext(tempFs, context, { 'libs/my-lib/tsconfig.json': JSON.stringify({ @@ -2608,6 +2675,68 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => { } `); }); + + it('should support the "${configDir}" template', async () => { + await applyFilesToTempFsAndContext(tempFs, context, { + 'libs/my-lib/tsconfig.json': JSON.stringify({ + compilerOptions: { outDir: '${configDir}/dist' }, + files: ['main.ts'], + }), + 'libs/my-lib/package.json': `{}`, + }); + expect(await invokeCreateNodesOnMatchingFiles(context, {})) + .toMatchInlineSnapshot(` + { + "projects": { + "libs/my-lib": { + "projectType": "library", + "targets": { + "typecheck": { + "cache": true, + "command": "tsc --build --emitDeclarationOnly", + "dependsOn": [ + "^typecheck", + ], + "inputs": [ + "production", + "^production", + { + "externalDependencies": [ + "typescript", + ], + }, + ], + "metadata": { + "description": "Runs type-checking for the project.", + "help": { + "command": "npx tsc --build --help", + "example": { + "args": [ + "--force", + ], + }, + }, + "technologies": [ + "typescript", + ], + }, + "options": { + "cwd": "libs/my-lib", + }, + "outputs": [ + "{projectRoot}/dist/**/*.d.ts", + "{projectRoot}/dist/tsconfig.tsbuildinfo", + ], + "syncGenerators": [ + "@nx/js:typescript-sync", + ], + }, + }, + }, + }, + } + `); + }); }); }); @@ -3351,6 +3480,78 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => { `); }); + it('should add the config file and the `include` and `exclude` patterns using the "${configDir}" template', async () => { + await applyFilesToTempFsAndContext(tempFs, context, { + 'libs/my-lib/tsconfig.lib.json': JSON.stringify({ + compilerOptions: { + outDir: 'dist', + }, + include: ['${configDir}/src/**/*.ts'], + exclude: ['${configDir}/src/**/*.spec.ts'], + }), + 'libs/my-lib/tsconfig.json': `{}`, + 'libs/my-lib/package.json': `{"main": "./dist/index.js"}`, + }); + expect( + await invokeCreateNodesOnMatchingFiles(context, { + typecheck: false, + build: true, + }) + ).toMatchInlineSnapshot(` + { + "projects": { + "libs/my-lib": { + "projectType": "library", + "targets": { + "build": { + "cache": true, + "command": "tsc --build tsconfig.lib.json", + "dependsOn": [ + "^build", + ], + "inputs": [ + "{projectRoot}/package.json", + "{projectRoot}/tsconfig.lib.json", + "{projectRoot}/src/**/*.ts", + "!{projectRoot}/src/**/*.spec.ts", + "^production", + { + "externalDependencies": [ + "typescript", + ], + }, + ], + "metadata": { + "description": "Builds the project with \`tsc\`.", + "help": { + "command": "npx tsc --build --help", + "example": { + "args": [ + "--force", + ], + }, + }, + "technologies": [ + "typescript", + ], + }, + "options": { + "cwd": "libs/my-lib", + }, + "outputs": [ + "{projectRoot}/dist", + ], + "syncGenerators": [ + "@nx/js:typescript-sync", + ], + }, + }, + }, + }, + } + `); + }); + it('should normalize and add directories in `include` with the ts extensions', async () => { await applyFilesToTempFsAndContext(tempFs, context, { 'libs/my-lib/tsconfig.lib.json': JSON.stringify({ @@ -4948,6 +5149,74 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => { } `); }); + + it('should support the "${configDir}" template', async () => { + await applyFilesToTempFsAndContext(tempFs, context, { + 'libs/my-lib/tsconfig.lib.json': JSON.stringify({ + compilerOptions: { + outDir: '${configDir}/dist', + }, + files: ['main.ts'], + }), + 'libs/my-lib/tsconfig.json': `{}`, + 'libs/my-lib/package.json': `{"main": "./dist/index.js"}`, + }); + expect( + await invokeCreateNodesOnMatchingFiles(context, { + typecheck: false, + build: true, + }) + ).toMatchInlineSnapshot(` + { + "projects": { + "libs/my-lib": { + "projectType": "library", + "targets": { + "build": { + "cache": true, + "command": "tsc --build tsconfig.lib.json", + "dependsOn": [ + "^build", + ], + "inputs": [ + "production", + "^production", + { + "externalDependencies": [ + "typescript", + ], + }, + ], + "metadata": { + "description": "Builds the project with \`tsc\`.", + "help": { + "command": "npx tsc --build --help", + "example": { + "args": [ + "--force", + ], + }, + }, + "technologies": [ + "typescript", + ], + }, + "options": { + "cwd": "libs/my-lib", + }, + "outputs": [ + "{projectRoot}/dist", + ], + "syncGenerators": [ + "@nx/js:typescript-sync", + ], + }, + }, + }, + }, + } + `); + }); }); }); }); diff --git a/packages/js/src/plugins/typescript/plugin.ts b/packages/js/src/plugins/typescript/plugin.ts index 91c0e63f24..0122e4e807 100644 --- a/packages/js/src/plugins/typescript/plugin.ts +++ b/packages/js/src/plugins/typescript/plugin.ts @@ -344,7 +344,7 @@ async function getConfigFileHash( ...(packageJson ? [hashObject(packageJson)] : []), // change this to bust the cache when making changes that would yield // different results for the same hash - hashObject({ bust: 2 }), + hashObject({ bust: 3 }), ]); } @@ -636,11 +636,18 @@ function getInputs( return [input]; }; + const configDirTemplate = '${configDir}'; + const substituteConfigDir = (p: string) => + p.startsWith(configDirTemplate) ? p.replace(configDirTemplate, './') : p; + projectTsConfigFiles.forEach(([configPath, config]) => { configFiles.add(configPath); const offset = relative(absoluteProjectRoot, dirname(configPath)); (config.raw?.include ?? []).forEach((p: string) => { - const normalized = normalizeInput(join(offset, p), config); + const normalized = normalizeInput( + join(offset, substituteConfigDir(p)), + config + ); normalized.forEach((input) => includePaths.add(input)); }); @@ -653,11 +660,14 @@ function getInputs( const otherFilesInclude: string[] = []; projectTsConfigFiles.forEach(([path, c]) => { if (path !== configPath) { - otherFilesInclude.push(...(c.raw?.include ?? [])); + otherFilesInclude.push( + ...(c.raw?.include ?? []).map(substituteConfigDir) + ); } }); const normalize = (p: string) => (p.startsWith('./') ? p.slice(2) : p); - config.raw.exclude.forEach((excludePath: string) => { + config.raw.exclude.forEach((e: string) => { + const excludePath = substituteConfigDir(e); if ( !otherFilesInclude.some( (includePath) =>