Skip to content

Commit 13319a8

Browse files
authored
fix(js): improve the @nx/js/typescript plugin performance (#30024)
Improve the perf of the `@nx/js/typescript` plugin both in cold and warm scenarios. The main changes done are: - Batch some processing to do it once instead of doing it per config file (avoids some duplicated processing) - Use a custom TS host to read tsconfig files to reduce I/O operations - Cache tsconfig files' reads Benchmark results in a repo with 656 TS projects: ``` # Before the changes Cold (NX_CACHE_PROJECT_GRAPH=false): ~2285 ms Warm (NX_CACHE_PROJECT_GRAPH=true): ~2142 ms # After the changes Cold (NX_CACHE_PROJECT_GRAPH=false): ~597 ms Warm (NX_CACHE_PROJECT_GRAPH=true): ~220 ms ``` Note: Once #29935 is merged. I'll send another change to batch the file hashes. ## Current Behavior ## Expected Behavior ## Related Issue(s) Fixes #
1 parent c2e89f8 commit 13319a8

File tree

2 files changed

+361
-183
lines changed

2 files changed

+361
-183
lines changed

packages/js/src/plugins/typescript/plugin.spec.ts

+51-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
import { detectPackageManager, type CreateNodesContext } from '@nx/devkit';
22
import { TempFs } from '@nx/devkit/internal-testing-utils';
33
import { minimatch } from 'minimatch';
4+
import { mkdirSync, rmdirSync } from 'node:fs';
45
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
56
import { getLockFileName } from 'nx/src/plugins/js/lock-file/lock-file';
67
import { setupWorkspaceContext } from 'nx/src/utils/workspace-context';
78
import { PLUGIN_NAME, createNodesV2, type TscPluginOptions } from './plugin';
89

10+
jest.mock('nx/src/utils/cache-directory', () => ({
11+
...jest.requireActual('nx/src/utils/cache-directory'),
12+
workspaceDataDirectory: 'tmp/project-graph-cache',
13+
}));
14+
915
describe(`Plugin: ${PLUGIN_NAME}`, () => {
1016
let context: CreateNodesContext;
1117
let cwd = process.cwd();
1218
let tempFs: TempFs;
1319
let originalCacheProjectGraph: string | undefined;
1420

1521
beforeEach(() => {
22+
mkdirSync('tmp/project-graph-cache', { recursive: true });
1623
tempFs = new TempFs('typescript-plugin');
1724
context = {
1825
nxJsonConfiguration: {
@@ -38,6 +45,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
3845
tempFs.cleanup();
3946
process.chdir(cwd);
4047
process.env.NX_CACHE_PROJECT_GRAPH = originalCacheProjectGraph;
48+
rmdirSync('tmp/project-graph-cache', { recursive: true });
4149
});
4250

4351
it('should not create nodes for root tsconfig.json files', async () => {
@@ -59,7 +67,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
5967
it('should create a node with a typecheck target for a project level tsconfig.json file by default (when there is a sibling package.json or project.json)', async () => {
6068
// Sibling package.json
6169
await applyFilesToTempFsAndContext(tempFs, context, {
62-
'libs/my-lib/tsconfig.json': `{}`,
70+
'libs/my-lib/tsconfig.json': JSON.stringify({ files: [] }),
6371
'libs/my-lib/package.json': `{}`,
6472
});
6573
expect(await invokeCreateNodesOnMatchingFiles(context, {}))
@@ -114,7 +122,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
114122

115123
// Sibling project.json
116124
await applyFilesToTempFsAndContext(tempFs, context, {
117-
'libs/my-lib/tsconfig.json': `{}`,
125+
'libs/my-lib/tsconfig.json': JSON.stringify({ files: [] }),
118126
'libs/my-lib/project.json': `{}`,
119127
});
120128
expect(await invokeCreateNodesOnMatchingFiles(context, {}))
@@ -169,7 +177,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
169177

170178
// Other tsconfigs present
171179
await applyFilesToTempFsAndContext(tempFs, context, {
172-
'libs/my-lib/tsconfig.json': `{}`,
180+
'libs/my-lib/tsconfig.json': JSON.stringify({ files: [] }),
173181
'libs/my-lib/tsconfig.lib.json': `{}`,
174182
'libs/my-lib/tsconfig.build.json': `{}`,
175183
'libs/my-lib/tsconfig.spec.json': `{}`,
@@ -229,7 +237,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
229237
it('should create a node with a typecheck target with "--verbose" flag when the "verboseOutput" plugin option is true', async () => {
230238
// Sibling package.json
231239
await applyFilesToTempFsAndContext(tempFs, context, {
232-
'libs/my-lib/tsconfig.json': `{}`,
240+
'libs/my-lib/tsconfig.json': JSON.stringify({ files: [] }),
233241
'libs/my-lib/package.json': `{}`,
234242
});
235243
expect(
@@ -285,7 +293,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
285293

286294
// Sibling project.json
287295
await applyFilesToTempFsAndContext(tempFs, context, {
288-
'libs/my-lib/tsconfig.json': `{}`,
296+
'libs/my-lib/tsconfig.json': JSON.stringify({ files: [] }),
289297
'libs/my-lib/project.json': `{}`,
290298
});
291299
expect(
@@ -341,7 +349,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
341349

342350
// Other tsconfigs present
343351
await applyFilesToTempFsAndContext(tempFs, context, {
344-
'libs/my-lib/tsconfig.json': `{}`,
352+
'libs/my-lib/tsconfig.json': JSON.stringify({ files: [] }),
345353
'libs/my-lib/tsconfig.lib.json': `{}`,
346354
'libs/my-lib/tsconfig.build.json': `{}`,
347355
'libs/my-lib/tsconfig.spec.json': `{}`,
@@ -446,6 +454,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
446454
await applyFilesToTempFsAndContext(tempFs, context, {
447455
'libs/my-lib/tsconfig.json': JSON.stringify({
448456
compilerOptions: { noEmit: true },
457+
files: [],
449458
}),
450459
'libs/my-lib/package.json': `{}`,
451460
});
@@ -506,6 +515,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
506515
}),
507516
'libs/my-lib/tsconfig.json': JSON.stringify({
508517
extends: '../../tsconfig.base.json',
518+
files: [],
509519
}),
510520
'libs/my-lib/package.json': `{}`,
511521
});
@@ -568,6 +578,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
568578
}),
569579
'libs/my-lib/tsconfig.lib.json': JSON.stringify({
570580
compilerOptions: { noEmit: true },
581+
files: [],
571582
}),
572583
'libs/my-lib/package.json': `{}`,
573584
});
@@ -641,6 +652,8 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
641652
'libs/my-lib/tsconfig.json': JSON.stringify({
642653
include: ['src/**/*.ts'],
643654
exclude: ['src/**/foo.ts'],
655+
// set this to keep outputs smaller
656+
compilerOptions: { outDir: 'dist' },
644657
}),
645658
'libs/my-lib/package.json': `{}`,
646659
});
@@ -686,7 +699,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
686699
"options": {
687700
"cwd": "libs/my-lib",
688701
},
689-
"outputs": [],
702+
"outputs": [
703+
"{projectRoot}/dist",
704+
],
690705
"syncGenerators": [
691706
"@nx/js:typescript-sync",
692707
],
@@ -709,6 +724,8 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
709724
'libs/my-lib/tsconfig.json': JSON.stringify({
710725
extends: '../../tsconfig.foo.json',
711726
include: ['src/**/*.ts'],
727+
// set this to keep outputs smaller
728+
compilerOptions: { outDir: 'dist' },
712729
}),
713730
'libs/my-lib/package.json': `{}`,
714731
});
@@ -757,7 +774,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
757774
"options": {
758775
"cwd": "libs/my-lib",
759776
},
760-
"outputs": [],
777+
"outputs": [
778+
"{projectRoot}/dist",
779+
],
761780
"syncGenerators": [
762781
"@nx/js:typescript-sync",
763782
],
@@ -781,6 +800,8 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
781800
'libs/my-lib/tsconfig.json': JSON.stringify({
782801
extends: '../../tsconfig.foo.json',
783802
include: ['src/**/*.ts'],
803+
// set this to keep outputs smaller
804+
compilerOptions: { outDir: 'dist' },
784805
}),
785806
'libs/my-lib/package.json': `{}`,
786807
});
@@ -835,7 +856,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
835856
"options": {
836857
"cwd": "libs/my-lib",
837858
},
838-
"outputs": [],
859+
"outputs": [
860+
"{projectRoot}/dist",
861+
],
839862
"syncGenerators": [
840863
"@nx/js:typescript-sync",
841864
],
@@ -859,23 +882,33 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
859882
{ path: './nested-project/tsconfig.json' }, // external project reference in a nested directory
860883
{ path: '../other-lib' }, // external project reference, it causes `dependentTasksOutputFiles` to be set
861884
],
885+
// set this to keep outputs smaller
886+
compilerOptions: { outDir: 'dist' },
862887
}),
863888
'libs/my-lib/tsconfig.lib.json': JSON.stringify({
864889
include: ['src/**/*.ts'],
865890
exclude: ['src/**/*.spec.ts'],
891+
// set this to keep outputs smaller
892+
compilerOptions: { outDir: 'dist' },
866893
}),
867894
'libs/my-lib/tsconfig.spec.json': JSON.stringify({
868895
include: ['src/**/*.spec.ts'],
869896
references: [{ path: './tsconfig.lib.json' }],
897+
// set this to keep outputs smaller
898+
compilerOptions: { outDir: 'dist' },
870899
}),
871900
'libs/my-lib/cypress/tsconfig.json': JSON.stringify({
872901
include: ['**/*.ts', '../cypress.config.ts', '../**/*.cy.ts'],
873902
references: [{ path: '../tsconfig.lib.json' }],
903+
// set this to keep outputs smaller
904+
compilerOptions: { outDir: 'dist' },
874905
}),
875906
'libs/my-lib/package.json': `{}`,
876907
'libs/my-lib/nested-project/package.json': `{}`,
877908
'libs/my-lib/nested-project/tsconfig.json': JSON.stringify({
878909
include: ['lib/**/*.ts'], // different pattern that should not be included in my-lib because it's an external project reference
910+
// set this to keep outputs smaller
911+
compilerOptions: { outDir: 'dist' },
879912
}),
880913
'libs/other-lib/tsconfig.json': JSON.stringify({
881914
include: ['**/*.ts'], // different pattern that should not be included because it's an external project
@@ -931,7 +964,10 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
931964
"options": {
932965
"cwd": "libs/my-lib",
933966
},
934-
"outputs": [],
967+
"outputs": [
968+
"{projectRoot}/dist",
969+
"{projectRoot}/cypress/dist",
970+
],
935971
"syncGenerators": [
936972
"@nx/js:typescript-sync",
937973
],
@@ -975,7 +1011,9 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
9751011
"options": {
9761012
"cwd": "libs/my-lib/nested-project",
9771013
},
978-
"outputs": [],
1014+
"outputs": [
1015+
"{projectRoot}/dist",
1016+
],
9791017
"syncGenerators": [
9801018
"@nx/js:typescript-sync",
9811019
],
@@ -2803,6 +2841,8 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
28032841
}),
28042842
'libs/my-lib/tsconfig.other.json': JSON.stringify({
28052843
include: ['other/**/*.ts', 'src/**/foo.ts'],
2844+
// set this to keep outputs smaller
2845+
compilerOptions: { outDir: 'dist' },
28062846
}),
28072847
'libs/other-lib/tsconfig.json': JSON.stringify({
28082848
include: ['**/*.ts'], // different pattern that should not be included because it's an external project

0 commit comments

Comments
 (0)