@nx/js 20.5.0-beta.3 → 20.5.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/migrations.json CHANGED
@@ -137,6 +137,15 @@
137
137
  "alwaysAddToPackageJson": false
138
138
  }
139
139
  }
140
+ },
141
+ "20.5.0": {
142
+ "version": "20.5.0-beta.3",
143
+ "packages": {
144
+ "verdaccio": {
145
+ "version": "^6.0.5",
146
+ "alwaysAddToPackageJson": false
147
+ }
148
+ }
140
149
  }
141
150
  }
142
151
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/js",
3
- "version": "20.5.0-beta.3",
3
+ "version": "20.5.0-beta.4",
4
4
  "private": false,
5
5
  "description": "The JS plugin for Nx contains executors and generators that provide the best experience for developing JavaScript and TypeScript projects. ",
6
6
  "repository": {
@@ -39,8 +39,8 @@
39
39
  "@babel/preset-env": "^7.23.2",
40
40
  "@babel/preset-typescript": "^7.22.5",
41
41
  "@babel/runtime": "^7.22.6",
42
- "@nx/devkit": "20.5.0-beta.3",
43
- "@nx/workspace": "20.5.0-beta.3",
42
+ "@nx/devkit": "20.5.0-beta.4",
43
+ "@nx/workspace": "20.5.0-beta.4",
44
44
  "@zkochan/js-yaml": "0.0.7",
45
45
  "babel-plugin-const-enum": "^1.0.1",
46
46
  "babel-plugin-macros": "^3.1.0",
@@ -64,7 +64,7 @@
64
64
  "tslib": "^2.3.0"
65
65
  },
66
66
  "peerDependencies": {
67
- "verdaccio": "^5.0.4"
67
+ "verdaccio": "^6.0.5"
68
68
  },
