From e9b78b106ee061e28783e8cd19df4a0e7f92930b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CJamesHenry=E2=80=9D?= Date: Mon, 19 Aug 2024 19:19:11 +0400 Subject: [PATCH 1/8] fix(js): preserve comments when patching references --- packages/js/package.json | 17 +-- .../typescript-sync/typescript-sync.spec.ts | 117 +++++++++++++++++- .../typescript-sync/typescript-sync.ts | 25 +++- 3 files changed, 140 insertions(+), 19 deletions(-) diff --git a/packages/js/package.json b/packages/js/package.json index 0937e6a42b26a..4930de9d6a722 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -34,11 +34,13 @@ "dependencies": { "@babel/core": "^7.23.2", "@babel/plugin-proposal-decorators": "^7.22.7", - "@babel/plugin-transform-runtime": "^7.23.2", "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", "@babel/preset-env": "^7.23.2", "@babel/preset-typescript": "^7.22.5", "@babel/runtime": "^7.22.6", + "@nx/devkit": "file:../devkit", + "@nx/workspace": "file:../workspace", "babel-plugin-const-enum": "^1.0.1", "babel-plugin-macros": "^2.8.0", "babel-plugin-transform-typescript-metadata": "^0.3.1", @@ -47,19 +49,18 @@ "detect-port": "^1.5.1", "fast-glob": "3.2.7", "fs-extra": "^11.1.0", - "npm-package-arg": "11.0.1", - "npm-run-path": "^4.0.1", - "ts-node": "10.9.1", - "tsconfig-paths": "^4.1.2", "ignore": "^5.0.4", "js-tokens": "^4.0.0", + "jsonc-parser": "3.2.0", "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", "ora": "5.3.0", "semver": "^7.5.3", "source-map-support": "0.5.19", - "tslib": "^2.3.0", - "@nx/devkit": "file:../devkit", - "@nx/workspace": "file:../workspace" + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" }, "peerDependencies": { "verdaccio": "^5.0.4" diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts index c3b5f9764ba08..96968f3191236 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts @@ -35,9 +35,17 @@ describe('syncGenerator()', () => { source: name, target: dep, })); - writeJson(tree, `${root}/tsconfig.json`, {}); + writeJson(tree, `${root}/tsconfig.json`, { + compilerOptions: { + composite: true, + }, + }); for (const runtimeTsConfigFileName of extraRuntimeTsConfigs) { - writeJson(tree, `${root}/${runtimeTsConfigFileName}`, {}); + writeJson(tree, `${root}/${runtimeTsConfigFileName}`, { + compilerOptions: { + composite: true, + }, + }); } writeJson(tree, `${root}/package.json`, { name: name, @@ -62,8 +70,16 @@ describe('syncGenerator()', () => { }); // Root tsconfigs - writeJson(tree, 'tsconfig.json', {}); - writeJson(tree, 'tsconfig.options.json', { compilerOptions: {} }); + writeJson(tree, 'tsconfig.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'tsconfig.options.json', { + compilerOptions: { + composite: true, + }, + }); // b => a addProject('a'); @@ -107,11 +123,18 @@ describe('syncGenerator()', () => { { path: './packages/d' }, ], })); - writeJson(tree, 'packages/a/tsconfig.lib.json', {}); + writeJson(tree, 'packages/a/tsconfig.lib.json', { + compilerOptions: { + composite: true, + }, + }); // unformatted tsconfig.json to test that it doesn't get picked up as a change tree.write( 'packages/b/tsconfig.json', `{ + "compilerOptions": { + "composite": true, + }, "references": [ { "path": "../a" } ]}` ); @@ -119,6 +142,9 @@ describe('syncGenerator()', () => { tree.write( 'packages/b/tsconfig.lib.json', `{ + "compilerOptions": { + "composite": true, + }, "references": [ { "path": "../a/tsconfig.lib.json" } ]}` ); @@ -127,6 +153,9 @@ describe('syncGenerator()', () => { references: [{ path: '../b' }, { path: '../a' }], })); writeJson(tree, 'packages/c/tsconfig.lib.json', { + compilerOptions: { + composite: true, + }, references: [ { path: '../b/tsconfig.lib.json' }, { path: '../a/tsconfig.lib.json' }, @@ -137,6 +166,9 @@ describe('syncGenerator()', () => { references: [{ path: '../b' }, { path: '../a' }], })); writeJson(tree, 'packages/d/tsconfig.lib.json', { + compilerOptions: { + composite: true, + }, references: [ { path: '../b/tsconfig.lib.json' }, { path: '../a/tsconfig.lib.json' }, @@ -147,6 +179,9 @@ describe('syncGenerator()', () => { references: [{ path: '../b' }, { path: '../d' }, { path: '../a' }], })); writeJson(tree, 'packages/e/tsconfig.lib.json', { + compilerOptions: { + composite: true, + }, references: [ { path: '../b/tsconfig.lib.json' }, { path: '../d/tsconfig.lib.json' }, @@ -187,6 +222,9 @@ describe('syncGenerator()', () => { it('should respect existing project references and discard non-existing ones in the tsconfig.json', async () => { writeJson(tree, 'tsconfig.json', { + compilerOptions: { + composite: true, + }, // Swapped order and additional manual reference references: [ { path: './packages/b' }, @@ -195,7 +233,11 @@ describe('syncGenerator()', () => { { path: './packages/d' }, // non-existing reference ], }); - writeJson(tree, 'packages/c/tsconfig.json', {}); + writeJson(tree, 'packages/c/tsconfig.json', { + compilerOptions: { + composite: true, + }, + }); await syncGenerator(tree); @@ -214,6 +256,36 @@ describe('syncGenerator()', () => { ] `); }); + + it('should leave comments outside of references untouched in the tsconfig.json when patching', async () => { + tree.write( + 'tsconfig.json', + `{ + // This is a top level comment + "compilerOptions": { + "composite": true, + // This is a nested comment + "target": "es5" + } +} +` + ); + + await syncGenerator(tree); + + expect(tree.read('tsconfig.json').toString('utf-8')) + .toMatchInlineSnapshot(` + "{ + // This is a top level comment + "compilerOptions": { + // This is a nested comment + "target": "es5" + }, + "references": [{ "path": "./packages/a" }, { "path": "./packages/b" }] + } + " + `); + }); }); describe('project level tsconfig.json', () => { @@ -352,6 +424,39 @@ describe('syncGenerator()', () => { `); }); + it('should leave comments outside of references untouched in the tsconfig.json when patching', async () => { + addProject('foo', ['bar'], ['tsconfig.build.json']); + addProject('bar', [], ['tsconfig.build.json']); + + tree.write( + 'packages/foo/tsconfig.build.json', + `{ + // This is a top level comment + "compilerOptions": { + // This is a nested comment + "target": "es5", + }, + "references": [] +} + ` + ); + + await syncGenerator(tree); + + expect(tree.read('packages/foo/tsconfig.build.json').toString('utf-8')) + .toMatchInlineSnapshot(` + "{ + // This is a top level comment + "compilerOptions": { + // This is a nested comment + "target": "es5" + }, + "references": [{ "path": "../bar/tsconfig.build.json" }] + } + " + `); + }); + describe('without custom sync generator options', () => { it.each` runtimeTsConfigFileName diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.ts b/packages/js/src/generators/typescript-sync/typescript-sync.ts index 7738df98997fe..e9360c1a9fdb2 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.ts @@ -5,12 +5,12 @@ import { logger, readJson, readNxJson, - writeJson, type ExpandedPluginConfiguration, type ProjectGraph, type ProjectGraphProjectNode, type Tree, } from '@nx/devkit'; +import { applyEdits, modify } from 'jsonc-parser'; import { dirname, normalize, relative } from 'node:path/posix'; import type { SyncGeneratorResult } from 'nx/src/utils/sync-generators'; import { @@ -109,10 +109,10 @@ export async function syncGenerator(tree: Tree): Promise { } if (hasChanges) { - rootTsconfig.references = Array.from(referencesSet).map((ref) => ({ + const updatedReferences = Array.from(referencesSet).map((ref) => ({ path: `./${ref}`, })); - writeJson(tree, rootTsconfigPath, rootTsconfig); + patchTsconfigJsonReferences(tree, rootTsconfigPath, updatedReferences); } } @@ -282,8 +282,7 @@ function updateTsConfigReferences( hasChanges ||= newReferencesSet.size !== originalReferencesSet.size; if (hasChanges) { - tsConfig.references = references; - writeJson(tree, tsConfigPath, tsConfig); + patchTsconfigJsonReferences(tree, tsConfigPath, references); } return hasChanges; @@ -398,3 +397,19 @@ function getTsConfigPathFromReferencePath( ? resolvedRefPath : joinPathFragments(resolvedRefPath, 'tsconfig.json'); } + +/** + * Minimally patch just the "references" property within the tsconfig file at a given path. + * This allows comments in other sections of the file to remain intact when syncing is run. + */ +function patchTsconfigJsonReferences( + tree: Tree, + tsconfigPath: string, + updatedReferences: { path: string }[] +) { + const jsonContents = tree.read(tsconfigPath).toString(); + const edits = modify(jsonContents, ['references'], updatedReferences, {}); + const updatedJsonContents = applyEdits(jsonContents, edits); + // The final contents will be formatted by formatFiles() later + tree.write(tsconfigPath, updatedJsonContents); +} From 79d84ed987d5ff4f4e5a9ac3d88beb46ae51d212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CJamesHenry=E2=80=9D?= Date: Mon, 19 Aug 2024 19:20:38 +0400 Subject: [PATCH 2/8] chore(js): add composite: true to test compilerOptions --- .../typescript-sync/typescript-sync.spec.ts | 213 +++++++++++++++--- 1 file changed, 179 insertions(+), 34 deletions(-) diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts index 96968f3191236..674c8d6b1add0 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts @@ -278,6 +278,7 @@ describe('syncGenerator()', () => { "{ // This is a top level comment "compilerOptions": { + "composite": true, // This is a nested comment "target": "es5" }, @@ -308,6 +309,9 @@ describe('syncGenerator()', () => { it('should respect existing internal project references in the tsconfig.json', async () => { writeJson(tree, 'packages/b/tsconfig.json', { + compilerOptions: { + composite: true, + }, // Swapped order and additional manual reference references: [ { path: './some/thing' }, @@ -338,6 +342,9 @@ describe('syncGenerator()', () => { it('should prune existing external project references that are no longer dependencies', async () => { writeJson(tree, 'packages/b/tsconfig.json', { + compilerOptions: { + composite: true, + }, references: [ { path: './some/thing' }, { path: './another/one' }, @@ -433,6 +440,7 @@ describe('syncGenerator()', () => { `{ // This is a top level comment "compilerOptions": { + "composite": true, // This is a nested comment "target": "es5", }, @@ -448,6 +456,7 @@ describe('syncGenerator()', () => { "{ // This is a top level comment "compilerOptions": { + "composite": true, // This is a nested comment "target": "es5" }, @@ -469,8 +478,16 @@ describe('syncGenerator()', () => { `( 'should sync project references to $runtimeTsConfigFileName files', async ({ runtimeTsConfigFileName }) => { - writeJson(tree, `packages/a/${runtimeTsConfigFileName}`, {}); - writeJson(tree, `packages/b/${runtimeTsConfigFileName}`, {}); + writeJson(tree, `packages/a/${runtimeTsConfigFileName}`, { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, `packages/b/${runtimeTsConfigFileName}`, { + compilerOptions: { + composite: true, + }, + }); await syncGenerator(tree); @@ -495,9 +512,21 @@ describe('syncGenerator()', () => { ); it('should sync project references to multiple runtime tsconfig files', async () => { - writeJson(tree, 'packages/a/tsconfig.lib.json', {}); - writeJson(tree, 'packages/b/tsconfig.cjs.json', {}); - writeJson(tree, 'packages/b/tsconfig.esm.json', {}); + writeJson(tree, 'packages/a/tsconfig.lib.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/b/tsconfig.cjs.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/b/tsconfig.esm.json', { + compilerOptions: { + composite: true, + }, + }); await syncGenerator(tree); @@ -528,8 +557,16 @@ describe('syncGenerator()', () => { }); it('should sync project references to different runtime tsconfig files', async () => { - writeJson(tree, 'packages/a/tsconfig.lib.json', {}); - writeJson(tree, 'packages/b/tsconfig.build.json', {}); + writeJson(tree, 'packages/a/tsconfig.lib.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/b/tsconfig.build.json', { + compilerOptions: { + composite: true, + }, + }); addProject('c', ['b'], ['tsconfig.cjs.json', 'tsconfig.esm.json']); addProject('d', ['b', 'a'], ['tsconfig.runtime.json']); addProject('e', ['c'], ['tsconfig.cjs.json', 'tsconfig.esm.json']); @@ -697,8 +734,16 @@ describe('syncGenerator()', () => { `( 'should collect transitive dependencies and sync project references to $runtimeTsConfigFileName files', async ({ runtimeTsConfigFileName }) => { - writeJson(tree, `packages/a/${runtimeTsConfigFileName}`, {}); - writeJson(tree, `packages/b/${runtimeTsConfigFileName}`, {}); + writeJson(tree, `packages/a/${runtimeTsConfigFileName}`, { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, `packages/b/${runtimeTsConfigFileName}`, { + compilerOptions: { + composite: true, + }, + }); // c => b => a // d => b => a // => a @@ -811,14 +856,38 @@ describe('syncGenerator()', () => { ); it('should not make changes to non-default runtime tsconfig files', async () => { - writeJson(tree, 'packages/a/tsconfig.lib.json', {}); - writeJson(tree, 'packages/a/tsconfig.custom.json', {}); - writeJson(tree, 'packages/a/tsconfig.spec.json', {}); + writeJson(tree, 'packages/a/tsconfig.lib.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/a/tsconfig.custom.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/a/tsconfig.spec.json', { + compilerOptions: { + composite: true, + }, + }); // default runtime tsconfig that should be updated - writeJson(tree, 'packages/b/tsconfig.lib.json', {}); + writeJson(tree, 'packages/b/tsconfig.lib.json', { + compilerOptions: { + composite: true, + }, + }); // non-default runtime tsconfig files that should not be updated - writeJson(tree, 'packages/b/tsconfig.custom.json', {}); - writeJson(tree, 'packages/b/tsconfig.spec.json', {}); + writeJson(tree, 'packages/b/tsconfig.custom.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/b/tsconfig.spec.json', { + compilerOptions: { + composite: true, + }, + }); await syncGenerator(tree); @@ -841,10 +910,18 @@ describe('syncGenerator()', () => { `); // assert that tsconfig.lib.json and tsconfig.spec.json files have not been updated expect(readJson(tree, 'packages/b/tsconfig.custom.json')).toStrictEqual( - {} + { + compilerOptions: { + composite: true, + }, + } ); expect(readJson(tree, 'packages/b/tsconfig.spec.json')).toStrictEqual( - {} + { + compilerOptions: { + composite: true, + }, + } ); }); }); @@ -860,8 +937,16 @@ describe('syncGenerator()', () => { }, }; updateNxJson(tree, nxJson); - writeJson(tree, 'packages/a/tsconfig.custom.json', {}); - writeJson(tree, 'packages/b/tsconfig.custom.json', {}); + writeJson(tree, 'packages/a/tsconfig.custom.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/b/tsconfig.custom.json', { + compilerOptions: { + composite: true, + }, + }); await syncGenerator(tree); @@ -897,9 +982,21 @@ describe('syncGenerator()', () => { }, }; updateNxJson(tree, nxJson); - writeJson(tree, 'packages/a/tsconfig.custom.json', {}); - writeJson(tree, 'packages/b/tsconfig.custom-cjs.json', {}); - writeJson(tree, 'packages/b/tsconfig.custom-esm.json', {}); + writeJson(tree, 'packages/a/tsconfig.custom.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/b/tsconfig.custom-cjs.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/b/tsconfig.custom-esm.json', { + compilerOptions: { + composite: true, + }, + }); await syncGenerator(tree); @@ -945,8 +1042,16 @@ describe('syncGenerator()', () => { }, }; updateNxJson(tree, nxJson); - writeJson(tree, 'packages/a/tsconfig.custom.json', {}); - writeJson(tree, 'packages/b/tsconfig.custom-build.json', {}); + writeJson(tree, 'packages/a/tsconfig.custom.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/b/tsconfig.custom-build.json', { + compilerOptions: { + composite: true, + }, + }); addProject( 'c', ['b'], @@ -1124,8 +1229,16 @@ describe('syncGenerator()', () => { }, }; updateNxJson(tree, nxJson); - writeJson(tree, 'packages/a/tsconfig.custom.json', {}); - writeJson(tree, 'packages/b/tsconfig.custom.json', {}); + writeJson(tree, 'packages/a/tsconfig.custom.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/b/tsconfig.custom.json', { + compilerOptions: { + composite: true, + }, + }); // c => b => a // d => b => a // => a @@ -1242,14 +1355,38 @@ describe('syncGenerator()', () => { }, }; updateNxJson(tree, nxJson); - writeJson(tree, 'packages/a/tsconfig.custom.json', {}); - writeJson(tree, 'packages/a/tsconfig.lib.json', {}); - writeJson(tree, 'packages/a/tsconfig.spec.json', {}); + writeJson(tree, 'packages/a/tsconfig.custom.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/a/tsconfig.lib.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/a/tsconfig.spec.json', { + compilerOptions: { + composite: true, + }, + }); // non-default runtime tsconfig that should be updated because is in the configured list - writeJson(tree, 'packages/b/tsconfig.custom.json', {}); + writeJson(tree, 'packages/b/tsconfig.custom.json', { + compilerOptions: { + composite: true, + }, + }); // default runtime tsconfig that shouldn't be updated because is not in the configured list - writeJson(tree, 'packages/b/tsconfig.lib.json', {}); - writeJson(tree, 'packages/b/tsconfig.spec.json', {}); + writeJson(tree, 'packages/b/tsconfig.lib.json', { + compilerOptions: { + composite: true, + }, + }); + writeJson(tree, 'packages/b/tsconfig.spec.json', { + compilerOptions: { + composite: true, + }, + }); await syncGenerator(tree); @@ -1272,10 +1409,18 @@ describe('syncGenerator()', () => { `); // assert that tsconfig.lib.json and tsconfig.spec.json files have not been updated expect(readJson(tree, 'packages/b/tsconfig.lib.json')).toStrictEqual( - {} + { + compilerOptions: { + composite: true, + }, + } ); expect(readJson(tree, 'packages/b/tsconfig.spec.json')).toStrictEqual( - {} + { + compilerOptions: { + composite: true, + }, + } ); }); }); From cb056576a25ed9310c6801c881b6ab8f377ead5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CJamesHenry=E2=80=9D?= Date: Mon, 19 Aug 2024 19:30:19 +0400 Subject: [PATCH 3/8] fix(js): check for composite: true during syncing --- .../typescript-sync/typescript-sync.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.ts b/packages/js/src/generators/typescript-sync/typescript-sync.ts index e9360c1a9fdb2..5e6f9cf022f2b 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.ts @@ -13,6 +13,12 @@ import { import { applyEdits, modify } from 'jsonc-parser'; import { dirname, normalize, relative } from 'node:path/posix'; import type { SyncGeneratorResult } from 'nx/src/utils/sync-generators'; +import { + parseJsonConfigFileContent, + readConfigFile, + sys, + System, +} from 'typescript'; import { PLUGIN_NAME, type TscPluginOptions, @@ -76,6 +82,16 @@ export async function syncGenerator(tree: Tree): Promise { } ); + const tsSysFromTree: System = { + ...sys, + readFile(path, encoding: BufferEncoding = 'utf-8') { + if (tree.exists(path)) { + return tree.read(path, encoding); + } + return sys.readFile(path, encoding); + }, + }; + // Track if any changes were made to the tsconfig files. We check the changes // made by this generator to know if the TS config is out of sync with the // project graph. Therefore, we don't format the files if there were no changes @@ -172,6 +188,7 @@ export async function syncGenerator(tree: Tree): Promise { hasChanges = updateTsConfigReferences( tree, + tsSysFromTree, runtimeTsConfigPath, dependencies, sourceProjectNode.data.root, @@ -185,6 +202,7 @@ export async function syncGenerator(tree: Tree): Promise { hasChanges = updateTsConfigReferences( tree, + tsSysFromTree, sourceProjectTsconfigPath, dependencies, sourceProjectNode.data.root, @@ -206,6 +224,7 @@ export default syncGenerator; function updateTsConfigReferences( tree: Tree, + sys: System, tsConfigPath: string, dependencies: ProjectGraphProjectNode[], projectRoot: string, @@ -251,6 +270,10 @@ function updateTsConfigReferences( runtimeTsConfigFileName ); if (tree.exists(runtimeTsConfigPath)) { + // Check composite is true in the dependency runtime tsconfig file before proceeding + if (!hasCompositeEnabled(sys, runtimeTsConfigPath)) { + continue; + } referencePath = runtimeTsConfigPath; } else { // Check for other possible runtime tsconfig file names @@ -262,11 +285,25 @@ function updateTsConfigReferences( possibleRuntimeTsConfigFileName ); if (tree.exists(possibleRuntimeTsConfigPath)) { + // Check composite is true in the dependency runtime tsconfig file before proceeding + if (!hasCompositeEnabled(sys, possibleRuntimeTsConfigPath)) { + continue; + } referencePath = possibleRuntimeTsConfigPath; break; } } } + } else { + // Check composite is true in the dependency tsconfig.json file before proceeding + if ( + !hasCompositeEnabled( + sys, + joinPathFragments(dep.data.root, 'tsconfig.json') + ) + ) { + continue; + } } const relativePathToTargetRoot = relative(projectRoot, referencePath); if (!newReferencesSet.has(relativePathToTargetRoot)) { @@ -413,3 +450,12 @@ function patchTsconfigJsonReferences( // The final contents will be formatted by formatFiles() later tree.write(tsconfigPath, updatedJsonContents); } + +function hasCompositeEnabled(sys: System, tsconfigPath: string): boolean { + const parsed = parseJsonConfigFileContent( + readConfigFile(tsconfigPath, sys.readFile).config, + sys, + dirname(tsconfigPath) + ); + return parsed.options.composite === true; +} From ad48628a5867ad639a62e50a0c988bdc73720791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CJamesHenry=E2=80=9D?= Date: Mon, 19 Aug 2024 19:32:23 +0400 Subject: [PATCH 4/8] chore(js): format --- .../typescript-sync/typescript-sync.spec.ts | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts index 674c8d6b1add0..6346e9040d8d9 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts @@ -916,13 +916,11 @@ describe('syncGenerator()', () => { }, } ); - expect(readJson(tree, 'packages/b/tsconfig.spec.json')).toStrictEqual( - { - compilerOptions: { - composite: true, - }, - } - ); + expect(readJson(tree, 'packages/b/tsconfig.spec.json')).toStrictEqual({ + compilerOptions: { + composite: true, + }, + }); }); }); @@ -1408,20 +1406,16 @@ describe('syncGenerator()', () => { ] `); // assert that tsconfig.lib.json and tsconfig.spec.json files have not been updated - expect(readJson(tree, 'packages/b/tsconfig.lib.json')).toStrictEqual( - { - compilerOptions: { - composite: true, - }, - } - ); - expect(readJson(tree, 'packages/b/tsconfig.spec.json')).toStrictEqual( - { - compilerOptions: { - composite: true, - }, - } - ); + expect(readJson(tree, 'packages/b/tsconfig.lib.json')).toStrictEqual({ + compilerOptions: { + composite: true, + }, + }); + expect(readJson(tree, 'packages/b/tsconfig.spec.json')).toStrictEqual({ + compilerOptions: { + composite: true, + }, + }); }); }); }); From ac0b567709f70a7df6390527f3df7346a940fbac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CJamesHenry=E2=80=9D?= Date: Tue, 20 Aug 2024 14:03:24 +0400 Subject: [PATCH 5/8] fix(js): add coverage for when composite is not true --- .../typescript-sync/typescript-sync.spec.ts | 57 +++++++++++++++++++ .../typescript-sync/typescript-sync.ts | 50 +++++++++------- 2 files changed, 86 insertions(+), 21 deletions(-) diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts index 6346e9040d8d9..d1f3163b5bd5d 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts @@ -287,6 +287,26 @@ describe('syncGenerator()', () => { " `); }); + + it('should not add a reference if the internally referenced tsconfig.json does not have composite: true', async () => { + // Delete composite from a, causing it to now show up in the final tsconfig.json snapshot + writeJson(tree, 'packages/a/tsconfig.json', { + compilerOptions: {}, + }); + + await syncGenerator(tree); + + expect(tree.read('tsconfig.json').toString('utf-8')) + .toMatchInlineSnapshot(` + "{ + "compilerOptions": { + "composite": true + }, + "references": [{ "path": "./packages/b" }] + } + " + `); + }); }); describe('project level tsconfig.json', () => { @@ -466,6 +486,43 @@ describe('syncGenerator()', () => { `); }); + it('should not add a reference if the dependency tsconfig.json does not have composite: true', async () => { + addProject('foo', ['bar'], ['tsconfig.build.json']); + addProject('bar', [], ['tsconfig.build.json']); + + // Delete composite from bar, causing it to now show up in the final tsconfig.json snapshots below + writeJson(tree, 'packages/bar/tsconfig.json', { + compilerOptions: {}, + }); + + await syncGenerator(tree); + + expect(tree.read('tsconfig.json').toString('utf-8')) + .toMatchInlineSnapshot(` + "{ + "compilerOptions": { + "composite": true + }, + "references": [ + { "path": "./packages/a" }, + { "path": "./packages/b" }, + { "path": "./packages/foo" } + ] + } + " + `); + + expect(tree.read('packages/foo/tsconfig.json').toString('utf-8')) + .toMatchInlineSnapshot(` + "{ + "compilerOptions": { + "composite": true + } + } + " + `); + }); + describe('without custom sync generator options', () => { it.each` runtimeTsConfigFileName diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.ts b/packages/js/src/generators/typescript-sync/typescript-sync.ts index 5e6f9cf022f2b..9cc54eb036cd3 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.ts @@ -13,12 +13,7 @@ import { import { applyEdits, modify } from 'jsonc-parser'; import { dirname, normalize, relative } from 'node:path/posix'; import type { SyncGeneratorResult } from 'nx/src/utils/sync-generators'; -import { - parseJsonConfigFileContent, - readConfigFile, - sys, - System, -} from 'typescript'; +import * as ts from 'typescript'; import { PLUGIN_NAME, type TscPluginOptions, @@ -82,13 +77,13 @@ export async function syncGenerator(tree: Tree): Promise { } ); - const tsSysFromTree: System = { - ...sys, + const tsSysFromTree: ts.System = { + ...ts.sys, readFile(path, encoding: BufferEncoding = 'utf-8') { if (tree.exists(path)) { return tree.read(path, encoding); } - return sys.readFile(path, encoding); + return ts.sys.readFile(path, encoding); }, }; @@ -99,7 +94,7 @@ export async function syncGenerator(tree: Tree): Promise { let hasChanges = false; if (tsconfigProjectNodeValues.length > 0) { - const referencesSet = new Set(); + const referencesSet = new Set(); for (const ref of rootTsconfig.references ?? []) { // reference path is relative to the tsconfig file const resolvedRefPath = getTsConfigPathFromReferencePath( @@ -125,9 +120,17 @@ export async function syncGenerator(tree: Tree): Promise { } if (hasChanges) { - const updatedReferences = Array.from(referencesSet).map((ref) => ({ - path: `./${ref}`, - })); + const updatedReferences = Array.from(referencesSet) + // Check composite is true in the internal reference before proceeding + .filter((ref) => + hasCompositeEnabled( + tsSysFromTree, + joinPathFragments(ref, 'tsconfig.json') + ) + ) + .map((ref) => ({ + path: `./${ref}`, + })); patchTsconfigJsonReferences(tree, rootTsconfigPath, updatedReferences); } } @@ -224,7 +227,7 @@ export default syncGenerator; function updateTsConfigReferences( tree: Tree, - sys: System, + tsSysFromTree: ts.System, tsConfigPath: string, dependencies: ProjectGraphProjectNode[], projectRoot: string, @@ -271,7 +274,7 @@ function updateTsConfigReferences( ); if (tree.exists(runtimeTsConfigPath)) { // Check composite is true in the dependency runtime tsconfig file before proceeding - if (!hasCompositeEnabled(sys, runtimeTsConfigPath)) { + if (!hasCompositeEnabled(tsSysFromTree, runtimeTsConfigPath)) { continue; } referencePath = runtimeTsConfigPath; @@ -286,7 +289,9 @@ function updateTsConfigReferences( ); if (tree.exists(possibleRuntimeTsConfigPath)) { // Check composite is true in the dependency runtime tsconfig file before proceeding - if (!hasCompositeEnabled(sys, possibleRuntimeTsConfigPath)) { + if ( + !hasCompositeEnabled(tsSysFromTree, possibleRuntimeTsConfigPath) + ) { continue; } referencePath = possibleRuntimeTsConfigPath; @@ -298,7 +303,7 @@ function updateTsConfigReferences( // Check composite is true in the dependency tsconfig.json file before proceeding if ( !hasCompositeEnabled( - sys, + tsSysFromTree, joinPathFragments(dep.data.root, 'tsconfig.json') ) ) { @@ -451,10 +456,13 @@ function patchTsconfigJsonReferences( tree.write(tsconfigPath, updatedJsonContents); } -function hasCompositeEnabled(sys: System, tsconfigPath: string): boolean { - const parsed = parseJsonConfigFileContent( - readConfigFile(tsconfigPath, sys.readFile).config, - sys, +function hasCompositeEnabled( + tsSysFromTree: ts.System, + tsconfigPath: string +): boolean { + const parsed = ts.parseJsonConfigFileContent( + ts.readConfigFile(tsconfigPath, tsSysFromTree.readFile).config, + tsSysFromTree, dirname(tsconfigPath) ); return parsed.options.composite === true; From 188f4ac99fdbff6ac1c9f794b27fedd5cb8b2429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CJamesHenry=E2=80=9D?= Date: Tue, 20 Aug 2024 14:38:52 +0400 Subject: [PATCH 6/8] chore(js): add cache for tsconfig composite lookups --- .../typescript-sync/typescript-sync.ts | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.ts b/packages/js/src/generators/typescript-sync/typescript-sync.ts index 9cc54eb036cd3..2ab421d3cceb8 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.ts @@ -65,6 +65,7 @@ export async function syncGenerator(tree: Tree): Promise { const rootTsconfig = readJson(tree, rootTsconfigPath); const projectGraph = await createProjectGraphAsync(); const projectRoots = new Set(); + const tsconfigHasCompositeEnabledCache = new Map(); const tsconfigProjectNodeValues = Object.values(projectGraph.nodes).filter( (node) => { @@ -125,6 +126,7 @@ export async function syncGenerator(tree: Tree): Promise { .filter((ref) => hasCompositeEnabled( tsSysFromTree, + tsconfigHasCompositeEnabledCache, joinPathFragments(ref, 'tsconfig.json') ) ) @@ -192,6 +194,7 @@ export async function syncGenerator(tree: Tree): Promise { updateTsConfigReferences( tree, tsSysFromTree, + tsconfigHasCompositeEnabledCache, runtimeTsConfigPath, dependencies, sourceProjectNode.data.root, @@ -206,6 +209,7 @@ export async function syncGenerator(tree: Tree): Promise { updateTsConfigReferences( tree, tsSysFromTree, + tsconfigHasCompositeEnabledCache, sourceProjectTsconfigPath, dependencies, sourceProjectNode.data.root, @@ -228,6 +232,7 @@ export default syncGenerator; function updateTsConfigReferences( tree: Tree, tsSysFromTree: ts.System, + tsconfigHasCompositeEnabledCache: Map, tsConfigPath: string, dependencies: ProjectGraphProjectNode[], projectRoot: string, @@ -274,7 +279,13 @@ function updateTsConfigReferences( ); if (tree.exists(runtimeTsConfigPath)) { // Check composite is true in the dependency runtime tsconfig file before proceeding - if (!hasCompositeEnabled(tsSysFromTree, runtimeTsConfigPath)) { + if ( + !hasCompositeEnabled( + tsSysFromTree, + tsconfigHasCompositeEnabledCache, + runtimeTsConfigPath + ) + ) { continue; } referencePath = runtimeTsConfigPath; @@ -290,7 +301,11 @@ function updateTsConfigReferences( if (tree.exists(possibleRuntimeTsConfigPath)) { // Check composite is true in the dependency runtime tsconfig file before proceeding if ( - !hasCompositeEnabled(tsSysFromTree, possibleRuntimeTsConfigPath) + !hasCompositeEnabled( + tsSysFromTree, + tsconfigHasCompositeEnabledCache, + possibleRuntimeTsConfigPath + ) ) { continue; } @@ -304,6 +319,7 @@ function updateTsConfigReferences( if ( !hasCompositeEnabled( tsSysFromTree, + tsconfigHasCompositeEnabledCache, joinPathFragments(dep.data.root, 'tsconfig.json') ) ) { @@ -458,12 +474,17 @@ function patchTsconfigJsonReferences( function hasCompositeEnabled( tsSysFromTree: ts.System, + tsconfigHasCompositeEnabledCache: Map, tsconfigPath: string ): boolean { - const parsed = ts.parseJsonConfigFileContent( - ts.readConfigFile(tsconfigPath, tsSysFromTree.readFile).config, - tsSysFromTree, - dirname(tsconfigPath) - ); - return parsed.options.composite === true; + if (!tsconfigHasCompositeEnabledCache.has(tsconfigPath)) { + const parsed = ts.parseJsonConfigFileContent( + ts.readConfigFile(tsconfigPath, tsSysFromTree.readFile).config, + tsSysFromTree, + dirname(tsconfigPath) + ); + const enabledVal = parsed.options.composite === true; + tsconfigHasCompositeEnabledCache.set(tsconfigPath, enabledVal); + } + return tsconfigHasCompositeEnabledCache.get(tsconfigPath); } From 40591023badb08d30cf18532303da95a06f21628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CJamesHenry=E2=80=9D?= Date: Tue, 20 Aug 2024 15:46:13 +0400 Subject: [PATCH 7/8] chore(js): cache all fs operations --- .../typescript-sync/typescript-sync.spec.ts | 2 +- .../typescript-sync/typescript-sync.ts | 146 ++++++++++++++---- 2 files changed, 121 insertions(+), 27 deletions(-) diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts index d1f3163b5bd5d..069e7a00ccc7e 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts @@ -100,7 +100,7 @@ describe('syncGenerator()', () => { tree.delete('tsconfig.json'); await expect(syncGenerator(tree)).rejects.toMatchInlineSnapshot( - `[Error: A "tsconfig.json" file must exist in the workspace root.]` + `[Error: A "tsconfig.json" file must exist in the workspace root in order to use this sync generator.]` ); }); diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.ts b/packages/js/src/generators/typescript-sync/typescript-sync.ts index 2ab421d3cceb8..dd8c629001324 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.ts @@ -3,7 +3,7 @@ import { formatFiles, joinPathFragments, logger, - readJson, + parseJson, readNxJson, type ExpandedPluginConfiguration, type ProjectGraph, @@ -59,10 +59,18 @@ export async function syncGenerator(tree: Tree): Promise { // Root tsconfig containing project references for the whole workspace const rootTsconfigPath = 'tsconfig.json'; if (!tree.exists(rootTsconfigPath)) { - throw new Error(`A "tsconfig.json" file must exist in the workspace root.`); + throw new Error( + `A "tsconfig.json" file must exist in the workspace root in order to use this sync generator.` + ); } - const rootTsconfig = readJson(tree, rootTsconfigPath); + const rawTsconfigContentsCache = new Map(); + const stringifiedRootJsonContents = readRawTsconfigContents( + tree, + rawTsconfigContentsCache, + rootTsconfigPath + ); + const rootTsconfig = parseJson(stringifiedRootJsonContents); const projectGraph = await createProjectGraphAsync(); const projectRoots = new Set(); const tsconfigHasCompositeEnabledCache = new Map(); @@ -74,17 +82,18 @@ export async function syncGenerator(tree: Tree): Promise { node.data.root, 'tsconfig.json' ); - return tree.exists(projectTsconfigPath); + return tsconfigExists( + tree, + rawTsconfigContentsCache, + projectTsconfigPath + ); } ); const tsSysFromTree: ts.System = { ...ts.sys, - readFile(path, encoding: BufferEncoding = 'utf-8') { - if (tree.exists(path)) { - return tree.read(path, encoding); - } - return ts.sys.readFile(path, encoding); + readFile(path) { + return readRawTsconfigContents(tree, rawTsconfigContentsCache, path); }, }; @@ -103,7 +112,7 @@ export async function syncGenerator(tree: Tree): Promise { rootTsconfigPath, ref.path ); - if (tree.exists(resolvedRefPath)) { + if (tsconfigExists(tree, rawTsconfigContentsCache, resolvedRefPath)) { // we only keep the references that still exist referencesSet.add(normalizeReferencePath(ref.path)); } else { @@ -133,7 +142,12 @@ export async function syncGenerator(tree: Tree): Promise { .map((ref) => ({ path: `./${ref}`, })); - patchTsconfigJsonReferences(tree, rootTsconfigPath, updatedReferences); + patchTsconfigJsonReferences( + tree, + rawTsconfigContentsCache, + rootTsconfigPath, + updatedReferences + ); } } @@ -160,7 +174,9 @@ export async function syncGenerator(tree: Tree): Promise { sourceProjectNode.data.root, 'tsconfig.json' ); - if (!tree.exists(sourceProjectTsconfigPath)) { + if ( + !tsconfigExists(tree, rawTsconfigContentsCache, sourceProjectTsconfigPath) + ) { if (process.env.NX_VERBOSE_LOGGING === 'true') { logger.warn( `Skipping project "${name}" as there is no tsconfig.json file found in the project root "${sourceProjectNode.data.root}".` @@ -185,7 +201,9 @@ export async function syncGenerator(tree: Tree): Promise { sourceProjectNode.data.root, runtimeTsConfigFileName ); - if (!tree.exists(runtimeTsConfigPath)) { + if ( + !tsconfigExists(tree, rawTsconfigContentsCache, runtimeTsConfigPath) + ) { continue; } @@ -194,6 +212,7 @@ export async function syncGenerator(tree: Tree): Promise { updateTsConfigReferences( tree, tsSysFromTree, + rawTsconfigContentsCache, tsconfigHasCompositeEnabledCache, runtimeTsConfigPath, dependencies, @@ -209,6 +228,7 @@ export async function syncGenerator(tree: Tree): Promise { updateTsConfigReferences( tree, tsSysFromTree, + rawTsconfigContentsCache, tsconfigHasCompositeEnabledCache, sourceProjectTsconfigPath, dependencies, @@ -229,9 +249,42 @@ export async function syncGenerator(tree: Tree): Promise { export default syncGenerator; +/** + * Within the context of a sync generator, performance is a key concern, + * so avoid FS interactions whenever possible. + */ +function readRawTsconfigContents( + tree: Tree, + rawTsconfigContentsCache: Map, + tsconfigPath: string +): string { + if (!rawTsconfigContentsCache.has(tsconfigPath)) { + rawTsconfigContentsCache.set( + tsconfigPath, + tree.read(tsconfigPath, 'utf-8').toString() + ); + } + return rawTsconfigContentsCache.get(tsconfigPath); +} + +/** + * Within the context of a sync generator, performance is a key concern, + * so avoid FS interactions whenever possible. + */ +function tsconfigExists( + tree: Tree, + rawTsconfigContentsCache: Map, + tsconfigPath: string +): boolean { + return rawTsconfigContentsCache.has(tsconfigPath) + ? true + : tree.exists(tsconfigPath); +} + function updateTsConfigReferences( tree: Tree, tsSysFromTree: ts.System, + rawTsconfigContentsCache: Map, tsconfigHasCompositeEnabledCache: Map, tsConfigPath: string, dependencies: ProjectGraphProjectNode[], @@ -240,7 +293,13 @@ function updateTsConfigReferences( runtimeTsConfigFileName?: string, possibleRuntimeTsConfigFileNames?: string[] ): boolean { - const tsConfig = readJson(tree, tsConfigPath); + const stringifiedJsonContents = readRawTsconfigContents( + tree, + rawTsconfigContentsCache, + tsConfigPath + ); + const tsConfig = parseJson(stringifiedJsonContents); + // We have at least one dependency so we can safely set it to an empty array if not already set const references = []; const originalReferencesSet = new Set(); @@ -255,14 +314,15 @@ function updateTsConfigReferences( ref.path ); if ( - isInternalProjectReference( + isProjectReferenceWithinNxProject( tree, + rawTsconfigContentsCache, resolvedRefPath, projectRoot, projectRoots ) ) { - // we keep all internal references + // we keep all references within the current Nx project references.push(ref); newReferencesSet.add(normalizedPath); } @@ -277,7 +337,7 @@ function updateTsConfigReferences( dep.data.root, runtimeTsConfigFileName ); - if (tree.exists(runtimeTsConfigPath)) { + if (tsconfigExists(tree, rawTsconfigContentsCache, runtimeTsConfigPath)) { // Check composite is true in the dependency runtime tsconfig file before proceeding if ( !hasCompositeEnabled( @@ -298,7 +358,13 @@ function updateTsConfigReferences( dep.data.root, possibleRuntimeTsConfigFileName ); - if (tree.exists(possibleRuntimeTsConfigPath)) { + if ( + tsconfigExists( + tree, + rawTsconfigContentsCache, + possibleRuntimeTsConfigPath + ) + ) { // Check composite is true in the dependency runtime tsconfig file before proceeding if ( !hasCompositeEnabled( @@ -340,7 +406,12 @@ function updateTsConfigReferences( hasChanges ||= newReferencesSet.size !== originalReferencesSet.size; if (hasChanges) { - patchTsconfigJsonReferences(tree, tsConfigPath, references); + patchTsconfigJsonReferences( + tree, + rawTsconfigContentsCache, + tsConfigPath, + references + ); } return hasChanges; @@ -410,13 +481,18 @@ function normalizeReferencePath(path: string): string { .replace(/^\.\//, ''); } -function isInternalProjectReference( +function isProjectReferenceWithinNxProject( tree: Tree, + rawTsconfigContentsCache: Map, refTsConfigPath: string, projectRoot: string, projectRoots: Set ): boolean { - let currentPath = getTsConfigDirName(tree, refTsConfigPath); + let currentPath = getTsConfigDirName( + tree, + rawTsconfigContentsCache, + refTsConfigPath + ); if (relative(projectRoot, currentPath).startsWith('..')) { // it's outside of the project root, so it's an external project reference @@ -435,8 +511,16 @@ function isInternalProjectReference( return true; } -function getTsConfigDirName(tree: Tree, tsConfigPath: string): string { - return tree.isFile(tsConfigPath) +function getTsConfigDirName( + tree: Tree, + rawTsconfigContentsCache: Map, + tsConfigPath: string +): string { + return ( + rawTsconfigContentsCache.has(tsConfigPath) + ? true + : tree.isFile(tsConfigPath) + ) ? dirname(tsConfigPath) : normalize(tsConfigPath); } @@ -462,12 +546,22 @@ function getTsConfigPathFromReferencePath( */ function patchTsconfigJsonReferences( tree: Tree, + rawTsconfigContentsCache: Map, tsconfigPath: string, updatedReferences: { path: string }[] ) { - const jsonContents = tree.read(tsconfigPath).toString(); - const edits = modify(jsonContents, ['references'], updatedReferences, {}); - const updatedJsonContents = applyEdits(jsonContents, edits); + const stringifiedJsonContents = readRawTsconfigContents( + tree, + rawTsconfigContentsCache, + tsconfigPath + ); + const edits = modify( + stringifiedJsonContents, + ['references'], + updatedReferences, + {} + ); + const updatedJsonContents = applyEdits(stringifiedJsonContents, edits); // The final contents will be formatted by formatFiles() later tree.write(tsconfigPath, updatedJsonContents); } From bb0f3ec269bd8eb463026abcf15b449f2985dca6 Mon Sep 17 00:00:00 2001 From: James Henry Date: Tue, 20 Aug 2024 16:01:03 +0400 Subject: [PATCH 8/8] chore(js): apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leosvel PĂ©rez Espinosa --- .../js/src/generators/typescript-sync/typescript-sync.spec.ts | 4 ++-- packages/js/src/generators/typescript-sync/typescript-sync.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts index 069e7a00ccc7e..5b6576439c8cc 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.spec.ts @@ -289,7 +289,7 @@ describe('syncGenerator()', () => { }); it('should not add a reference if the internally referenced tsconfig.json does not have composite: true', async () => { - // Delete composite from a, causing it to now show up in the final tsconfig.json snapshot + // Delete composite from a, causing it to not show up in the final tsconfig.json snapshot writeJson(tree, 'packages/a/tsconfig.json', { compilerOptions: {}, }); @@ -490,7 +490,7 @@ describe('syncGenerator()', () => { addProject('foo', ['bar'], ['tsconfig.build.json']); addProject('bar', [], ['tsconfig.build.json']); - // Delete composite from bar, causing it to now show up in the final tsconfig.json snapshots below + // Delete composite from bar, causing it to not show up in the final tsconfig.json snapshots below writeJson(tree, 'packages/bar/tsconfig.json', { compilerOptions: {}, }); diff --git a/packages/js/src/generators/typescript-sync/typescript-sync.ts b/packages/js/src/generators/typescript-sync/typescript-sync.ts index dd8c629001324..652401a84f34d 100644 --- a/packages/js/src/generators/typescript-sync/typescript-sync.ts +++ b/packages/js/src/generators/typescript-sync/typescript-sync.ts @@ -261,7 +261,7 @@ function readRawTsconfigContents( if (!rawTsconfigContentsCache.has(tsconfigPath)) { rawTsconfigContentsCache.set( tsconfigPath, - tree.read(tsconfigPath, 'utf-8').toString() + tree.read(tsconfigPath, 'utf-8') ); } return rawTsconfigContentsCache.get(tsconfigPath);