@nx/js 20.5.0-beta.2 → 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.2",
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.2",
43
- "@nx/workspace": "20.5.0-beta.2",
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": {
@@ -88,7 +88,7 @@ async function* nodeExecutor(options, context) {
88
88
  start: async () => {
89
89
  // Wait for build to finish.
90
90
  const result = await buildResult;
91
- if (!result.success) {
91
+ if (result && !result.success) {
92
92
  // If in watch-mode, don't throw or else the process exits.
93
93
  if (options.watch) {
94
94
  if (!task.killed) {
@@ -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) {
@@ -8,7 +8,6 @@ const project_name_and_root_utils_1 = require("@nx/devkit/src/generators/project
8
8
  const prompt_1 = require("@nx/devkit/src/generators/prompt");
9
9
  const target_defaults_utils_1 = require("@nx/devkit/src/generators/target-defaults-utils");
10
10
  const log_show_project_command_1 = require("@nx/devkit/src/utils/log-show-project-command");
11
- const find_matching_projects_1 = require("nx/src/utils/find-matching-projects");
12
11
  const path_1 = require("path");
13
12
  const generator_prompts_1 = require("../../utils/generator-prompts");
14
13
  const update_package_json_1 = require("../../utils/package-json/update-package-json");
@@ -22,9 +21,9 @@ const ts_config_1 = require("../../utils/typescript/ts-config");
22
21
  const ts_solution_setup_1 = require("../../utils/typescript/ts-solution-setup");
23
22
  const versions_1 = require("../../utils/versions");
24
23
  const init_1 = require("../init/init");
25
- const generator_1 = require("../setup-verdaccio/generator");
26
24
  const sort_fields_1 = require("../../utils/package-json/sort-fields");
27
25
  const get_import_path_1 = require("../../utils/get-import-path");
26
+ const add_release_config_1 = require("./utils/add-release-config");
28
27
  const defaultOutputDirectory = 'dist';
29
28
  async function libraryGenerator(tree, schema) {
30
29
  return await libraryGeneratorInternal(tree, {
@@ -54,9 +53,6 @@ async function libraryGeneratorInternal(tree, schema) {
54
53
  if (!options.skipPackageJson) {
55
54
  tasks.push(addProjectDependencies(tree, options));
56
55
  }
57
- if (options.publishable) {
58
- tasks.push(await (0, generator_1.default)(tree, { ...options, skipFormat: true }));
59
- }
60
56
  if (options.bundler === 'rollup') {
61
57
  const { configurationGenerator } = (0, devkit_1.ensurePackage)('@nx/rollup', versions_1.nxVersion);
62
58
  await configurationGenerator(tree, {
@@ -151,9 +147,7 @@ async function libraryGeneratorInternal(tree, schema) {
151
147
  await (0, devkit_1.formatFiles)(tree);
152
148
  }
153
149
  if (options.publishable) {
154
- tasks.push(() => {
155
- logNxReleaseDocsInfo();
156
- });
150
+ tasks.push(await (0, add_release_config_1.releaseTasks)(tree));
157
151
  }
158
152
  // Always run install to link packages.
159
153
  if (options.isUsingTsSolutionConfig) {
@@ -220,26 +214,12 @@ async function configureProject(tree, options) {
220
214
  }
221
215
  }
222
216
  if (options.publishable) {
223
- if (!options.isUsingTsSolutionConfig) {
224
- const packageRoot = (0, devkit_1.joinPathFragments)(defaultOutputDirectory, '{projectRoot}');
225
- projectConfiguration.targets ??= {};
226
- projectConfiguration.targets['nx-release-publish'] = {
227
- options: {
228
- packageRoot,
229
- },
230
- };
231
- projectConfiguration.release = {
232
- version: {
233
- generatorOptions: {
234
- packageRoot,
235
- // using git tags to determine the current version is required here because
236
- // the version in the package root is overridden with every build
237
- currentVersionResolver: 'git-tag',
238
- },
239
- },
240
- };
217
+ if (options.isUsingTsSolutionConfig) {
218
+ await (0, add_release_config_1.addReleaseConfigForTsSolution)(tree, options.name, projectConfiguration);
219
+ }
220
+ else {
221
+ await (0, add_release_config_1.addReleaseConfigForNonTsSolution)(tree, options.name, projectConfiguration, defaultOutputDirectory);
241
222
  }
242
- await addProjectToNxReleaseConfig(tree, options, projectConfiguration);
243
223
  }
244
224
  if (!options.useProjectJson) {
245
225
  // we want the package.json as clean as possible, with the bare minimum
@@ -930,96 +910,6 @@ function determineEntryFields(options) {
930
910
  }
931
911
  }
932
912
  }
933
- function projectsConfigMatchesProject(projectsConfig, project) {
934
- if (!projectsConfig) {
935
- return false;
936
- }
937
- if (typeof projectsConfig === 'string') {
938
- projectsConfig = [projectsConfig];
939
- }
940
- const graph = {
941
- [project.name]: project,
942
- };
943
- const matchingProjects = (0, find_matching_projects_1.findMatchingProjects)(projectsConfig, graph);
944
- return matchingProjects.includes(project.name);
945
- }
946
- async function addProjectToNxReleaseConfig(tree, options, projectConfiguration) {
947
- const nxJson = (0, devkit_1.readNxJson)(tree);
948
- const addPreVersionCommand = () => {
949
- const pmc = (0, devkit_1.getPackageManagerCommand)();
950
- nxJson.release = {
951
- ...nxJson.release,
952
- version: {
953
- preVersionCommand: `${pmc.dlx} nx run-many -t build`,
954
- ...nxJson.release?.version,
955
- },
956
- };
957
- };
958
- if (!nxJson.release || (!nxJson.release.projects && !nxJson.release.groups)) {
959
- // skip adding any projects configuration since the new project should be
960
- // automatically included by nx release's default project detection logic
961
- addPreVersionCommand();
962
- (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
963
- return;
964
- }
965
- const project = {
966
- name: options.name,
967
- type: 'lib',
968
- data: {
969
- root: projectConfiguration.root,
970
- tags: projectConfiguration.tags,
971
- },
972
- };
973
- if (projectsConfigMatchesProject(nxJson.release.projects, project)) {
974
- devkit_1.output.log({
975
- title: `Project already included in existing release configuration`,
976
- });
977
- addPreVersionCommand();
978
- (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
979
- return;
980
- }
981
- if (Array.isArray(nxJson.release.projects)) {
982
- nxJson.release.projects.push(options.name);
983
- addPreVersionCommand();
984
- (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
985
- devkit_1.output.log({
986
- title: `Added project to existing release configuration`,
987
- });
988
- }
989
- if (nxJson.release.groups) {
990
- const allGroups = Object.entries(nxJson.release.groups);
991
- for (const [name, group] of allGroups) {
992
- if (projectsConfigMatchesProject(group.projects, project)) {
993
- addPreVersionCommand();
994
- (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
995
- return `Project already included in existing release configuration for group ${name}`;
996
- }
997
- }
998
- devkit_1.output.warn({
999
- title: `Could not find a release group that includes ${options.name}`,
1000
- bodyLines: [
1001
- `Ensure that ${options.name} is included in a release group's "projects" list in nx.json so it can be published with "nx release"`,
1002
- ],
1003
- });
1004
- addPreVersionCommand();
1005
- (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
1006
- return;
1007
- }
1008
- if (typeof nxJson.release.projects === 'string') {
1009
- nxJson.release.projects = [nxJson.release.projects, options.name];
1010
- addPreVersionCommand();
1011
- (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
1012
- devkit_1.output.log({
1013
- title: `Added project to existing release configuration`,
1014
- });
1015
- return;
1016
- }
1017
- }
1018
- function logNxReleaseDocsInfo() {
1019
- devkit_1.output.log({
1020
- title: `📦 To learn how to publish this library, see https://nx.dev/core-features/manage-releases.`,
1021
- });
1022
- }
1023
913
  function findRootJestPreset(tree) {
1024
914
  const ext = ['js', 'cjs', 'mjs'].find((ext) => tree.exists(`jest.preset.${ext}`));
1025
915
  return ext ? `jest.preset.${ext}` : null;
@@ -0,0 +1,11 @@
1
+ import { GeneratorCallback, ProjectConfiguration, Tree } from '@nx/devkit';
2
+ /**
3
+ * Adds release option in nx.json to build the project before versioning
4
+ */
5
+ export declare function addReleaseConfigForTsSolution(tree: Tree, projectName: string, projectConfiguration: ProjectConfiguration): Promise<void>;
6
+ /**
7
+ * Add release configuration for non-ts solution projects
8
+ * Add release option in project.json and add packageRoot to nx-release-publish target
9
+ */
10
+ export declare function addReleaseConfigForNonTsSolution(tree: Tree, projectName: string, projectConfiguration: ProjectConfiguration, defaultOutputDirectory?: string): Promise<ProjectConfiguration>;
11
+ export declare function releaseTasks(tree: Tree): Promise<GeneratorCallback>;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addReleaseConfigForTsSolution = addReleaseConfigForTsSolution;
4
+ exports.addReleaseConfigForNonTsSolution = addReleaseConfigForNonTsSolution;
5
+ exports.releaseTasks = releaseTasks;
6
+ const devkit_1 = require("@nx/devkit");
7
+ const find_matching_projects_1 = require("nx/src/utils/find-matching-projects");
8
+ const generator_1 = require("../../setup-verdaccio/generator");
9
+ /**
10
+ * Adds release option in nx.json to build the project before versioning
11
+ */
12
+ async function addReleaseConfigForTsSolution(tree, projectName, projectConfiguration) {
13
+ const nxJson = (0, devkit_1.readNxJson)(tree);
14
+ const addPreVersionCommand = () => {
15
+ const pmc = (0, devkit_1.getPackageManagerCommand)();
16
+ nxJson.release = {
17
+ ...nxJson.release,
18
+ version: {
19
+ preVersionCommand: `${pmc.dlx} nx run-many -t build`,
20
+ ...nxJson.release?.version,
21
+ },
22
+ };
23
+ };
24
+ // if the release configuration does not exist, it will be created
25
+ if (!nxJson.release || (!nxJson.release.projects && !nxJson.release.groups)) {
26
+ // skip adding any projects configuration since the new project should be
27
+ // automatically included by nx release's default project detection logic
28
+ addPreVersionCommand();
29
+ (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
30
+ return;
31
+ }
32
+ const project = {
33
+ name: projectName,
34
+ type: 'lib',
35
+ data: {
36
+ root: projectConfiguration.root,
37
+ tags: projectConfiguration.tags,
38
+ },
39
+ };
40
+ // if the project is already included in the release configuration, it will not be added again
41
+ if (projectsConfigMatchesProject(nxJson.release.projects, project)) {
42
+ devkit_1.output.log({
43
+ title: `Project already included in existing release configuration`,
44
+ });
45
+ addPreVersionCommand();
46
+ (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
47
+ return;
48
+ }
49
+ // if the release configuration is a string, it will be converted to an array and added to it
50
+ if (Array.isArray(nxJson.release.projects)) {
51
+ nxJson.release.projects.push(projectName);
52
+ addPreVersionCommand();
53
+ (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
54
+ devkit_1.output.log({
55
+ title: `Added project to existing release configuration`,
56
+ });
57
+ }
58
+ if (nxJson.release.groups) {
59
+ const allGroups = Object.entries(nxJson.release.groups);
60
+ for (const [name, group] of allGroups) {
61
+ if (projectsConfigMatchesProject(group.projects, project)) {
62
+ addPreVersionCommand();
63
+ (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
64
+ devkit_1.output.log({
65
+ title: `Project already included in existing release configuration for group ${name}`,
66
+ });
67
+ return;
68
+ }
69
+ }
70
+ devkit_1.output.warn({
71
+ title: `Could not find a release group that includes ${projectName}`,
72
+ bodyLines: [
73
+ `Ensure that ${projectName} is included in a release group's "projects" list in nx.json so it can be published with "nx release"`,
74
+ ],
75
+ });
76
+ addPreVersionCommand();
77
+ (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
78
+ return;
79
+ }
80
+ if (typeof nxJson.release.projects === 'string') {
81
+ nxJson.release.projects = [nxJson.release.projects, projectName];
82
+ addPreVersionCommand();
83
+ (0, devkit_1.writeJson)(tree, 'nx.json', nxJson);
84
+ devkit_1.output.log({
85
+ title: `Added project to existing release configuration`,
86
+ });
87
+ return;
88
+ }
89
+ }
90
+ /**
91
+ * Add release configuration for non-ts solution projects
92
+ * Add release option in project.json and add packageRoot to nx-release-publish target
93
+ */
94
+ async function addReleaseConfigForNonTsSolution(tree, projectName, projectConfiguration, defaultOutputDirectory = 'dist') {
95
+ const packageRoot = (0, devkit_1.joinPathFragments)(defaultOutputDirectory, '{projectRoot}');
96
+ projectConfiguration.targets ??= {};
97
+ projectConfiguration.targets['nx-release-publish'] = {
98
+ options: {
99
+ packageRoot,
100
+ },
101
+ };
102
+ projectConfiguration.release = {
103
+ version: {
104
+ generatorOptions: {
105
+ packageRoot,
106
+ // using git tags to determine the current version is required here because
107
+ // the version in the package root is overridden with every build
108
+ currentVersionResolver: 'git-tag',
109
+ fallbackCurrentVersionResolver: 'disk',
110
+ },
111
+ },
112
+ };
113
+ await addReleaseConfigForTsSolution(tree, projectName, projectConfiguration);
114
+ return projectConfiguration;
115
+ }
116
+ function projectsConfigMatchesProject(projectsConfig, project) {
117
+ if (!projectsConfig) {
118
+ return false;
119
+ }
120
+ if (typeof projectsConfig === 'string') {
121
+ projectsConfig = [projectsConfig];
122
+ }
123
+ const graph = {
124
+ [project.name]: project,
125
+ };
126
+ const matchingProjects = (0, find_matching_projects_1.findMatchingProjects)(projectsConfig, graph);
127
+ return matchingProjects.includes(project.name);
128
+ }
129
+ async function releaseTasks(tree) {
130
+ return (0, devkit_1.runTasksInSerial)(await (0, generator_1.default)(tree, { skipFormat: true }), () => logNxReleaseDocsInfo());
131
+ }
132
+ function logNxReleaseDocsInfo() {
133
+ devkit_1.output.log({
134
+ title: `📦 To learn how to publish this library, see https://nx.dev/core-features/manage-releases.`,
135
+ });
136
+ }
@@ -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
  }
@@ -6,20 +6,28 @@ const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
6
6
  const minimatch_1 = require("minimatch");
7
7
  const node_fs_1 = require("node:fs");
8
8
  const node_path_1 = require("node:path");
9
+ const posix = require("node:path/posix");
9
10
  const file_hasher_1 = require("nx/src/hasher/file-hasher");
10
11
  // eslint-disable-next-line @typescript-eslint/no-restricted-imports
11
12
  const lock_file_1 = require("nx/src/plugins/js/lock-file/lock-file");
12
13
  const cache_directory_1 = require("nx/src/utils/cache-directory");
13
- const ts_config_1 = require("../../utils/typescript/ts-config");
14
14
  const util_1 = require("./util");
15
15
  const pmc = (0, devkit_1.getPackageManagerCommand)();
16
- function readTargetsCache(cachePath) {
17
- return process.env.NX_CACHE_PROJECT_GRAPH !== 'false' && (0, node_fs_1.existsSync)(cachePath)
18
- ? (0, devkit_1.readJsonFile)(cachePath)
19
- : {};
16
+ let tsConfigCache;
17
+ const tsConfigCachePath = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, 'tsconfig-files.hash');
18
+ let cache;
19
+ function readFromCache(cachePath) {
20
+ try {
21
+ return process.env.NX_CACHE_PROJECT_GRAPH !== 'false'
22
+ ? (0, devkit_1.readJsonFile)(cachePath)
23
+ : {};
24
+ }
25
+ catch {
26
+ return {};
27
+ }
20
28
  }
21
- function writeTargetsToCache(cachePath, results) {
22
- (0, devkit_1.writeJsonFile)(cachePath, results);
29
+ function writeToCache(cachePath, data) {
30
+ (0, devkit_1.writeJsonFile)(cachePath, data, { spaces: 0 });
23
31
  }
24
32
  /**
25
33
  * @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'.
@@ -34,15 +42,32 @@ exports.createNodesV2 = [
34
42
  tsConfigGlob,
35
43
  async (configFilePaths, options, context) => {
36
44
  const optionsHash = (0, file_hasher_1.hashObject)(options);
37
- const cachePath = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, `tsc-${optionsHash}.hash`);
38
- const targetsCache = readTargetsCache(cachePath);
45
+ const targetsCachePath = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, `tsc-${optionsHash}.hash`);
46
+ const targetsCache = readFromCache(targetsCachePath);
47
+ cache = { fileHashes: {}, rawFiles: {}, isExternalProjectReference: {} };
48
+ initializeTsConfigCache(configFilePaths, context.workspaceRoot);
39
49
  const normalizedOptions = normalizePluginOptions(options);
40
- const lockFileName = (0, lock_file_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot));
50
+ const { configFilePaths: validConfigFilePaths, hashes, projectRoots, } = await resolveValidConfigFilesAndHashes(configFilePaths, optionsHash, context);
41
51
  try {
42
- return await (0, devkit_1.createNodesFromFiles)((configFile, options, context) => createNodesInternal(configFile, options, context, lockFileName, targetsCache), configFilePaths, normalizedOptions, context);
52
+ return await (0, devkit_1.createNodesFromFiles)((configFilePath, options, context, idx) => {
53
+ const projectRoot = projectRoots[idx];
54
+ const hash = hashes[idx];
55
+ const cacheKey = `${hash}_${configFilePath}`;
56
+ targetsCache[cacheKey] ??= buildTscTargets((0, node_path_1.join)(context.workspaceRoot, configFilePath), projectRoot, options, context);
57
+ const { targets } = targetsCache[cacheKey];
58
+ return {
59
+ projects: {
60
+ [projectRoot]: {
61
+ projectType: 'library',
62
+ targets,
63
+ },
64
+ },
65
+ };
66
+ }, validConfigFilePaths, normalizedOptions, context);
43
67
  }
44
68
  finally {
45
- writeTargetsToCache(cachePath, targetsCache);
69
+ writeToCache(targetsCachePath, targetsCache);
70
+ writeToCache(tsConfigCachePath, toRelativePaths(tsConfigCache, context.workspaceRoot));
46
71
  }
47
72
  },
48
73
  ];
@@ -50,28 +75,92 @@ exports.createNodes = [
50
75
  tsConfigGlob,
51
76
  async (configFilePath, options, context) => {
52
77
  devkit_1.logger.warn('`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 20, this will change to the createNodesV2 API.');
78
+ const projectRoot = (0, node_path_1.dirname)(configFilePath);
79
+ if (!checkIfConfigFileShouldBeProject(configFilePath, projectRoot, context)) {
80
+ return {};
81
+ }
53
82
  const normalizedOptions = normalizePluginOptions(options);
54
- const lockFileName = (0, lock_file_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot));
55
- return createNodesInternal(configFilePath, normalizedOptions, context, lockFileName, {});
83
+ cache = { fileHashes: {}, rawFiles: {}, isExternalProjectReference: {} };
84
+ initializeTsConfigCache([configFilePath], context.workspaceRoot);
85
+ const { targets } = buildTscTargets((0, node_path_1.join)(context.workspaceRoot, configFilePath), projectRoot, normalizedOptions, context);
86
+ writeToCache(tsConfigCachePath, toRelativePaths(tsConfigCache, context.workspaceRoot));
87
+ return {
88
+ projects: {
89
+ [projectRoot]: {
90
+ projectType: 'library',
91
+ targets,
92
+ },
93
+ },
94
+ };
56
95
  },
57
96
  ];
58
- async function createNodesInternal(configFilePath, options, context, lockFileName, targetsCache) {
59
- const projectRoot = (0, node_path_1.dirname)(configFilePath);
60
- const fullConfigPath = (0, devkit_1.joinPathFragments)(context.workspaceRoot, configFilePath);
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)))) ?? '';
99
+ const validConfigFilePaths = [];
100
+ const hashes = [];
101
+ const projectRoots = [];
102
+ for await (const configFilePath of configFilePaths) {
103
+ const projectRoot = (0, node_path_1.dirname)(configFilePath);
104
+ if (!checkIfConfigFileShouldBeProject(configFilePath, projectRoot, context)) {
105
+ continue;
106
+ }
107
+ projectRoots.push(projectRoot);
108
+ validConfigFilePaths.push(configFilePath);
109
+ hashes.push(await getConfigFileHash(configFilePath, context.workspaceRoot, projectRoot, optionsHash, lockFileHash));
110
+ }
111
+ return { configFilePaths: validConfigFilePaths, hashes, projectRoots };
112
+ }
113
+ /**
114
+ * The cache key is composed by:
115
+ * - hashes of the content of the relevant files that can affect what's inferred by the plugin:
116
+ * - current config file
117
+ * - config files extended by the current config file (recursively up to the root config file)
118
+ * - referenced config files that are internal to the owning Nx project of the current config file,
119
+ * or is a shallow external reference of the owning Nx project
120
+ * - lock file
121
+ * - project's package.json
122
+ * - hash of the plugin options
123
+ * - current config file path
124
+ */
125
+ async function getConfigFileHash(configFilePath, workspaceRoot, projectRoot, optionsHash, lockFileHash) {
126
+ const fullConfigPath = (0, node_path_1.join)(workspaceRoot, configFilePath);
127
+ const tsConfig = retrieveTsConfigFromCache(fullConfigPath, workspaceRoot);
128
+ const extendedConfigFiles = getExtendedConfigFiles(tsConfig, workspaceRoot);
129
+ const internalReferencedFiles = resolveInternalProjectReferences(tsConfig, workspaceRoot, projectRoot);
130
+ const externalProjectReferences = resolveShallowExternalProjectReferences(tsConfig, workspaceRoot, projectRoot);
131
+ let packageJson = null;
132
+ try {
133
+ packageJson = (0, devkit_1.readJsonFile)((0, node_path_1.join)(workspaceRoot, projectRoot, 'package.json'));
134
+ }
135
+ catch { }
136
+ return (0, file_hasher_1.hashArray)([
137
+ ...[
138
+ fullConfigPath,
139
+ ...extendedConfigFiles.files.sort(),
140
+ ...Object.keys(internalReferencedFiles).sort(),
141
+ ...Object.keys(externalProjectReferences).sort(),
142
+ ].map((file) => getFileHash(file, workspaceRoot)),
143
+ ...extendedConfigFiles.packages.sort(),
144
+ lockFileHash,
145
+ optionsHash,
146
+ ...(packageJson ? [(0, file_hasher_1.hashObject)(packageJson)] : []),
147
+ ]);
148
+ }
149
+ function checkIfConfigFileShouldBeProject(configFilePath, projectRoot, context) {
61
150
  // Do not create a project for the workspace root tsconfig files.
62
151
  if (projectRoot === '.') {
63
- return {};
152
+ return false;
64
153
  }
65
154
  // Do not create a project if package.json and project.json isn't there.
66
155
  const siblingFiles = (0, node_fs_1.readdirSync)((0, node_path_1.join)(context.workspaceRoot, projectRoot));
67
156
  if (!siblingFiles.includes('package.json') &&
68
157
  !siblingFiles.includes('project.json')) {
69
- return {};
158
+ return false;
70
159
  }
71
160
  // Do not create a project if it's not a tsconfig.json and there is no tsconfig.json in the same directory
72
161
  if ((0, node_path_1.basename)(configFilePath) !== 'tsconfig.json' &&
73
162
  !siblingFiles.includes('tsconfig.json')) {
74
- return {};
163
+ return false;
75
164
  }
76
165
  // Do not create project for Next.js projects since they are not compatible with
77
166
  // project references and typecheck will fail.
@@ -79,53 +168,14 @@ async function createNodesInternal(configFilePath, options, context, lockFileNam
79
168
  siblingFiles.includes('next.config.cjs') ||
80
169
  siblingFiles.includes('next.config.mjs') ||
81
170
  siblingFiles.includes('next.config.ts')) {
82
- return {};
171
+ return false;
83
172
  }
84
- /**
85
- * The cache key is composed by:
86
- * - hashes of the content of the relevant files that can affect what's inferred by the plugin:
87
- * - current config file
88
- * - config files extended by the current config file (recursively up to the root config file)
89
- * - referenced config files that are internal to the owning Nx project of the current config file, or is a shallow external reference of the owning Nx project
90
- * - lock file
91
- * - hash of the plugin options
92
- * - current config file path
93
- */
94
- const tsConfig = readCachedTsConfig(fullConfigPath);
95
- const extendedConfigFiles = getExtendedConfigFiles(fullConfigPath, tsConfig);
96
- const internalReferencedFiles = resolveInternalProjectReferences(tsConfig, context.workspaceRoot, projectRoot);
97
- const externalProjectReferences = resolveShallowExternalProjectReferences(tsConfig, context.workspaceRoot, projectRoot);
98
- const packageJsonPath = (0, devkit_1.joinPathFragments)(projectRoot, 'package.json');
99
- const packageJson = (0, node_fs_1.existsSync)(packageJsonPath)
100
- ? (0, devkit_1.readJsonFile)(packageJsonPath)
101
- : null;
102
- const nodeHash = (0, file_hasher_1.hashArray)([
103
- ...[
104
- fullConfigPath,
105
- ...extendedConfigFiles.files,
106
- ...Object.keys(internalReferencedFiles),
107
- ...Object.keys(externalProjectReferences),
108
- (0, node_path_1.join)(context.workspaceRoot, lockFileName),
109
- ].map(file_hasher_1.hashFile),
110
- (0, file_hasher_1.hashObject)(options),
111
- ...(packageJson ? [(0, file_hasher_1.hashObject)(packageJson)] : []),
112
- ]);
113
- const cacheKey = `${nodeHash}_${configFilePath}`;
114
- targetsCache[cacheKey] ??= buildTscTargets(fullConfigPath, projectRoot, options, context);
115
- const { targets } = targetsCache[cacheKey];
116
- return {
117
- projects: {
118
- [projectRoot]: {
119
- projectType: 'library',
120
- targets,
121
- },
122
- },
123
- };
173
+ return true;
124
174
  }
125
175
  function buildTscTargets(configFilePath, projectRoot, options, context) {
126
176
  const targets = {};
127
177
  const namedInputs = (0, get_named_inputs_1.getNamedInputs)(projectRoot, context);
128
- const tsConfig = readCachedTsConfig(configFilePath);
178
+ const tsConfig = retrieveTsConfigFromCache(configFilePath, context.workspaceRoot);
129
179
  let internalProjectReferences;
130
180
  // Typecheck target
131
181
  if ((0, node_path_1.basename)(configFilePath) === 'tsconfig.json' && options.typecheck) {
@@ -196,7 +246,7 @@ function buildTscTargets(configFilePath, projectRoot, options, context) {
196
246
  function getInputs(namedInputs, configFilePath, tsConfig, internalProjectReferences, workspaceRoot, projectRoot) {
197
247
  const configFiles = new Set();
198
248
  const externalDependencies = ['typescript'];
199
- const extendedConfigFiles = getExtendedConfigFiles(configFilePath, tsConfig);
249
+ const extendedConfigFiles = getExtendedConfigFiles(tsConfig, workspaceRoot);
200
250
  extendedConfigFiles.files.forEach((configPath) => {
201
251
  configFiles.add(configPath);
202
252
  });
@@ -294,7 +344,9 @@ function getOutputs(configFilePath, tsConfig, internalProjectReferences, workspa
294
344
  outputs.add(pathToInputOrOutput((0, devkit_1.joinPathFragments)(config.options.outDir, relativeRootDir, `*.tsbuildinfo`), workspaceRoot, projectRoot));
295
345
  }
296
346
  }
297
- else if (config.fileNames.length) {
347
+ else if (config.raw?.include?.length ||
348
+ config.raw?.files?.length ||
349
+ (!config.raw?.include && !config.raw?.files)) {
298
350
  // tsc produce files in place when no outDir or outFile is set
299
351
  outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.js'));
300
352
  outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.cjs'));
@@ -407,23 +459,17 @@ function pathToInputOrOutput(path, workspaceRoot, projectRoot) {
407
459
  }
408
460
  return (0, devkit_1.joinPathFragments)('{projectRoot}', pathRelativeToProjectRoot);
409
461
  }
410
- function getExtendedConfigFiles(tsConfigPath, tsConfig) {
462
+ function getExtendedConfigFiles(tsConfig, workspaceRoot) {
411
463
  const extendedConfigFiles = new Set();
412
464
  const extendedExternalPackages = new Set();
413
- let currentConfigPath = tsConfigPath;
414
- let currentConfig = tsConfig;
415
- while (currentConfig.raw?.extends) {
416
- const extendedConfigPath = resolveExtendedTsConfigPath(currentConfig.raw.extends, (0, node_path_1.dirname)(currentConfigPath));
417
- if (!extendedConfigPath) {
418
- break;
419
- }
420
- if (extendedConfigPath.externalPackage) {
421
- extendedExternalPackages.add(extendedConfigPath.externalPackage);
465
+ let currentExtendedConfigFile = tsConfig.extendedConfigFile;
466
+ while (currentExtendedConfigFile) {
467
+ if (currentExtendedConfigFile.externalPackage) {
468
+ extendedExternalPackages.add(currentExtendedConfigFile.externalPackage);
422
469
  break;
423
470
  }
424
- extendedConfigFiles.add(extendedConfigPath.filePath);
425
- currentConfig = readCachedTsConfig(extendedConfigPath.filePath);
426
- currentConfigPath = extendedConfigPath.filePath;
471
+ extendedConfigFiles.add(currentExtendedConfigFile.filePath);
472
+ currentExtendedConfigFile = retrieveTsConfigFromCache(currentExtendedConfigFile.filePath, workspaceRoot).extendedConfigFile;
427
473
  }
428
474
  return {
429
475
  files: Array.from(extendedConfigFiles),
@@ -431,27 +477,31 @@ function getExtendedConfigFiles(tsConfigPath, tsConfig) {
431
477
  };
432
478
  }
433
479
  function resolveInternalProjectReferences(tsConfig, workspaceRoot, projectRoot, projectReferences = {}) {
434
- walkProjectReferences(tsConfig, workspaceRoot, projectRoot, (configPath, config) => {
435
- if (isExternalProjectReference(configPath, workspaceRoot, projectRoot)) {
436
- return false;
480
+ if (!tsConfig.projectReferences?.length) {
481
+ return {};
482
+ }
483
+ for (const ref of tsConfig.projectReferences) {
484
+ let refConfigPath = ref.path;
485
+ if (projectReferences[refConfigPath]) {
486
+ // Already resolved
487
+ continue;
437
488
  }
438
- else {
439
- projectReferences[configPath] = config;
489
+ if (!(0, node_fs_1.existsSync)(refConfigPath)) {
490
+ // the referenced tsconfig doesn't exist, ignore it
491
+ continue;
440
492
  }
441
- });
442
- return projectReferences;
443
- }
444
- function resolveShallowExternalProjectReferences(tsConfig, workspaceRoot, projectRoot, projectReferences = {}) {
445
- walkProjectReferences(tsConfig, workspaceRoot, projectRoot, (configPath, config) => {
446
- if (isExternalProjectReference(configPath, workspaceRoot, projectRoot)) {
447
- projectReferences[configPath] = config;
493
+ if (isExternalProjectReference(refConfigPath, workspaceRoot, projectRoot)) {
494
+ continue;
448
495
  }
449
- return false;
450
- });
496
+ if (!refConfigPath.endsWith('.json')) {
497
+ refConfigPath = (0, node_path_1.join)(refConfigPath, 'tsconfig.json');
498
+ }
499
+ projectReferences[refConfigPath] = retrieveTsConfigFromCache(refConfigPath, workspaceRoot);
500
+ resolveInternalProjectReferences(projectReferences[refConfigPath], workspaceRoot, projectRoot, projectReferences);
501
+ }
451
502
  return projectReferences;
452
503
  }
453
- function walkProjectReferences(tsConfig, workspaceRoot, projectRoot, visitor, // false stops recursion
454
- projectReferences = {}) {
504
+ function resolveShallowExternalProjectReferences(tsConfig, workspaceRoot, projectRoot, projectReferences = {}) {
455
505
  if (!tsConfig.projectReferences?.length) {
456
506
  return projectReferences;
457
507
  }
@@ -465,13 +515,11 @@ projectReferences = {}) {
465
515
  // the referenced tsconfig doesn't exist, ignore it
466
516
  continue;
467
517
  }
468
- if (!refConfigPath.endsWith('.json')) {
469
- refConfigPath = (0, node_path_1.join)(refConfigPath, 'tsconfig.json');
470
- }
471
- const refTsConfig = readCachedTsConfig(refConfigPath);
472
- const result = visitor(refConfigPath, refTsConfig);
473
- if (result !== false) {
474
- walkProjectReferences(refTsConfig, workspaceRoot, projectRoot, visitor);
518
+ if (isExternalProjectReference(refConfigPath, workspaceRoot, projectRoot)) {
519
+ if (!refConfigPath.endsWith('.json')) {
520
+ refConfigPath = (0, node_path_1.join)(refConfigPath, 'tsconfig.json');
521
+ }
522
+ projectReferences[refConfigPath] = retrieveTsConfigFromCache(refConfigPath, workspaceRoot);
475
523
  }
476
524
  }
477
525
  return projectReferences;
@@ -497,7 +545,7 @@ function hasExternalProjectReferences(tsConfigPath, tsConfig, workspaceRoot, pro
497
545
  if (!refConfigPath.endsWith('.json')) {
498
546
  refConfigPath = (0, node_path_1.join)(refConfigPath, 'tsconfig.json');
499
547
  }
500
- const refTsConfig = readCachedTsConfig(refConfigPath);
548
+ const refTsConfig = retrieveTsConfigFromCache(refConfigPath, workspaceRoot);
501
549
  const result = hasExternalProjectReferences(refConfigPath, refTsConfig, workspaceRoot, projectRoot, seen);
502
550
  if (result) {
503
551
  return true;
@@ -506,21 +554,28 @@ function hasExternalProjectReferences(tsConfigPath, tsConfig, workspaceRoot, pro
506
554
  return false;
507
555
  }
508
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
+ }
509
561
  const absoluteProjectRoot = (0, node_path_1.join)(workspaceRoot, projectRoot);
510
562
  let currentPath = getTsConfigDirName(refTsConfigPath);
511
563
  if ((0, node_path_1.relative)(absoluteProjectRoot, currentPath).startsWith('..')) {
512
564
  // it's outside of the project root, so it's an external project reference
565
+ cache.isExternalProjectReference[relativePath] = true;
513
566
  return true;
514
567
  }
515
568
  while (currentPath !== absoluteProjectRoot) {
516
569
  if ((0, node_fs_1.existsSync)((0, node_path_1.join)(currentPath, 'package.json')) ||
517
570
  (0, node_fs_1.existsSync)((0, node_path_1.join)(currentPath, 'project.json'))) {
518
571
  // it's inside a nested project root, so it's and external project reference
572
+ cache.isExternalProjectReference[relativePath] = true;
519
573
  return true;
520
574
  }
521
575
  currentPath = (0, node_path_1.dirname)(currentPath);
522
576
  }
523
577
  // it's inside the project root, so it's an internal project reference
578
+ cache.isExternalProjectReference[relativePath] = false;
524
579
  return false;
525
580
  }
526
581
  function getTsConfigDirName(tsConfigPath) {
@@ -528,19 +583,79 @@ function getTsConfigDirName(tsConfigPath) {
528
583
  ? (0, node_path_1.dirname)(tsConfigPath)
529
584
  : (0, node_path_1.normalize)(tsConfigPath);
530
585
  }
531
- const tsConfigCache = new Map();
532
- function readCachedTsConfig(tsConfigPath) {
533
- const cacheKey = getTsConfigCacheKey(tsConfigPath);
534
- if (tsConfigCache.has(cacheKey)) {
535
- return tsConfigCache.get(cacheKey);
536
- }
537
- const tsConfig = (0, ts_config_1.readTsConfig)(tsConfigPath);
538
- tsConfigCache.set(cacheKey, tsConfig);
539
- return tsConfig;
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] = {
619
+ data: {
620
+ options: tsConfig.options,
621
+ projectReferences: tsConfig.projectReferences,
622
+ raw: tsConfig.raw,
623
+ extendedConfigFile: extendedConfigFile ?? null,
624
+ },
625
+ hash,
626
+ extendedFilesHash,
627
+ };
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('|');
540
644
  }
541
- function getTsConfigCacheKey(tsConfigPath) {
542
- const timestamp = (0, node_fs_1.statSync)(tsConfigPath).mtimeMs;
543
- return `${tsConfigPath}-${timestamp}`;
645
+ let ts;
646
+ function readTsConfig(tsConfigPath, workspaceRoot) {
647
+ if (!ts) {
648
+ ts = require('typescript');
649
+ }
650
+ const tsSys = {
651
+ ...ts.sys,
652
+ readFile: (path) => readFile(path, workspaceRoot),
653
+ readDirectory: () => [],
654
+ };
655
+ const readResult = ts.readConfigFile(tsConfigPath, tsSys.readFile);
656
+ // read with a custom host that won't read directories which is only used
657
+ // to identify the filenames included in the program, which we won't use
658
+ return ts.parseJsonConfigFileContent(readResult.config, tsSys, (0, node_path_1.dirname)(tsConfigPath));
544
659
  }
545
660
  function normalizePluginOptions(pluginOptions = {}) {
546
661
  const defaultTypecheckTargetName = 'typecheck';
@@ -600,3 +715,83 @@ function resolveExtendedTsConfigPath(tsConfigPath, directory) {
600
715
  return null;
601
716
  }
602
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
  }
@@ -6,4 +6,5 @@ export declare function findNpmDependencies(workspaceRoot: string, sourceProject
6
6
  includeTransitiveDependencies?: boolean;
7
7
  ignoredFiles?: string[];
8
8
  useLocalPathsForWorkspaceDependencies?: boolean;
9
+ runtimeHelpers?: string[];
9
10
  }): Record<string, string>;
@@ -22,7 +22,7 @@ function findNpmDependencies(workspaceRoot, sourceProject, projectGraph, project
22
22
  return;
23
23
  seen?.add(currentProject.name);
24
24
  collectDependenciesFromFileMap(workspaceRoot, currentProject, projectGraph, projectFileMap, buildTarget, options.ignoredFiles, options.useLocalPathsForWorkspaceDependencies, collectedDeps);
25
- collectHelperDependencies(workspaceRoot, currentProject, projectGraph, buildTarget, collectedDeps);
25
+ collectHelperDependencies(workspaceRoot, currentProject, projectGraph, buildTarget, options.runtimeHelpers, collectedDeps);
26
26
  if (options.includeTransitiveDependencies) {
27
27
  const projectDeps = projectGraph.dependencies[currentProject.name];
28
28
  for (const dep of projectDeps) {
@@ -114,7 +114,17 @@ function readPackageJson(project, workspaceRoot) {
114
114
  return (0, devkit_1.readJsonFile)(packageJsonPath);
115
115
  return null;
116
116
  }
117
- function collectHelperDependencies(workspaceRoot, sourceProject, projectGraph, buildTarget, npmDeps) {
117
+ function collectHelperDependencies(workspaceRoot, sourceProject, projectGraph, buildTarget, runtimeHelpers, npmDeps) {
118
+ if (runtimeHelpers?.length > 0) {
119
+ for (const helper of runtimeHelpers) {
120
+ if (!npmDeps[helper] &&
121
+ projectGraph.externalNodes[`npm:${helper}`]?.type === 'npm') {
122
+ npmDeps[helper] =
123
+ projectGraph.externalNodes[`npm:${helper}`].data.version;
124
+ }
125
+ }
126
+ return;
127
+ }
118
128
  const target = sourceProject.data.targets[buildTarget];
119
129
  if (!target)
120
130
  return;
@@ -78,10 +78,7 @@ async function execAsync(command, cwd) {
78
78
  return new Promise((resolve, reject) => {
79
79
  (0, child_process_1.exec)(command, { cwd, windowsHide: false }, (error, stdout, stderr) => {
80
80
  if (error) {
81
- return reject(error);
82
- }
83
- if (stderr) {
84
- return reject(stderr);
81
+ return reject((stderr ? `${stderr}\n` : '') + error);
85
82
  }
86
83
  return resolve(stdout.trim());
87
84
  });
@@ -1,4 +1,4 @@
1
1
  import { type Tree } from '@nx/devkit';
2
2
  export declare const defaultExclude: string[];
3
- export declare function addSwcConfig(tree: Tree, projectDir: string, type?: 'commonjs' | 'es6', supportTsx?: boolean): void;
3
+ export declare function addSwcConfig(tree: Tree, projectDir: string, type?: 'commonjs' | 'es6', supportTsx?: boolean, swcName?: string, additionalExcludes?: string[]): void;
4
4
  export declare function addSwcTestConfig(tree: Tree, projectDir: string, type?: 'commonjs' | 'es6', supportTsx?: boolean): void;
@@ -43,11 +43,11 @@ const swcOptionsString = (type = 'commonjs', exclude, supportTsx) => `{
43
43
  "exclude": ${JSON.stringify(exclude)}
44
44
  }
45
45
  `;
46
- function addSwcConfig(tree, projectDir, type = 'commonjs', supportTsx = false) {
47
- const swcrcPath = (0, path_1.join)(projectDir, '.swcrc');
46
+ function addSwcConfig(tree, projectDir, type = 'commonjs', supportTsx = false, swcName = '.swcrc', additionalExcludes = []) {
47
+ const swcrcPath = (0, path_1.join)(projectDir, swcName);
48
48
  if (tree.exists(swcrcPath))
49
49
  return;
50
- tree.write(swcrcPath, swcOptionsString(type, exports.defaultExclude, supportTsx));
50
+ tree.write(swcrcPath, swcOptionsString(type, [...exports.defaultExclude, ...additionalExcludes], supportTsx));
51
51
  }
52
52
  function addSwcTestConfig(tree, projectDir, type = 'commonjs', supportTsx = false) {
53
53
  const swcrcPath = (0, path_1.join)(projectDir, '.spec.swcrc');
@@ -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
  /**