69
69
  "peerDependenciesMeta": {
70
70
  "verdaccio": {
@@ -34,19 +34,37 @@ async function runExecutor(options, context) {
34
34
  const packageJson = (0, devkit_1.readJsonFile)(packageJsonPath);
35
35
  const packageName = packageJson.name;
36
36
  /**
37
- * pnpm supports dynamically updating locally linked packages during its packing phase, but other package managers do not.
38
- * Therefore, protect the user from publishing invalid packages by checking if it contains local dependency protocols.
37
+ * Whether or not dynamically replacing local dependency protocols (such as "workspace:*") is supported during `nx release publish` is
38
+ * dependent on the package manager the user is using.
39
+ *
40
+ * npm does not support the workspace protocol at all, and `npm publish` does not support dynamically updating locally linked packages
41
+ * during its packing phase, so we give the user a clear error message informing them of that.
42
+ *
43
+ * - `pnpm publish` provides ideal support, it has the possibility of providing JSON output consistent with npm
44
+ * - `bun publish`, provides very good support, including all the flags we need apart from the JSON output, so we just have to accept that
45
+ * it will look and feel different and print what it gives us and perform one bit of string manipulation for the dry-run case.
46
+ * - `yarn npm publish`, IS NOT YET SUPPORTED, and will be tricky because it does not support the majority of the flags we need. However, it
47
+ * does support replacing local dependency protocols with the correct version during its packing phase.
39
48
  */
40
- if (pm !== 'pnpm') {
49
+ if (pm === 'npm' || pm === 'yarn') {
41
50
  const depTypes = ['dependencies', 'devDependencies', 'peerDependencies'];
42
51
  for (const depType of depTypes) {
43
52
  const deps = packageJson[depType];
44
53
  if (deps) {
45
54
  for (const depName in deps) {
46
55
  if ((0, is_locally_linked_package_version_1.isLocallyLinkedPackageVersion)(deps[depName])) {
47
- console.error(`Error: Cannot publish package "${packageName}" because it contains a local dependency protocol in its "${depType}", and your package manager is ${pm}.
56
+ if (pm === 'npm') {
57
+ console.error(`Error: Cannot publish package "${packageName}" because it contains a local dependency protocol in its "${depType}", and your package manager is npm.
58
+
59
+ Please update the local dependency on "${depName}" to be a valid semantic version (e.g. using \`nx release\`) before publishing, or switch to pnpm or bun as a package manager, which support dynamically replacing these protocols during publishing.`);
60
+ }
61
+ else if (pm === 'yarn') {
62
+ console.error(`Error: Cannot publish package "${packageName}" because it contains a local dependency protocol in its "${depType}", and your package manager is yarn.
63
+
64
+ Currently, yarn is not supported for this use case because its \`yarn npm publish\` command does not support the customization needed.
48
65
 
49
- Please update the local dependency on "${depName}" to be a valid semantic version (e.g. using \`nx release\`) before publishing, or switch to pnpm as a package manager, which supports dynamically replacing these protocols during publishing.`);
66
+ Please update the local dependency on "${depName}" to be a valid semantic version (e.g. using \`nx release\`) before publishing, or switch to pnpm or bun as a package manager, which support dynamically replacing these protocols during publishing.`);
67
+ }
50
68
  return {
51
69
  success: false,
52
70
  };
@@ -184,10 +202,13 @@ Please update the local dependency on "${depName}" to be a valid semantic versio
184
202
  * JSON output under the name of the package in that case (and it would need to be handled below).
185
203
  */
186
204
  const publishCommandSegments = [
187
- pm === 'pnpm'
188
- ? // Unlike npm, pnpm publish does not support a custom registryConfigKey option, and will error on uncommitted changes by default if --no-git-checks is not set
189
- `pnpm publish "${packageRoot}" --json --registry="${registry}" --tag=${tag} --no-git-checks`
190
- : `npm publish "${packageRoot}" --json --"${registryConfigKey}=${registry}" --tag=${tag}`,
205
+ pm === 'bun'
206
+ ? // Unlike npm, bun publish does not support a custom registryConfigKey option
207
+ `bun publish --cwd="${packageRoot}" --json --registry="${registry}" --tag=${tag}`
208
+ : pm === 'pnpm'
209
+ ? // Unlike npm, pnpm publish does not support a custom registryConfigKey option, and will error on uncommitted changes by default if --no-git-checks is not set
210
+ `pnpm publish "${packageRoot}" --json --registry="${registry}" --tag=${tag} --no-git-checks`
211
+ : `npm publish "${packageRoot}" --json --"${registryConfigKey}=${registry}" --tag=${tag}`,
191
212
  ];
192
213
  if (options.otp) {
193
214
  publishCommandSegments.push(`--otp=${options.otp}`);
@@ -206,6 +227,23 @@ Please update the local dependency on "${depName}" to be a valid semantic versio
206
227
  stdio: ['ignore', 'pipe', 'pipe'],
207
228
  windowsHide: false,
208
229
  });
230
+ // If in dry-run mode, the version on disk will not represent the version that would be published, so we scrub it from the output to avoid confusion.
231
+ const dryRunVersionPlaceholder = 'X.X.X-dry-run';
232
+ const publishSummaryMessage = isDryRun
233
+ ? `Would publish to ${registry} with tag "${tag}", but ${chalk.keyword('orange')('[dry-run]')} was set`
234
+ : `Published to ${registry} with tag "${tag}"`;
235
+ // bun publish does not support outputting JSON, so we need to modify and print the output string directly
236
+ if (pm === 'bun') {
237
+ let outputStr = output.toString();
238
+ if (isDryRun) {
239
+ outputStr = outputStr.replace(new RegExp(`${packageJson.name}@${packageJson.version}`, 'g'), `${packageJson.name}@${dryRunVersionPlaceholder}`);
240
+ }
241
+ console.log(outputStr);
242
+ console.log(publishSummaryMessage);
243
+ return {
244
+ success: true,
245
+ };
246
+ }
209
247
  /**
210
248
  * We cannot JSON.parse the output directly because if the user is using lifecycle scripts, npm/pnpm will mix its publish output with the JSON output all on stdout.
211
249
  * Additionally, we want to capture and show the lifecycle script outputs as beforeJsonData and afterJsonData and print them accordingly below.
@@ -217,8 +255,6 @@ Please update the local dependency on "${depName}" to be a valid semantic versio
217
255
  success: false,
218
256
  };
219
257
  }
220
- // If in dry-run mode, the version on disk will not represent the version that would be published, so we scrub it from the output to avoid confusion.
221
- const dryRunVersionPlaceholder = 'X.X.X-dry-run';
222
258
  if (isDryRun) {
223
259
  for (const [key, val] of Object.entries(jsonData)) {
224
260
  if (typeof val !== 'string') {
@@ -235,18 +271,23 @@ Please update the local dependency on "${depName}" to be a valid semantic versio
235
271
  if (typeof afterJsonData === 'string' && afterJsonData.trim().length > 0) {
236
272
  console.log(afterJsonData);
237
273
  }
238
- if (isDryRun) {
239
- console.log(`Would publish to ${registry} with tag "${tag}", but ${chalk.keyword('orange')('[dry-run]')} was set`);
240
- }
241
- else {
242
- console.log(`Published to ${registry} with tag "${tag}"`);
243
- }
274
+ // Print the summary message after the JSON data has been printed
275
+ console.log(publishSummaryMessage);
244
276
  return {
245
277
  success: true,
246
278
  };
247
279
  }
248
280
  catch (err) {
249
281
  try {
282
+ // bun publish does not support outputting JSON, so we cannot perform any further processing
283
+ if (pm === 'bun') {
284
+ console.error(`bun publish error:`);
285
+ console.error(err.stderr?.toString() || '');
286
+ console.error(err.stdout?.toString() || '');
287
+ return {
288
+ success: false,
289
+ };
290
+ }
250
291
  const stdoutData = JSON.parse(err.stdout?.toString() || '{}');
251
292
  console.error(`${pm} publish error:`);
252
293
  if (stdoutData.error?.summary) {
@@ -191,7 +191,7 @@ To fix this you will either need to add a package.json file at that location, or
191
191
  const releaseTagPattern = options.releaseGroup.releaseTagPattern;
192
192
  latestMatchingGitTag = await (0, git_1.getLatestGitTagForPattern)(releaseTagPattern, {
193
193
  projectName: project.name,
194
- });
194
+ }, options.releaseGroup.releaseTagPatternCheckAllBranchesWhen);
195
195
  if (!latestMatchingGitTag) {
196
196
  if (options.fallbackCurrentVersionResolver === 'disk') {
197
197
  if (!currentVersionFromDisk &&
@@ -404,7 +404,7 @@ To fix this you will either need to add a package.json file at that location, or
404
404
  const allDependentProjects = Object.values(localPackageDependencies)
405
405
  .flat()
406
406
  .filter((localPackageDependency) => {
407
- return localPackageDependency.target === project.name;
407
+ return localPackageDependency.target === projectName;
408
408
  });
409
409
  const includeTransitiveDependents = updateDependents !== 'never' &&
410
410
  options.releaseGroup.projectsRelationship === 'independent';
@@ -423,9 +423,10 @@ To fix this you will either need to add a package.json file at that location, or
423
423
  const dependentProjectsOutsideCurrentBatch = [];
424
424
  // Track circular dependencies using value of project1:project2
425
425
  const circularDependencies = new Set();
426
+ const projectsDependOnCurrentProject = localPackageDependencies[projectName]?.map((localPackageDependencies) => localPackageDependencies.target) ?? [];
426
427
  for (const dependentProject of allDependentProjects) {
427
428
  // Track circular dependencies (add both directions for easy look up)
428
- if (dependentProject.target === projectName) {
429
+ if (projectsDependOnCurrentProject.includes(dependentProject.source)) {
429
430
  circularDependencies.add(`${dependentProject.source}:${dependentProject.target}`);
430
431
  circularDependencies.add(`${dependentProject.target}:${dependentProject.source}`);
431
432
  }
@@ -15,6 +15,7 @@ const util_1 = require("./util");
15
15
  const pmc = (0, devkit_1.getPackageManagerCommand)();
16
16
  let tsConfigCache;
17
17
  const tsConfigCachePath = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, 'tsconfig-files.hash');
18
+ let cache;
18
19
  function readFromCache(cachePath) {
19
20
  try {
20
21
  return process.env.NX_CACHE_PROJECT_GRAPH !== 'false'
@@ -43,11 +44,10 @@ exports.createNodesV2 = [
43
44
  const optionsHash = (0, file_hasher_1.hashObject)(options);
44
45
  const targetsCachePath = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, `tsc-${optionsHash}.hash`);
45
46
  const targetsCache = readFromCache(targetsCachePath);
46
- tsConfigCache =
47
- readFromCache(tsConfigCachePath);
47
+ cache = { fileHashes: {}, rawFiles: {}, isExternalProjectReference: {} };
48
+ initializeTsConfigCache(configFilePaths, context.workspaceRoot);
48
49
  const normalizedOptions = normalizePluginOptions(options);
49
- const lockFileHash = (0, file_hasher_1.hashFile)((0, node_path_1.join)(context.workspaceRoot, (0, lock_file_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot))));
50
- const { configFilePaths: validConfigFilePaths, hashes, projectRoots, } = await resolveValidConfigFilesAndHashes(configFilePaths, optionsHash, lockFileHash, context);
50
+ const { configFilePaths: validConfigFilePaths, hashes, projectRoots, } = await resolveValidConfigFilesAndHashes(configFilePaths, optionsHash, context);
51
51
  try {
52
52
  return await (0, devkit_1.createNodesFromFiles)((configFilePath, options, context, idx) => {
53
53
  const projectRoot = projectRoots[idx];
@@ -67,7 +67,7 @@ exports.createNodesV2 = [
67
67
  }
68
68
  finally {
69
69
  writeToCache(targetsCachePath, targetsCache);
70
- writeToCache(tsConfigCachePath, tsConfigCache);
70
+ writeToCache(tsConfigCachePath, toRelativePaths(tsConfigCache, context.workspaceRoot));
71
71
  }
72
72
  },
73
73
  ];
@@ -80,9 +80,10 @@ exports.createNodes = [
80
80
  return {};
81
81
  }
82
82
  const normalizedOptions = normalizePluginOptions(options);
83
- tsConfigCache =
84
- readFromCache(tsConfigCachePath);
83
+ cache = { fileHashes: {}, rawFiles: {}, isExternalProjectReference: {} };
84
+ initializeTsConfigCache([configFilePath], context.workspaceRoot);
85
85
  const { targets } = buildTscTargets((0, node_path_1.join)(context.workspaceRoot, configFilePath), projectRoot, normalizedOptions, context);
86
+ writeToCache(tsConfigCachePath, toRelativePaths(tsConfigCache, context.workspaceRoot));
86
87
  return {
87
88
  projects: {
88
89
  [projectRoot]: {
@@ -93,11 +94,11 @@ exports.createNodes = [
93
94
  };
94
95
  },
95
96
  ];
96
- async function resolveValidConfigFilesAndHashes(configFilePaths, optionsHash, lockFileHash, context) {
97
+ async function resolveValidConfigFilesAndHashes(configFilePaths, optionsHash, context) {
98
+ const lockFileHash = (0, file_hasher_1.hashFile)((0, node_path_1.join)(context.workspaceRoot, (0, lock_file_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot)))) ?? '';
97
99
  const validConfigFilePaths = [];
98
100
  const hashes = [];
99
101
  const projectRoots = [];
100
- const fileHashesCache = {};
101
102
  for await (const configFilePath of configFilePaths) {
102
103
  const projectRoot = (0, node_path_1.dirname)(configFilePath);
103
104
  if (!checkIfConfigFileShouldBeProject(configFilePath, projectRoot, context)) {
@@ -105,7 +106,7 @@ async function resolveValidConfigFilesAndHashes(configFilePaths, optionsHash, lo
105
106
  }
106
107
  projectRoots.push(projectRoot);
107
108
  validConfigFilePaths.push(configFilePath);
108
- hashes.push(await getConfigFileHash(configFilePath, context.workspaceRoot, projectRoot, optionsHash, lockFileHash, fileHashesCache));
109
+ hashes.push(await getConfigFileHash(configFilePath, context.workspaceRoot, projectRoot, optionsHash, lockFileHash));
109
110
  }
110
111
  return { configFilePaths: validConfigFilePaths, hashes, projectRoots };
111
112
  }
@@ -121,10 +122,10 @@ async function resolveValidConfigFilesAndHashes(configFilePaths, optionsHash, lo
121
122
  * - hash of the plugin options
122
123
  * - current config file path
123
124
  */
124
- async function getConfigFileHash(configFilePath, workspaceRoot, projectRoot, optionsHash, lockFileHash, fileHashesCache) {
125
+ async function getConfigFileHash(configFilePath, workspaceRoot, projectRoot, optionsHash, lockFileHash) {
125
126
  const fullConfigPath = (0, node_path_1.join)(workspaceRoot, configFilePath);
126
- const tsConfig = readCachedTsConfig(fullConfigPath, workspaceRoot);
127
- const extendedConfigFiles = getExtendedConfigFiles(fullConfigPath, tsConfig, workspaceRoot);
127
+ const tsConfig = retrieveTsConfigFromCache(fullConfigPath, workspaceRoot);
128
+ const extendedConfigFiles = getExtendedConfigFiles(tsConfig, workspaceRoot);
128
129
  const internalReferencedFiles = resolveInternalProjectReferences(tsConfig, workspaceRoot, projectRoot);
129
130
  const externalProjectReferences = resolveShallowExternalProjectReferences(tsConfig, workspaceRoot, projectRoot);
130
131
  let packageJson = null;
@@ -135,13 +136,11 @@ async function getConfigFileHash(configFilePath, workspaceRoot, projectRoot, opt
135
136
  return (0, file_hasher_1.hashArray)([
136
137
  ...[
137
138
  fullConfigPath,
138
- ...extendedConfigFiles.files,
139
- ...Object.keys(internalReferencedFiles),
140
- ...Object.keys(externalProjectReferences),
141
- ].map((file) => {
142
- fileHashesCache[file] ??= (0, file_hasher_1.hashFile)(file);
143
- return fileHashesCache[file];
144
- }),
139
+ ...extendedConfigFiles.files.sort(),
140
+ ...Object.keys(internalReferencedFiles).sort(),
141
+ ...Object.keys(externalProjectReferences).sort(),
142
+ ].map((file) => getFileHash(file, workspaceRoot)),
143
+ ...extendedConfigFiles.packages.sort(),
145
144
  lockFileHash,
146
145
  optionsHash,
147
146
  ...(packageJson ? [(0, file_hasher_1.hashObject)(packageJson)] : []),
@@ -176,7 +175,7 @@ function checkIfConfigFileShouldBeProject(configFilePath, projectRoot, context)
176
175
  function buildTscTargets(configFilePath, projectRoot, options, context) {
177
176
  const targets = {};
178
177
  const namedInputs = (0, get_named_inputs_1.getNamedInputs)(projectRoot, context);
179
- const tsConfig = readCachedTsConfig(configFilePath, context.workspaceRoot);
178
+ const tsConfig = retrieveTsConfigFromCache(configFilePath, context.workspaceRoot);
180
179
  let internalProjectReferences;
181
180
  // Typecheck target
182
181
  if ((0, node_path_1.basename)(configFilePath) === 'tsconfig.json' && options.typecheck) {
@@ -247,7 +246,7 @@ function buildTscTargets(configFilePath, projectRoot, options, context) {
247
246
  function getInputs(namedInputs, configFilePath, tsConfig, internalProjectReferences, workspaceRoot, projectRoot) {
248
247
  const configFiles = new Set();
249
248
  const externalDependencies = ['typescript'];
250
- const extendedConfigFiles = getExtendedConfigFiles(configFilePath, tsConfig, workspaceRoot);
249
+ const extendedConfigFiles = getExtendedConfigFiles(tsConfig, workspaceRoot);
251
250
  extendedConfigFiles.files.forEach((configPath) => {
252
251
  configFiles.add(configPath);
253
252
  });
@@ -460,23 +459,17 @@ function pathToInputOrOutput(path, workspaceRoot, projectRoot) {
460
459
  }
461
460
  return (0, devkit_1.joinPathFragments)('{projectRoot}', pathRelativeToProjectRoot);
462
461
  }
463
- function getExtendedConfigFiles(tsConfigPath, tsConfig, workspaceRoot) {
462
+ function getExtendedConfigFiles(tsConfig, workspaceRoot) {
464
463
  const extendedConfigFiles = new Set();
465
464
  const extendedExternalPackages = new Set();
466
- let currentConfigPath = tsConfigPath;
467
- let currentConfig = tsConfig;
468
- while (currentConfig.raw?.extends) {
469
- const extendedConfigPath = resolveExtendedTsConfigPath(currentConfig.raw.extends, (0, node_path_1.dirname)(currentConfigPath));
470
- if (!extendedConfigPath) {
465
+ let currentExtendedConfigFile = tsConfig.extendedConfigFile;
466
+ while (currentExtendedConfigFile) {
467
+ if (currentExtendedConfigFile.externalPackage) {
468
+ extendedExternalPackages.add(currentExtendedConfigFile.externalPackage);
471
469
  break;
472
470
  }
473
- if (extendedConfigPath.externalPackage) {
474
- extendedExternalPackages.add(extendedConfigPath.externalPackage);
475
- break;
476
- }
477
- extendedConfigFiles.add(extendedConfigPath.filePath);
478
- currentConfig = readCachedTsConfig(extendedConfigPath.filePath, workspaceRoot);
479
- currentConfigPath = extendedConfigPath.filePath;
471
+ extendedConfigFiles.add(currentExtendedConfigFile.filePath);
472
+ currentExtendedConfigFile = retrieveTsConfigFromCache(currentExtendedConfigFile.filePath, workspaceRoot).extendedConfigFile;
480
473
  }
481
474
  return {
482
475
  files: Array.from(extendedConfigFiles),
@@ -503,7 +496,7 @@ function resolveInternalProjectReferences(tsConfig, workspaceRoot, projectRoot,
503
496
  if (!refConfigPath.endsWith('.json')) {
504
497
  refConfigPath = (0, node_path_1.join)(refConfigPath, 'tsconfig.json');
505
498
  }
506
- projectReferences[refConfigPath] = readCachedTsConfig(refConfigPath, workspaceRoot);
499
+ projectReferences[refConfigPath] = retrieveTsConfigFromCache(refConfigPath, workspaceRoot);
507
500
  resolveInternalProjectReferences(projectReferences[refConfigPath], workspaceRoot, projectRoot, projectReferences);
508
501
  }
509
502
  return projectReferences;
@@ -526,7 +519,7 @@ function resolveShallowExternalProjectReferences(tsConfig, workspaceRoot, projec
526
519
  if (!refConfigPath.endsWith('.json')) {
527
520
  refConfigPath = (0, node_path_1.join)(refConfigPath, 'tsconfig.json');
528
521
  }
529
- projectReferences[refConfigPath] = readCachedTsConfig(refConfigPath, workspaceRoot);
522
+ projectReferences[refConfigPath] = retrieveTsConfigFromCache(refConfigPath, workspaceRoot);
530
523
  }
531
524
  }
532
525
  return projectReferences;
@@ -552,7 +545,7 @@ function hasExternalProjectReferences(tsConfigPath, tsConfig, workspaceRoot, pro
552
545
  if (!refConfigPath.endsWith('.json')) {
553
546
  refConfigPath = (0, node_path_1.join)(refConfigPath, 'tsconfig.json');
554
547
  }
555
- const refTsConfig = readCachedTsConfig(refConfigPath, workspaceRoot);
548
+ const refTsConfig = retrieveTsConfigFromCache(refConfigPath, workspaceRoot);
556
549
  const result = hasExternalProjectReferences(refConfigPath, refTsConfig, workspaceRoot, projectRoot, seen);
557
550
  if (result) {
558
551
  return true;
@@ -561,21 +554,28 @@ function hasExternalProjectReferences(tsConfigPath, tsConfig, workspaceRoot, pro
561
554
  return false;
562
555
  }
563
556
  function isExternalProjectReference(refTsConfigPath, workspaceRoot, projectRoot) {
557
+ const relativePath = posix.relative(workspaceRoot, refTsConfigPath);
558
+ if (cache.isExternalProjectReference[relativePath] !== undefined) {
559
+ return cache.isExternalProjectReference[relativePath];
560
+ }
564
561
  const absoluteProjectRoot = (0, node_path_1.join)(workspaceRoot, projectRoot);
565
562
  let currentPath = getTsConfigDirName(refTsConfigPath);
566
563
  if ((0, node_path_1.relative)(absoluteProjectRoot, currentPath).startsWith('..')) {
567
564
  // it's outside of the project root, so it's an external project reference
565
+ cache.isExternalProjectReference[relativePath] = true;
568
566
  return true;
569
567
  }
570
568
  while (currentPath !== absoluteProjectRoot) {
571
569
  if ((0, node_fs_1.existsSync)((0, node_path_1.join)(currentPath, 'package.json')) ||
572
570
  (0, node_fs_1.existsSync)((0, node_path_1.join)(currentPath, 'project.json'))) {
573
571
  // it's inside a nested project root, so it's and external project reference
572
+ cache.isExternalProjectReference[relativePath] = true;
574
573
  return true;
575
574
  }
576
575
  currentPath = (0, node_path_1.dirname)(currentPath);
577
576
  }
578
577
  // it's inside the project root, so it's an internal project reference
578
+ cache.isExternalProjectReference[relativePath] = false;
579
579
  return false;
580
580
  }
581
581
  function getTsConfigDirName(tsConfigPath) {
@@ -583,32 +583,79 @@ function getTsConfigDirName(tsConfigPath) {
583
583
  ? (0, node_path_1.dirname)(tsConfigPath)
584
584
  : (0, node_path_1.normalize)(tsConfigPath);
585
585
  }
586
- function readCachedTsConfig(tsConfigPath, workspaceRoot) {
587
- const relativeTsConfigPath = posix.relative(workspaceRoot, tsConfigPath);
588
- const timestamp = (0, node_fs_1.statSync)(tsConfigPath).mtimeMs;
589
- if (tsConfigCache[relativeTsConfigPath]?.timestamp === timestamp) {
590
- return tsConfigCache[relativeTsConfigPath].data;
591
- }
592
- const tsConfig = readTsConfig(tsConfigPath);
593
- tsConfigCache[relativeTsConfigPath] = {
586
+ function retrieveTsConfigFromCache(tsConfigPath, workspaceRoot) {
587
+ const relativePath = posix.relative(workspaceRoot, tsConfigPath);
588
+ // we don't need to check the hash if it's in the cache, because we've already
589
+ // checked it when we initially populated the cache
590
+ return tsConfigCache[relativePath]
591
+ ? tsConfigCache[relativePath].data
592
+ : readTsConfigAndCache(tsConfigPath, workspaceRoot);
593
+ }
594
+ function initializeTsConfigCache(configFilePaths, workspaceRoot) {
595
+ tsConfigCache = toAbsolutePaths(readFromCache(tsConfigCachePath), workspaceRoot);
596
+ // ensure hashes are checked and the cache is invalidated and populated as needed
597
+ for (const configFilePath of configFilePaths) {
598
+ const fullConfigPath = (0, node_path_1.join)(workspaceRoot, configFilePath);
599
+ readTsConfigAndCache(fullConfigPath, workspaceRoot);
600
+ }
601
+ }
602
+ function readTsConfigAndCache(tsConfigPath, workspaceRoot) {
603
+ const relativePath = posix.relative(workspaceRoot, tsConfigPath);
604
+ const hash = getFileHash(tsConfigPath, workspaceRoot);
605
+ let extendedFilesHash;
606
+ if (tsConfigCache[relativePath] &&
607
+ tsConfigCache[relativePath].hash === hash) {
608
+ extendedFilesHash = getExtendedFilesHash(tsConfigCache[relativePath].data.extendedConfigFile, workspaceRoot);
609
+ if (tsConfigCache[relativePath].extendedFilesHash === extendedFilesHash) {
610
+ return tsConfigCache[relativePath].data;
611
+ }
612
+ }
613
+ const tsConfig = readTsConfig(tsConfigPath, workspaceRoot);
614
+ const extendedConfigFile = tsConfig.raw?.extends
615
+ ? resolveExtendedTsConfigPath(tsConfig.raw.extends, (0, node_path_1.dirname)(tsConfigPath))
616
+ : null;
617
+ extendedFilesHash ??= getExtendedFilesHash(extendedConfigFile, workspaceRoot);
618
+ tsConfigCache[relativePath] = {
594
619
  data: {
595
620
  options: tsConfig.options,
596
621
  projectReferences: tsConfig.projectReferences,
597
622
  raw: tsConfig.raw,
623
+ extendedConfigFile: extendedConfigFile ?? null,
598
624
  },
599
- timestamp,
625
+ hash,
626
+ extendedFilesHash,
600
627
  };
601
- return tsConfig;
628
+ return tsConfigCache[relativePath].data;
629
+ }
630
+ function getExtendedFilesHash(extendedConfigFile, workspaceRoot) {
631
+ const hashes = [];
632
+ if (!extendedConfigFile) {
633
+ return '';
634
+ }
635
+ if (extendedConfigFile.externalPackage) {
636
+ hashes.push(extendedConfigFile.externalPackage);
637
+ }
638
+ else if (extendedConfigFile.filePath) {
639
+ hashes.push(getFileHash(extendedConfigFile.filePath, workspaceRoot));
640
+ hashes.push(getExtendedFilesHash(readTsConfigAndCache(extendedConfigFile.filePath, workspaceRoot)
641
+ .extendedConfigFile, workspaceRoot));
642
+ }
643
+ return hashes.join('|');
602
644
  }
603
645
  let ts;
604
- function readTsConfig(tsConfigPath) {
646
+ function readTsConfig(tsConfigPath, workspaceRoot) {
605
647
  if (!ts) {
606
648
  ts = require('typescript');
607
649
  }
608
- const readResult = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
650
+ const tsSys = {
651
+ ...ts.sys,
652
+ readFile: (path) => readFile(path, workspaceRoot),
653
+ readDirectory: () => [],
654
+ };
655
+ const readResult = ts.readConfigFile(tsConfigPath, tsSys.readFile);
609
656
  // read with a custom host that won't read directories which is only used
610
657
  // to identify the filenames included in the program, which we won't use
611
- return ts.parseJsonConfigFileContent(readResult.config, { ...ts.sys, readDirectory: () => [] }, (0, node_path_1.dirname)(tsConfigPath));
658
+ return ts.parseJsonConfigFileContent(readResult.config, tsSys, (0, node_path_1.dirname)(tsConfigPath));
612
659
  }
613
660
  function normalizePluginOptions(pluginOptions = {}) {
614
661
  const defaultTypecheckTargetName = 'typecheck';
@@ -668,3 +715,83 @@ function resolveExtendedTsConfigPath(tsConfigPath, directory) {
668
715
  return null;
669
716
  }
670
717
  }
718
+ function getFileHash(filePath, workspaceRoot) {
719
+ const relativePath = posix.relative(workspaceRoot, filePath);
720
+ if (!cache.fileHashes[relativePath]) {
721
+ const content = readFile(filePath, workspaceRoot);
722
+ cache.fileHashes[relativePath] = (0, file_hasher_1.hashArray)([content]);
723
+ }
724
+ return cache.fileHashes[relativePath];
725
+ }
726
+ function readFile(filePath, workspaceRoot) {
727
+ const relativePath = posix.relative(workspaceRoot, filePath);
728
+ if (!cache.rawFiles[relativePath]) {
729
+ const content = (0, node_fs_1.readFileSync)(filePath, 'utf8');
730
+ cache.rawFiles[relativePath] = content;
731
+ }
732
+ return cache.rawFiles[relativePath];
733
+ }
734
+ function toAbsolutePaths(cache, workspaceRoot) {
735
+ const updatedCache = {};
736
+ for (const [key, { data, extendedFilesHash, hash }] of Object.entries(cache)) {
737
+ updatedCache[key] = {
738
+ data: {
739
+ options: { noEmit: data.options.noEmit },
740
+ extendedConfigFile: data.extendedConfigFile,
741
+ },
742
+ extendedFilesHash,
743
+ hash,
744
+ };
745
+ if (data.options.rootDir) {
746
+ updatedCache[key].data.options.rootDir = (0, node_path_1.join)(workspaceRoot, data.options.rootDir);
747
+ }
748
+ if (data.options.outDir) {
749
+ updatedCache[key].data.options.outDir = (0, node_path_1.join)(workspaceRoot, data.options.outDir);
750
+ }
751
+ if (data.options.outFile) {
752
+ updatedCache[key].data.options.outFile = (0, node_path_1.join)(workspaceRoot, data.options.outFile);
753
+ }
754
+ if (data.options.tsBuildInfoFile) {
755
+ updatedCache[key].data.options.tsBuildInfoFile = (0, node_path_1.join)(workspaceRoot, data.options.tsBuildInfoFile);
756
+ }
757
+ if (data.extendedConfigFile?.filePath) {
758
+ updatedCache[key].data.extendedConfigFile.filePath = (0, node_path_1.join)(workspaceRoot, data.extendedConfigFile.filePath);
759
+ }
760
+ if (data.projectReferences) {
761
+ updatedCache[key].data.projectReferences = data.projectReferences.map((ref) => ({ ...ref, path: (0, node_path_1.join)(workspaceRoot, ref.path) }));
762
+ }
763
+ }
764
+ return updatedCache;
765
+ }
766
+ function toRelativePaths(cache, workspaceRoot) {
767
+ const updatedCache = {};
768
+ for (const [key, { data, extendedFilesHash, hash }] of Object.entries(cache)) {
769
+ updatedCache[key] = {
770
+ data: {
771
+ options: { noEmit: data.options.noEmit },
772
+ extendedConfigFile: data.extendedConfigFile,
773
+ },
774
+ extendedFilesHash,
775
+ hash,
776
+ };
777
+ if (data.options.rootDir) {
778
+ updatedCache[key].data.options.rootDir = posix.relative(workspaceRoot, data.options.rootDir);
779
+ }
780
+ if (data.options.outDir) {
781
+ updatedCache[key].data.options.outDir = posix.relative(workspaceRoot, data.options.outDir);
782
+ }
783
+ if (data.options.outFile) {
784
+ updatedCache[key].data.options.outFile = posix.relative(workspaceRoot, data.options.outFile);
785
+ }
786
+ if (data.options.tsBuildInfoFile) {
787
+ updatedCache[key].data.options.tsBuildInfoFile = posix.relative(workspaceRoot, data.options.tsBuildInfoFile);
788
+ }
789
+ if (data.extendedConfigFile?.filePath) {
790
+ updatedCache[key].data.extendedConfigFile.filePath = posix.relative(workspaceRoot, data.extendedConfigFile.filePath);
791
+ }
792
+ if (data.projectReferences) {
793
+ updatedCache[key].data.projectReferences = data.projectReferences.map((ref) => ({ ...ref, path: posix.relative(workspaceRoot, ref.path) }));
794
+ }
795
+ }
796
+ return updatedCache;
797
+ }
@@ -16,6 +16,7 @@ const fileutils_1 = require("nx/src/utils/fileutils");
16
16
  const output_1 = require("nx/src/utils/output");
17
17
  const path_1 = require("path");
18
18
  const ts_config_1 = require("./typescript/ts-config");
19
+ const crypto_1 = require("crypto");
19
20
  function isBuildable(target, node) {
20
21
  return (node.data.targets &&
21
22
  node.data.targets[target] &&
@@ -229,7 +230,7 @@ function computeCompilerOptionsPaths(tsConfig, dependencies) {
229
230
  return paths;
230
231
  }
231
232
  function createTmpTsConfig(tsconfigPath, workspaceRoot, projectRoot, dependencies, useWorkspaceAsBaseUrl = false) {
232
- const tmpTsConfigPath = (0, path_1.join)(workspaceRoot, 'tmp', projectRoot, process.env.NX_TASK_TARGET_TARGET ?? 'build', 'tsconfig.generated.json');
233
+ const tmpTsConfigPath = (0, path_1.join)(workspaceRoot, 'tmp', projectRoot, process.env.NX_TASK_TARGET_TARGET ?? 'build', `tsconfig.generated.${(0, crypto_1.randomUUID)()}.json`);
233
234
  if (tsconfigPath === tmpTsConfigPath) {
234
235
  return tsconfigPath;
235
236
  }
@@ -7,7 +7,7 @@ export declare const swcHelpersVersion = "~0.5.11";
7
7
  export declare const swcNodeVersion = "~1.9.1";
8
8
  export declare const tsLibVersion = "^2.3.0";
9
9
  export declare const typesNodeVersion = "18.16.9";
10
- export declare const verdaccioVersion = "^5.0.4";
10
+ export declare const verdaccioVersion = "^6.0.5";
11
11
  export declare const typescriptVersion = "~5.7.2";
12
12
  /**
13
13
  * The minimum version is currently determined from the lowest version
@@ -10,7 +10,7 @@ exports.swcHelpersVersion = '~0.5.11';
10
10
  exports.swcNodeVersion = '~1.9.1';
11
11
  exports.tsLibVersion = '^2.3.0';
12
12
  exports.typesNodeVersion = '18.16.9';
13
- exports.verdaccioVersion = '^5.0.4';
13
+ exports.verdaccioVersion = '^6.0.5';
14
14
  // Typescript
15
15
  exports.typescriptVersion = '~5.7.2';
16
16
  /**