@nx/js 22.6.0-beta.13 → 22.6.0-beta.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/js",
3
- "version": "22.6.0-beta.13",
3
+ "version": "22.6.0-beta.14",
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": "22.6.0-beta.13",
43
- "@nx/workspace": "22.6.0-beta.13",
42
+ "@nx/devkit": "22.6.0-beta.14",
43
+ "@nx/workspace": "22.6.0-beta.14",
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",
@@ -60,7 +60,7 @@
60
60
  "tslib": "^2.3.0"
61
61
  },
62
62
  "devDependencies": {
63
- "nx": "22.6.0-beta.13"
63
+ "nx": "22.6.0-beta.14"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "verdaccio": "^6.0.5"
@@ -1 +1 @@
1
- {"version":3,"file":"release-publish.impl.d.ts","sourceRoot":"","sources":["../../../../../../packages/js/src/executors/release-publish/release-publish.impl.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,eAAe,EAEhB,MAAM,YAAY,CAAC;AAQpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAiBjD,wBAA8B,WAAW,CACvC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,eAAe;;GAsYzB"}
1
+ {"version":3,"file":"release-publish.impl.d.ts","sourceRoot":"","sources":["../../../../../../packages/js/src/executors/release-publish/release-publish.impl.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,eAAe,EAEhB,MAAM,YAAY,CAAC;AAQpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAiBjD,wBAA8B,WAAW,CACvC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,eAAe;;GAkdzB"}
@@ -23,6 +23,25 @@ function processEnv(color) {
23
23
  }
24
24
  async function runExecutor(options, context) {
25
25
  const pm = (0, devkit_1.detectPackageManager)();
26
+ // Check if npm is installed (needed for dist-tag management and as fallback for view command)
27
+ let isNpmInstalled = false;
28
+ try {
29
+ isNpmInstalled =
30
+ (0, child_process_1.execSync)('npm --version', {
31
+ encoding: 'utf-8',
32
+ windowsHide: true,
33
+ stdio: ['ignore', 'pipe', 'ignore'],
34
+ }).trim() !== '';
35
+ }
36
+ catch {
37
+ // Allow missing npm only when using bun
38
+ if (pm !== 'bun') {
39
+ console.error(`npm was not found in the current environment. This is only supported when using \`bun\` as a package manager, but your detected package manager is "${pm}"`);
40
+ return {
41
+ success: false,
42
+ };
43
+ }
44
+ }
26
45
  /**
27
46
  * We need to check both the env var and the option because the executor may have been triggered
28
47
  * indirectly via dependsOn, in which case the env var will be set, but the option will not.
@@ -83,6 +102,20 @@ Please update the local dependency on "${depName}" to be a valid semantic versio
83
102
  success: true,
84
103
  };
85
104
  }
105
+ /**
106
+ * If version data was provided by the nx release version step, check if this project
107
+ * actually had a new version resolved. If not (newVersion is null), there is nothing
108
+ * to publish, so we can skip this project entirely.
109
+ */
110
+ if (options.nxReleaseVersionData) {
111
+ const projectVersionData = options.nxReleaseVersionData[context.projectName];
112
+ if (projectVersionData && projectVersionData.newVersion === null) {
113
+ console.warn(`Skipped ${packageTxt}, because no new version was resolved for this project`);
114
+ return {
115
+ success: true,
116
+ };
117
+ }
118
+ }
86
119
  const warnFn = (message) => {
87
120
  console.log(chalk.keyword('orange')(message));
88
121
  };
@@ -93,9 +126,13 @@ Please update the local dependency on "${depName}" to be a valid semantic versio
93
126
  registry: options.registry,
94
127
  tag: options.tag,
95
128
  }, warnFn);
96
- const npmViewCommandSegments = [
97
- `npm view ${packageName} versions dist-tags --json --"${registryConfigKey}=${registry}"`,
98
- ];
129
+ // Use bun info when bun is the package manager, otherwise use npm view
130
+ // (npm view works across npm/pnpm/yarn environments and is the established default)
131
+ const npmViewCommandSegments = pm === 'bun'
132
+ ? ['bun info', packageName, `--json --"${registryConfigKey}=${registry}"`]
133
+ : [
134
+ `npm view ${packageName} versions dist-tags --json --"${registryConfigKey}=${registry}"`,
135
+ ];
99
136
  const npmDistTagAddCommandSegments = [
100
137
  `npm dist-tag add ${packageName}@${packageJson.version} ${tag} --"${registryConfigKey}=${registry}"`,
101
138
  ];
@@ -124,80 +161,101 @@ Please update the local dependency on "${depName}" to be a valid semantic versio
124
161
  success: true,
125
162
  };
126
163
  }
127
- // If only one version of a package exists in the registry, versions will be a string instead of an array.
128
- const versions = Array.isArray(resultJson.versions)
129
- ? resultJson.versions
130
- : [resultJson.versions];
131
- if (versions.includes(currentVersion)) {
132
- try {
133
- if (!isDryRun) {
134
- (0, child_process_1.execSync)(npmDistTagAddCommandSegments.join(' '), {
135
- env: processEnv(true),
136
- cwd: context.root,
137
- stdio: 'ignore',
138
- windowsHide: false,
139
- });
140
- console.log(`Added the dist-tag ${tag} to v${currentVersion} for registry ${registry}.\n`);
141
- }
142
- else {
143
- console.log(`Would add the dist-tag ${tag} to v${currentVersion} for registry ${registry}, but ${chalk.keyword('orange')('[dry-run]')} was set.\n`);
144
- }
145
- return {
146
- success: true,
147
- };
148
- }
149
- catch (err) {
164
+ if (isNpmInstalled) {
165
+ // If only one version of a package exists in the registry, versions will be a string instead of an array.
166
+ const versions = Array.isArray(resultJson.versions)
167
+ ? resultJson.versions
168
+ : [resultJson.versions];
169
+ if (versions.includes(currentVersion)) {
150
170
  try {
151
- const stdoutData = JSON.parse(err.stdout?.toString() || '{}');
152
- // If the error is that the package doesn't exist, then we can ignore it because we will be publishing it for the first time in the next step
153
- if (!(stdoutData.error?.code?.includes('E404') &&
154
- stdoutData.error?.summary?.includes('no such package available')) &&
155
- !(err.stderr?.toString().includes('E404') &&
156
- err.stderr?.toString().includes('no such package available'))) {
157
- console.error('npm dist-tag add error:');
158
- // npm returns error.summary and error.detail
159
- if (stdoutData.error?.summary) {
160
- console.error(stdoutData.error.summary);
161
- }
162
- if (stdoutData.error?.detail) {
163
- console.error(stdoutData.error.detail);
164
- }
165
- // pnpm returns error.code and error.message
166
- if (stdoutData.error?.code && !stdoutData.error?.summary) {
167
- console.error(`Error code: ${stdoutData.error.code}`);
168
- }
169
- if (stdoutData.error?.message && !stdoutData.error?.summary) {
170
- console.error(stdoutData.error.message);
171
- }
172
- if (context.isVerbose) {
173
- console.error('npm dist-tag add stdout:');
174
- console.error(JSON.stringify(stdoutData, null, 2));
171
+ if (!isDryRun) {
172
+ (0, child_process_1.execSync)(npmDistTagAddCommandSegments.join(' '), {
173
+ env: processEnv(true),
174
+ cwd: context.root,
175
+ stdio: 'ignore',
176
+ windowsHide: false,
177
+ });
178
+ console.log(`Added the dist-tag ${tag} to v${currentVersion} for registry ${registry}.\n`);
179
+ }
180
+ else {
181
+ console.log(`Would add the dist-tag ${tag} to v${currentVersion} for registry ${registry}, but ${chalk.keyword('orange')('[dry-run]')} was set.\n`);
182
+ }
183
+ return {
184
+ success: true,
185
+ };
186
+ }
187
+ catch (err) {
188
+ try {
189
+ const stdoutData = JSON.parse(err.stdout?.toString() || '{}');
190
+ // If the error is that the package doesn't exist, then we can ignore it because we will be publishing it for the first time in the next step
191
+ if (!(stdoutData.error?.code?.includes('E404') &&
192
+ stdoutData.error?.summary?.includes('no such package available')) &&
193
+ !(err.stderr?.toString().includes('E404') &&
194
+ err.stderr?.toString().includes('no such package available'))) {
195
+ console.error('npm dist-tag add error:');
196
+ // npm returns error.summary and error.detail
197
+ if (stdoutData.error?.summary) {
198
+ console.error(stdoutData.error.summary);
199
+ }
200
+ if (stdoutData.error?.detail) {
201
+ console.error(stdoutData.error.detail);
202
+ }
203
+ // pnpm returns error.code and error.message
204
+ if (stdoutData.error?.code && !stdoutData.error?.summary) {
205
+ console.error(`Error code: ${stdoutData.error.code}`);
206
+ }
207
+ if (stdoutData.error?.message && !stdoutData.error?.summary) {
208
+ console.error(stdoutData.error.message);
209
+ }
210
+ if (context.isVerbose) {
211
+ console.error('npm dist-tag add stdout:');
212
+ console.error(JSON.stringify(stdoutData, null, 2));
213
+ }
214
+ return {
215
+ success: false,
216
+ };
175
217
  }
218
+ }
219
+ catch (err) {
220
+ console.error('Something unexpected went wrong when processing the npm dist-tag add output\n', err);
176
221
  return {
177
222
  success: false,
178
223
  };
179
224
  }
180
225
  }
181
- catch (err) {
182
- console.error('Something unexpected went wrong when processing the npm dist-tag add output\n', err);
183
- return {
184
- success: false,
185
- };
186
- }
187
226
  }
188
227
  }
189
228
  }
190
229
  catch (err) {
191
- const stdoutData = JSON.parse(err.stdout?.toString() || '{}');
192
- // If the error is that the package doesn't exist, then we can ignore it because we will be publishing it for the first time in the next step
193
- if (!(stdoutData.error?.code?.includes('E404') &&
194
- stdoutData.error?.summary?.toLowerCase().includes('not found')) &&
195
- !(err.stderr?.toString().includes('E404') &&
196
- err.stderr?.toString().toLowerCase().includes('not found'))) {
197
- console.error(`Something unexpected went wrong when checking for existing dist-tags.\n`, err);
198
- return {
199
- success: false,
200
- };
230
+ try {
231
+ const stdoutData = JSON.parse(err.stdout?.toString() || '{}');
232
+ // If the error is that the package doesn't exist, then we can ignore it because we will be publishing it for the first time in the next step
233
+ if (!(stdoutData.error?.code?.includes('E404') &&
234
+ stdoutData.error?.summary?.toLowerCase().includes('not found')) &&
235
+ !(err.stderr?.toString().includes('E404') &&
236
+ err.stderr?.toString().toLowerCase().includes('not found')) &&
237
+ // bun uses plain '404' instead of 'E404'
238
+ !(err.stderr?.toString().includes('404') &&
239
+ err.stderr?.toString().toLowerCase().includes('not found'))) {
240
+ console.error(`Something unexpected went wrong when checking for existing dist-tags.\n`, err);
241
+ return {
242
+ success: false,
243
+ };
244
+ }
245
+ }
246
+ catch {
247
+ // JSON parse failed entirely — check stderr/stdout for plain 404
248
+ const stderrStr = err.stderr?.toString() || '';
249
+ const stdoutStr = err.stdout?.toString() || '';
250
+ if (!((stderrStr.includes('404') &&
251
+ stderrStr.toLowerCase().includes('not found')) ||
252
+ (stdoutStr.includes('404') &&
253
+ stdoutStr.toLowerCase().includes('not found')))) {
254
+ console.error(`Something unexpected went wrong when checking for existing dist-tags.\n`, err);
255
+ return {
256
+ success: false,
257
+ };
258
+ }
201
259
  }
202
260
  }
203
261
  }
@@ -6,4 +6,8 @@ export interface PublishExecutorSchema {
6
6
  dryRun?: boolean;
7
7
  access?: 'public' | 'restricted';
8
8
  firstRelease?: boolean;
9
+ nxReleaseVersionData?: Record<
10
+ string,
11
+ { currentVersion: string; newVersion: string | null; [key: string]: any }
12
+ >;
9
13
  }
@@ -465,13 +465,8 @@ function getInputs(namedInputs, config, tsConfig, internalProjectReferences, wor
465
465
  dependentTasksOutputFiles: '**/*.{d.ts,tsbuildinfo}',
466
466
  transitive: true,
467
467
  });
468
- const externalRefConfigFiles = getExternalProjectReferenceConfigFiles(tsConfig, internalProjectReferences, workspaceRoot, config.project, cache, configFiles);
469
- if (externalRefConfigFiles.length > 0) {
470
- // tsc --build also reads the tsconfig files from external project
471
- // references (and their extended configs) when processing project
472
- // references, so we need to add them as inputs
473
- inputs.push(...externalRefConfigFiles.map((p) => pathToInputOrOutput(p, workspaceRoot, config.project)));
474
- }
468
+ const externalRefPatterns = getExternalProjectReferenceTsconfigPatterns(tsConfig, internalProjectReferences, workspaceRoot, config.project, cache);
469
+ inputs.push(...externalRefPatterns);
475
470
  // inputs.push({ externalDependencies });
476
471
  return inputs;
477
472
  }
@@ -624,11 +619,12 @@ function resolveShallowExternalProjectReferences(tsConfig, workspaceRoot, projec
624
619
  return projectReferences;
625
620
  }
626
621
  /**
627
- * Collects config file paths from external project references, including their
628
- * extended config files. It checks the root tsconfig and all internal project
629
- * references for their direct external references.
622
+ * Collects unique tsconfig paths (relative to their project root) from the
623
+ * project reference chain and returns them as `^{projectRoot}/...` input
624
+ * patterns. We only need to discover the full set of distinct relative paths
625
+ * from the reference chain.
630
626
  */
631
- function getExternalProjectReferenceConfigFiles(tsConfig, internalProjectReferences, workspaceRoot, project, cache, existingConfigFiles) {
627
+ function getExternalProjectReferenceTsconfigPatterns(tsConfig, internalProjectReferences, workspaceRoot, project, cache) {
632
628
  const externalRefs = {};
633
629
  // Collect direct external references from the root tsconfig
634
630
  resolveShallowExternalProjectReferences(tsConfig, workspaceRoot, project, cache, externalRefs);
@@ -636,22 +632,54 @@ function getExternalProjectReferenceConfigFiles(tsConfig, internalProjectReferen
636
632
  for (const refTsConfig of Object.values(internalProjectReferences)) {
637
633
  resolveShallowExternalProjectReferences(refTsConfig, workspaceRoot, project, cache, externalRefs);
638
634
  }
639
- const result = [];
640
- const seen = new Set();
641
- for (const [refConfigPath, refTsConfig] of Object.entries(externalRefs)) {
642
- if (!existingConfigFiles.has(refConfigPath) && !seen.has(refConfigPath)) {
643
- result.push(refConfigPath);
644
- seen.add(refConfigPath);
645
- }
646
- const extendedFiles = getExtendedConfigFiles(refTsConfig, workspaceRoot, cache);
647
- for (const extFile of extendedFiles.files) {
648
- if (!existingConfigFiles.has(extFile) && !seen.has(extFile)) {
649
- result.push(extFile);
650
- seen.add(extFile);
635
+ // Collect unique tsconfig paths (relative to project root) from the
636
+ // reference chain, seeded with the external refs.
637
+ const uniqueRelPaths = new Set();
638
+ const visited = new Set();
639
+ const worklist = [];
640
+ for (const refConfigPath of Object.keys(externalRefs)) {
641
+ const refContext = getConfigContext(refConfigPath, workspaceRoot, cache);
642
+ uniqueRelPaths.add(posixRelative(refContext.project.absolute, refConfigPath));
643
+ worklist.push({
644
+ configPath: refConfigPath,
645
+ ownerProject: refContext.project,
646
+ });
647
+ }
648
+ for (let i = 0; i < worklist.length; i++) {
649
+ const { configPath, ownerProject } = worklist[i];
650
+ if (visited.has(configPath)) {
651
+ continue;
652
+ }
653
+ visited.add(configPath);
654
+ const wsRelPath = posixRelative(workspaceRoot, configPath);
655
+ const tsConfigData = tsConfigCacheData[wsRelPath]?.data;
656
+ if (!tsConfigData?.projectReferences?.length) {
657
+ continue;
658
+ }
659
+ for (const ref of tsConfigData.projectReferences) {
660
+ let refPath = ref.path;
661
+ if (!refPath.endsWith('.json')) {
662
+ refPath = (0, node_path_1.join)(refPath, 'tsconfig.json');
663
+ }
664
+ // Skip references not found in the tsconfig cache
665
+ const refWsRelPath = posixRelative(workspaceRoot, refPath);
666
+ if (!tsConfigCacheData[refWsRelPath]) {
667
+ continue;
668
+ }
669
+ const refContext = getConfigContext(refPath, workspaceRoot, cache);
670
+ // Collect paths for references within the same project
671
+ if (!isExternalProjectReference(refContext, ownerProject, workspaceRoot, cache)) {
672
+ uniqueRelPaths.add(posixRelative(ownerProject.absolute, refPath));
673
+ }
674
+ if (!visited.has(refPath)) {
675
+ worklist.push({
676
+ configPath: refPath,
677
+ ownerProject: refContext.project,
678
+ });
651
679
  }
652
680
  }
653
681
  }
654
- return result;
682
+ return Array.from(uniqueRelPaths).map((relPath) => `^{projectRoot}/${relPath}`);
655
683
  }
656
684
  function isExternalProjectReference(refConfig, project, workspaceRoot, cache) {
657
685
  const owner = cache.configOwners.get(refConfig.relativePath);