@nx/js 19.6.0 → 19.7.0-beta.0

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/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  # Nx: Smart Monorepos · Fast CI
24
24
 
25
- Nx is a build system with built-in tooling and advanced CI capabilities. It helps you maintain and scale monorepos, both locally and on CI.
25
+ Nx is a build system, optimized for monorepos, with plugins for popular frameworks and tools and advanced CI capabilities including caching and distribution.
26
26
 
27
27
  This package is a [JavaScript/TypeScript plugin for Nx](https://nx.dev/js/overview).
28
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/js",
3
- "version": "19.6.0",
3
+ "version": "19.7.0-beta.0",
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": {
@@ -34,11 +34,13 @@
34
34
  "dependencies": {
35
35
  "@babel/core": "^7.23.2",
36
36
  "@babel/plugin-proposal-decorators": "^7.22.7",
37
- "@babel/plugin-transform-runtime": "^7.23.2",
38
37
  "@babel/plugin-transform-class-properties": "^7.22.5",
38
+ "@babel/plugin-transform-runtime": "^7.23.2",
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": "19.7.0-beta.0",
43
+ "@nx/workspace": "19.7.0-beta.0",
42
44
  "babel-plugin-const-enum": "^1.0.1",
43
45
  "babel-plugin-macros": "^2.8.0",
44
46
  "babel-plugin-transform-typescript-metadata": "^0.3.1",
@@ -47,20 +49,19 @@
47
49
  "detect-port": "^1.5.1",
48
50
  "fast-glob": "3.2.7",
49
51
  "fs-extra": "^11.1.0",
50
- "npm-package-arg": "11.0.1",
51
- "npm-run-path": "^4.0.1",
52
- "ts-node": "10.9.1",
53
- "tsconfig-paths": "^4.1.2",
54
52
  "ignore": "^5.0.4",
55
53
  "js-tokens": "^4.0.0",
54
+ "jsonc-parser": "3.2.0",
56
55
  "minimatch": "9.0.3",
56
+ "npm-package-arg": "11.0.1",
57
+ "npm-run-path": "^4.0.1",
57
58
  "ora": "5.3.0",
58
59
  "semver": "^7.5.3",
59
60
  "source-map-support": "0.5.19",
61
+ "ts-node": "10.9.1",
62
+ "tsconfig-paths": "^4.1.2",
60
63
  "tslib": "^2.3.0",
61
- "@nx/devkit": "19.6.0",
62
- "@nx/workspace": "19.6.0",
63
- "@nrwl/js": "19.6.0"
64
+ "@nrwl/js": "19.7.0-beta.0"
64
65
  },
65
66
  "peerDependencies": {
66
67
  "verdaccio": "^5.0.4"
@@ -1,7 +1,4 @@
1
- declare const Module: any;
2
1
  declare const url: any;
3
- declare const originalLoader: any;
2
+ declare const patchSigint: any;
3
+ declare const patchRequire: any;
4
4
  declare const dynamicImport: Function;
5
- declare const mappings: any;
6
- declare const keys: string[];
7
- declare const fileToRun: any;
@@ -1,21 +1,7 @@
1
- const Module = require('module');
2
1
  const url = require('node:url');
3
- const originalLoader = Module._load;
2
+ const { patchSigint } = require('./patch-sigint');
3
+ const { patchRequire } = require('./patch-require');
4
+ patchSigint();
5
+ patchRequire();
4
6
  const dynamicImport = new Function('specifier', 'return import(specifier)');
5
- const mappings = JSON.parse(process.env.NX_MAPPINGS);
6
- const keys = Object.keys(mappings);
7
- const fileToRun = url.pathToFileURL(process.env.NX_FILE_TO_RUN);
8
- Module._load = function (request, parent) {
9
- if (!parent)
10
- return originalLoader.apply(this, arguments);
11
- const match = keys.find((k) => request === k);
12
- if (match) {
13
- const newArguments = [...arguments];
14
- newArguments[0] = mappings[match];
15
- return originalLoader.apply(this, newArguments);
16
- }
17
- else {
18
- return originalLoader.apply(this, arguments);
19
- }
20
- };
21
- dynamicImport(fileToRun);
7
+ dynamicImport(url.pathToFileURL(process.env.NX_FILE_TO_RUN));
@@ -111,11 +111,14 @@ async function* nodeExecutor(options, context) {
111
111
  task.childProcess.stderr.on('data', handleStdErr);
112
112
  task.childProcess.once('exit', (code) => {
113
113
  task.childProcess.off('data', handleStdErr);
114
- if (options.watch && !task.killed) {
114
+ if (options.watch &&
115
+ !task.killed &&
116
+ // SIGINT should exist the process rather than watch for changes.
117
+ code !== 130) {
115
118
  devkit_1.logger.info(`NX Process exited with code ${code}, waiting for changes to restart...`);
116
119
  }
117
- if (!options.watch) {
118
- if (code !== 0) {
120
+ if (!options.watch || code === 130) {
121
+ if (code !== 0 && code !== 130) {
119
122
  error(new Error(`Process exited with code ${code}`));
120
123
  }
121
124
  else {
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Overrides require calls to map buildable workspace libs to their output location.
3
+ * This is useful for running programs compiled via TSC/SWC that aren't bundled.
4
+ */
5
+ export declare function patchRequire(): void;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.patchRequire = patchRequire;
4
+ const Module = require('node:module');
5
+ const originalLoader = Module._load;
6
+ /**
7
+ * Overrides require calls to map buildable workspace libs to their output location.
8
+ * This is useful for running programs compiled via TSC/SWC that aren't bundled.
9
+ */
10
+ function patchRequire() {
11
+ const mappings = JSON.parse(process.env.NX_MAPPINGS);
12
+ const keys = Object.keys(mappings);
13
+ Module._load = function (request, parent) {
14
+ if (!parent)
15
+ return originalLoader.apply(this, arguments);
16
+ const match = keys.find((k) => request === k);
17
+ if (match) {
18
+ const newArguments = [...arguments];
19
+ newArguments[0] = mappings[match];
20
+ return originalLoader.apply(this, newArguments);
21
+ }
22
+ else {
23
+ return originalLoader.apply(this, arguments);
24
+ }
25
+ };
26
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Patches the current process so that Ctrl+C is properly handled.
3
+ * Without this patch, SIGINT or Ctrl+C does not wait for graceful shutdown and exits immediately.
4
+ */
5
+ export declare function patchSigint(): void;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.patchSigint = patchSigint;
4
+ const readline = require('node:readline');
5
+ /**
6
+ * Patches the current process so that Ctrl+C is properly handled.
7
+ * Without this patch, SIGINT or Ctrl+C does not wait for graceful shutdown and exits immediately.
8
+ */
9
+ function patchSigint() {
10
+ readline.emitKeypressEvents(process.stdin);
11
+ if (process.stdin.isTTY)
12
+ process.stdin.setRawMode(true);
13
+ process.stdin.on('keypress', async (chunk, key) => {
14
+ if (key && key.ctrl && key.name === 'c') {
15
+ process.stdin.setRawMode(false); // To ensure nx terminal is not stuck in raw mode
16
+ const listeners = process.listeners('SIGINT');
17
+ for (const listener of listeners) {
18
+ await listener('SIGINT');
19
+ }
20
+ process.exit(130);
21
+ }
22
+ });
23
+ console.log('To exit the process, press Ctrl+C');
24
+ }
@@ -29,11 +29,11 @@ function determineModuleFormatFromTsConfig(absolutePathToTsConfig) {
29
29
  }
30
30
  function createTypeScriptCompilationOptions(normalizedOptions, context) {
31
31
  return {
32
- outputPath: normalizedOptions.outputPath,
32
+ outputPath: (0, devkit_1.joinPathFragments)(normalizedOptions.outputPath),
33
33
  projectName: context.projectName,
34
34
  projectRoot: normalizedOptions.projectRoot,
35
- rootDir: normalizedOptions.rootDir,
36
- tsConfig: normalizedOptions.tsConfig,
35
+ rootDir: (0, devkit_1.joinPathFragments)(normalizedOptions.rootDir),
36
+ tsConfig: (0, devkit_1.joinPathFragments)(normalizedOptions.tsConfig),
37
37
  watch: normalizedOptions.watch,
38
38
  deleteOutputPath: normalizedOptions.clean,
39
39
  getCustomTransformers: (0, lib_1.getCustomTrasformersFactory)(normalizedOptions.transformers),
@@ -159,7 +159,7 @@ async function addProject(tree, options) {
159
159
  }
160
160
  }
161
161
  if (options.publishable) {
162
- const packageRoot = (0, path_1.join)(defaultOutputDirectory, '{projectRoot}');
162
+ const packageRoot = (0, devkit_1.joinPathFragments)(defaultOutputDirectory, '{projectRoot}');
163
163
  projectConfiguration.targets ??= {};
164
164
  projectConfiguration.targets['nx-release-publish'] = {
165
165
  options: {
@@ -76,7 +76,7 @@ Valid values are: ${version_1.validReleaseVersionPrefixes
76
76
  if (!packageRoot) {
77
77
  throw new Error(`The project "${projectName}" does not have a packageRoot available. Please report this issue on https://github.com/nrwl/nx`);
78
78
  }
79
- const packageJsonPath = (0, node_path_1.join)(packageRoot, 'package.json');
79
+ const packageJsonPath = (0, devkit_1.joinPathFragments)(packageRoot, 'package.json');
80
80
  if (!tree.exists(packageJsonPath)) {
81
81
  throw new Error(`The project "${projectName}" does not have a package.json available at ${packageJsonPath}.
82
82
 
@@ -544,7 +544,7 @@ To fix this you will either need to add a package.json file at that location, or
544
544
  if (!dependencyPackageRoot) {
545
545
  throw new Error(`The project "${dependencyProjectName}" does not have a packageRoot available. Please report this issue on https://github.com/nrwl/nx`);
546
546
  }
547
- const dependencyPackageJsonPath = (0, node_path_1.join)(dependencyPackageRoot, 'package.json');
547
+ const dependencyPackageJsonPath = (0, devkit_1.joinPathFragments)(dependencyPackageRoot, 'package.json');
548
548
  const dependencyPackageJson = (0, devkit_1.readJson)(tree, dependencyPackageJsonPath);
549
549
  updateDependentProjectAndAddToVersionData({
550
550
  dependentProject: transitiveDependentProject,
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.syncGenerator = syncGenerator;
4
4
  const devkit_1 = require("@nx/devkit");
5
+ const jsonc_parser_1 = require("jsonc-parser");
5
6
  const posix_1 = require("node:path/posix");
7
+ const ts = require("typescript");
6
8
  const plugin_1 = require("../../plugins/typescript/plugin");
7
9
  const COMMON_RUNTIME_TS_CONFIG_FILE_NAMES = [
8
10
  'tsconfig.app.json',
@@ -27,16 +29,25 @@ async function syncGenerator(tree) {
27
29
  // Root tsconfig containing project references for the whole workspace
28
30
  const rootTsconfigPath = 'tsconfig.json';
29
31
  if (!tree.exists(rootTsconfigPath)) {
30
- throw new Error(`A "tsconfig.json" file must exist in the workspace root.`);
32
+ throw new Error(`A "tsconfig.json" file must exist in the workspace root in order to use this sync generator.`);
31
33
  }
32
- const rootTsconfig = (0, devkit_1.readJson)(tree, rootTsconfigPath);
34
+ const rawTsconfigContentsCache = new Map();
35
+ const stringifiedRootJsonContents = readRawTsconfigContents(tree, rawTsconfigContentsCache, rootTsconfigPath);
36
+ const rootTsconfig = (0, devkit_1.parseJson)(stringifiedRootJsonContents);
33
37
  const projectGraph = await (0, devkit_1.createProjectGraphAsync)();
34
38
  const projectRoots = new Set();
39
+ const tsconfigHasCompositeEnabledCache = new Map();
35
40
  const tsconfigProjectNodeValues = Object.values(projectGraph.nodes).filter((node) => {
36
41
  projectRoots.add(node.data.root);
37
42
  const projectTsconfigPath = (0, devkit_1.joinPathFragments)(node.data.root, 'tsconfig.json');
38
- return tree.exists(projectTsconfigPath);
43
+ return tsconfigExists(tree, rawTsconfigContentsCache, projectTsconfigPath);
39
44
  });
45
+ const tsSysFromTree = {
46
+ ...ts.sys,
47
+ readFile(path) {
48
+ return readRawTsconfigContents(tree, rawTsconfigContentsCache, path);
49
+ },
50
+ };
40
51
  // Track if any changes were made to the tsconfig files. We check the changes
41
52
  // made by this generator to know if the TS config is out of sync with the
42
53
  // project graph. Therefore, we don't format the files if there were no changes
@@ -47,7 +58,7 @@ async function syncGenerator(tree) {
47
58
  for (const ref of rootTsconfig.references ?? []) {
48
59
  // reference path is relative to the tsconfig file
49
60
  const resolvedRefPath = getTsConfigPathFromReferencePath(tree, rootTsconfigPath, ref.path);
50
- if (tree.exists(resolvedRefPath)) {
61
+ if (tsconfigExists(tree, rawTsconfigContentsCache, resolvedRefPath)) {
51
62
  // we only keep the references that still exist
52
63
  referencesSet.add(normalizeReferencePath(ref.path));
53
64
  }
@@ -64,10 +75,13 @@ async function syncGenerator(tree) {
64
75
  }
65
76
  }
66
77
  if (hasChanges) {
67
- rootTsconfig.references = Array.from(referencesSet).map((ref) => ({
78
+ const updatedReferences = Array.from(referencesSet)
79
+ // Check composite is true in the internal reference before proceeding
80
+ .filter((ref) => hasCompositeEnabled(tsSysFromTree, tsconfigHasCompositeEnabledCache, (0, devkit_1.joinPathFragments)(ref, 'tsconfig.json')))
81
+ .map((ref) => ({
68
82
  path: `./${ref}`,
69
83
  }));
70
- (0, devkit_1.writeJson)(tree, rootTsconfigPath, rootTsconfig);
84
+ patchTsconfigJsonReferences(tree, rawTsconfigContentsCache, rootTsconfigPath, updatedReferences);
71
85
  }
72
86
  }
73
87
  const runtimeTsConfigFileNames = nxJson.sync?.generatorOptions?.['@nx/js:typescript-sync']
@@ -84,7 +98,7 @@ async function syncGenerator(tree) {
84
98
  const sourceProjectNode = projectGraph.nodes[name];
85
99
  // Find the relevant tsconfig file for the source project
86
100
  const sourceProjectTsconfigPath = (0, devkit_1.joinPathFragments)(sourceProjectNode.data.root, 'tsconfig.json');
87
- if (!tree.exists(sourceProjectTsconfigPath)) {
101
+ if (!tsconfigExists(tree, rawTsconfigContentsCache, sourceProjectTsconfigPath)) {
88
102
  if (process.env.NX_VERBOSE_LOGGING === 'true') {
89
103
  devkit_1.logger.warn(`Skipping project "${name}" as there is no tsconfig.json file found in the project root "${sourceProjectNode.data.root}".`);
90
104
  }
@@ -97,16 +111,16 @@ async function syncGenerator(tree) {
97
111
  }
98
112
  for (const runtimeTsConfigFileName of runtimeTsConfigFileNames) {
99
113
  const runtimeTsConfigPath = (0, devkit_1.joinPathFragments)(sourceProjectNode.data.root, runtimeTsConfigFileName);
100
- if (!tree.exists(runtimeTsConfigPath)) {
114
+ if (!tsconfigExists(tree, rawTsconfigContentsCache, runtimeTsConfigPath)) {
101
115
  continue;
102
116
  }
103
117
  // Update project references for the runtime tsconfig
104
118
  hasChanges =
105
- updateTsConfigReferences(tree, runtimeTsConfigPath, dependencies, sourceProjectNode.data.root, projectRoots, runtimeTsConfigFileName, runtimeTsConfigFileNames) || hasChanges;
119
+ updateTsConfigReferences(tree, tsSysFromTree, rawTsconfigContentsCache, tsconfigHasCompositeEnabledCache, runtimeTsConfigPath, dependencies, sourceProjectNode.data.root, projectRoots, runtimeTsConfigFileName, runtimeTsConfigFileNames) || hasChanges;
106
120
  }
107
121
  // Update project references for the tsconfig.json file
108
122
  hasChanges =
109
- updateTsConfigReferences(tree, sourceProjectTsconfigPath, dependencies, sourceProjectNode.data.root, projectRoots) || hasChanges;
123
+ updateTsConfigReferences(tree, tsSysFromTree, rawTsconfigContentsCache, tsconfigHasCompositeEnabledCache, sourceProjectTsconfigPath, dependencies, sourceProjectNode.data.root, projectRoots) || hasChanges;
110
124
  }
111
125
  if (hasChanges) {
112
126
  await (0, devkit_1.formatFiles)(tree);
@@ -116,8 +130,28 @@ async function syncGenerator(tree) {
116
130
  }
117
131
  }
118
132
  exports.default = syncGenerator;
119
- function updateTsConfigReferences(tree, tsConfigPath, dependencies, projectRoot, projectRoots, runtimeTsConfigFileName, possibleRuntimeTsConfigFileNames) {
120
- const tsConfig = (0, devkit_1.readJson)(tree, tsConfigPath);
133
+ /**
134
+ * Within the context of a sync generator, performance is a key concern,
135
+ * so avoid FS interactions whenever possible.
136
+ */
137
+ function readRawTsconfigContents(tree, rawTsconfigContentsCache, tsconfigPath) {
138
+ if (!rawTsconfigContentsCache.has(tsconfigPath)) {
139
+ rawTsconfigContentsCache.set(tsconfigPath, tree.read(tsconfigPath, 'utf-8'));
140
+ }
141
+ return rawTsconfigContentsCache.get(tsconfigPath);
142
+ }
143
+ /**
144
+ * Within the context of a sync generator, performance is a key concern,
145
+ * so avoid FS interactions whenever possible.
146
+ */
147
+ function tsconfigExists(tree, rawTsconfigContentsCache, tsconfigPath) {
148
+ return rawTsconfigContentsCache.has(tsconfigPath)
149
+ ? true
150
+ : tree.exists(tsconfigPath);
151
+ }
152
+ function updateTsConfigReferences(tree, tsSysFromTree, rawTsconfigContentsCache, tsconfigHasCompositeEnabledCache, tsConfigPath, dependencies, projectRoot, projectRoots, runtimeTsConfigFileName, possibleRuntimeTsConfigFileNames) {
153
+ const stringifiedJsonContents = readRawTsconfigContents(tree, rawTsconfigContentsCache, tsConfigPath);
154
+ const tsConfig = (0, devkit_1.parseJson)(stringifiedJsonContents);
121
155
  // We have at least one dependency so we can safely set it to an empty array if not already set
122
156
  const references = [];
123
157
  const originalReferencesSet = new Set();
@@ -127,8 +161,8 @@ function updateTsConfigReferences(tree, tsConfigPath, dependencies, projectRoot,
127
161
  originalReferencesSet.add(normalizedPath);
128
162
  // reference path is relative to the tsconfig file
129
163
  const resolvedRefPath = getTsConfigPathFromReferencePath(tree, tsConfigPath, ref.path);
130
- if (isInternalProjectReference(tree, resolvedRefPath, projectRoot, projectRoots)) {
131
- // we keep all internal references
164
+ if (isProjectReferenceWithinNxProject(tree, rawTsconfigContentsCache, resolvedRefPath, projectRoot, projectRoots)) {
165
+ // we keep all references within the current Nx project
132
166
  references.push(ref);
133
167
  newReferencesSet.add(normalizedPath);
134
168
  }
@@ -139,7 +173,11 @@ function updateTsConfigReferences(tree, tsConfigPath, dependencies, projectRoot,
139
173
  let referencePath = dep.data.root;
140
174
  if (runtimeTsConfigFileName) {
141
175
  const runtimeTsConfigPath = (0, devkit_1.joinPathFragments)(dep.data.root, runtimeTsConfigFileName);
142
- if (tree.exists(runtimeTsConfigPath)) {
176
+ if (tsconfigExists(tree, rawTsconfigContentsCache, runtimeTsConfigPath)) {
177
+ // Check composite is true in the dependency runtime tsconfig file before proceeding
178
+ if (!hasCompositeEnabled(tsSysFromTree, tsconfigHasCompositeEnabledCache, runtimeTsConfigPath)) {
179
+ continue;
180
+ }
143
181
  referencePath = runtimeTsConfigPath;
144
182
  }
145
183
  else {
@@ -148,13 +186,23 @@ function updateTsConfigReferences(tree, tsConfigPath, dependencies, projectRoot,
148
186
  for (const possibleRuntimeTsConfigFileName of possibleRuntimeTsConfigFileNames ??
149
187
  []) {
150
188
  const possibleRuntimeTsConfigPath = (0, devkit_1.joinPathFragments)(dep.data.root, possibleRuntimeTsConfigFileName);
151
- if (tree.exists(possibleRuntimeTsConfigPath)) {
189
+ if (tsconfigExists(tree, rawTsconfigContentsCache, possibleRuntimeTsConfigPath)) {
190
+ // Check composite is true in the dependency runtime tsconfig file before proceeding
191
+ if (!hasCompositeEnabled(tsSysFromTree, tsconfigHasCompositeEnabledCache, possibleRuntimeTsConfigPath)) {
192
+ continue;
193
+ }
152
194
  referencePath = possibleRuntimeTsConfigPath;
153
195
  break;
154
196
  }
155
197
  }
156
198
  }
157
199
  }
200
+ else {
201
+ // Check composite is true in the dependency tsconfig.json file before proceeding
202
+ if (!hasCompositeEnabled(tsSysFromTree, tsconfigHasCompositeEnabledCache, (0, devkit_1.joinPathFragments)(dep.data.root, 'tsconfig.json'))) {
203
+ continue;
204
+ }
205
+ }
158
206
  const relativePathToTargetRoot = (0, posix_1.relative)(projectRoot, referencePath);
159
207
  if (!newReferencesSet.has(relativePathToTargetRoot)) {
160
208
  newReferencesSet.add(relativePathToTargetRoot);
@@ -167,8 +215,7 @@ function updateTsConfigReferences(tree, tsConfigPath, dependencies, projectRoot,
167
215
  }
168
216
  hasChanges ||= newReferencesSet.size !== originalReferencesSet.size;
169
217
  if (hasChanges) {
170
- tsConfig.references = references;
171
- (0, devkit_1.writeJson)(tree, tsConfigPath, tsConfig);
218
+ patchTsconfigJsonReferences(tree, rawTsconfigContentsCache, tsConfigPath, references);
172
219
  }
173
220
  return hasChanges;
174
221
  }
@@ -214,8 +261,8 @@ function normalizeReferencePath(path) {
214
261
  .replace(/\/tsconfig.json$/, '')
215
262
  .replace(/^\.\//, '');
216
263
  }
217
- function isInternalProjectReference(tree, refTsConfigPath, projectRoot, projectRoots) {
218
- let currentPath = getTsConfigDirName(tree, refTsConfigPath);
264
+ function isProjectReferenceWithinNxProject(tree, rawTsconfigContentsCache, refTsConfigPath, projectRoot, projectRoots) {
265
+ let currentPath = getTsConfigDirName(tree, rawTsconfigContentsCache, refTsConfigPath);
219
266
  if ((0, posix_1.relative)(projectRoot, currentPath).startsWith('..')) {
220
267
  // it's outside of the project root, so it's an external project reference
221
268
  return false;
@@ -230,8 +277,10 @@ function isInternalProjectReference(tree, refTsConfigPath, projectRoot, projectR
230
277
  // it's inside the project root, so it's an internal project reference
231
278
  return true;
232
279
  }
233
- function getTsConfigDirName(tree, tsConfigPath) {
234
- return tree.isFile(tsConfigPath)
280
+ function getTsConfigDirName(tree, rawTsconfigContentsCache, tsConfigPath) {
281
+ return (rawTsconfigContentsCache.has(tsConfigPath)
282
+ ? true
283
+ : tree.isFile(tsConfigPath))
235
284
  ? (0, posix_1.dirname)(tsConfigPath)
236
285
  : (0, posix_1.normalize)(tsConfigPath);
237
286
  }
@@ -241,3 +290,22 @@ function getTsConfigPathFromReferencePath(tree, ownerTsConfigPath, referencePath
241
290
  ? resolvedRefPath
242
291
  : (0, devkit_1.joinPathFragments)(resolvedRefPath, 'tsconfig.json');
243
292
  }
293
+ /**
294
+ * Minimally patch just the "references" property within the tsconfig file at a given path.
295
+ * This allows comments in other sections of the file to remain intact when syncing is run.
296
+ */
297
+ function patchTsconfigJsonReferences(tree, rawTsconfigContentsCache, tsconfigPath, updatedReferences) {
298
+ const stringifiedJsonContents = readRawTsconfigContents(tree, rawTsconfigContentsCache, tsconfigPath);
299
+ const edits = (0, jsonc_parser_1.modify)(stringifiedJsonContents, ['references'], updatedReferences, {});
300
+ const updatedJsonContents = (0, jsonc_parser_1.applyEdits)(stringifiedJsonContents, edits);
301
+ // The final contents will be formatted by formatFiles() later
302
+ tree.write(tsconfigPath, updatedJsonContents);
303
+ }
304
+ function hasCompositeEnabled(tsSysFromTree, tsconfigHasCompositeEnabledCache, tsconfigPath) {
305
+ if (!tsconfigHasCompositeEnabledCache.has(tsconfigPath)) {
306
+ const parsed = ts.parseJsonConfigFileContent(ts.readConfigFile(tsconfigPath, tsSysFromTree.readFile).config, tsSysFromTree, (0, posix_1.dirname)(tsconfigPath));
307
+ const enabledVal = parsed.options.composite === true;
308
+ tsconfigHasCompositeEnabledCache.set(tsconfigPath, enabledVal);
309
+ }
310
+ return tsconfigHasCompositeEnabledCache.get(tsconfigPath);
311
+ }
@@ -283,6 +283,9 @@ function updatePaths(dependencies, paths) {
283
283
  const pathsKeys = Object.keys(paths);
284
284
  // For each registered dependency
285
285
  dependencies.forEach((dep) => {
286
+ if (dep.node.type === 'npm') {
287
+ return;
288
+ }
286
289
  // If there are outputs
287
290
  if (dep.outputs && dep.outputs.length > 0) {
288
291
  // Directly map the dependency name to the output paths (dist/packages/..., etc.)
@@ -294,14 +297,19 @@ function updatePaths(dependencies, paths) {
294
297
  // If the path points to the current dependency and is nested (/)
295
298
  if (path.startsWith(nestedName)) {
296
299
  const nestedPart = path.slice(nestedName.length);
297
- // Bind secondary endpoints for ng-packagr projects
300
+ // Bind potential secondary endpoints for ng-packagr projects
298
301
  let mappedPaths = dep.outputs.map((output) => `${output}/${nestedPart}`);
299
- // Get the dependency's package name
300
- const { root } = (dep.node?.data || {});
301
- if (root) {
302
- // Update nested mappings to point to the dependency's output paths
303
- mappedPaths = mappedPaths.concat(paths[path].flatMap((path) => dep.outputs.map((output) => path.replace(root, output))));
304
- }
302
+ const { root } = dep.node.data;
303
+ // Update nested mappings to point to the dependency's output paths
304
+ mappedPaths = mappedPaths.concat(paths[path].flatMap((p) => dep.outputs.flatMap((output) => {
305
+ const basePath = p.replace(root, output);
306
+ return [
307
+ // extension-less path to support compiled output
308
+ basePath.replace(new RegExp(`${(0, path_1.extname)(basePath)}$`, 'gi'), ''),
309
+ // original path with the root re-mapped to the output path
310
+ basePath,
311
+ ];
312
+ })));
305
313
  paths[path] = mappedPaths;
306
314
  }
307
315
  }
@@ -2,7 +2,7 @@ import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project
2
2
  import type { AssetGlob, FileInputOutput } from './assets/assets';
3
3
  import { TransformerEntry } from './typescript/types';
4
4
  // nx-ignore-next-line
5
- const { Linter } = require('@nx/eslint'); // use require to import to avoid circular dependency
5
+ const { Linter, LinterType } = require('@nx/eslint'); // use require to import to avoid circular dependency
6
6
 
7
7
  export type Compiler = 'tsc' | 'swc';
8
8
  export type Bundler = 'swc' | 'tsc' | 'rollup' | 'vite' | 'esbuild' | 'none';
@@ -17,7 +17,7 @@ export interface LibraryGeneratorSchema {
17
17
  skipPackageJson?: boolean;
18
18
  includeBabelRc?: boolean;
19
19
  unitTestRunner?: 'jest' | 'vitest' | 'none';
20
- linter?: Linter;
20
+ linter?: Linter | LinterType;
21
21
  testEnvironment?: 'jsdom' | 'node';
22
22
  importPath?: string;
23
23
  js?: boolean;
@@ -13,6 +13,7 @@ interface BaseTypeCheckOptions {
13
13
  cacheDir?: string;
14
14
  incremental?: boolean;
15
15
  rootDir?: string;
16
+ projectRoot?: string;
16
17
  }
17
18
  type Mode = NoEmitMode | EmitDeclarationOnlyMode;
18
19
  interface NoEmitMode {
@@ -55,21 +55,28 @@ async function runTypeCheck(options) {
55
55
  }
56
56
  async function setupTypeScript(options) {
57
57
  const ts = await Promise.resolve().then(() => require('typescript'));
58
- const { workspaceRoot, tsConfigPath, cacheDir, incremental, rootDir } = options;
58
+ const { workspaceRoot, tsConfigPath, cacheDir, incremental, projectRoot } = options;
59
59
  const config = (0, ts_config_1.readTsConfig)(tsConfigPath);
60
60
  if (config.errors.length) {
61
61
  const errorMessages = config.errors.map((e) => e.messageText).join('\n');
62
62
  throw new Error(`Invalid config file due to following: ${errorMessages}`);
63
63
  }
64
64
  const emitOptions = options.mode === 'emitDeclarationOnly'
65
- ? { emitDeclarationOnly: true, declaration: true, outDir: options.outDir }
65
+ ? {
66
+ emitDeclarationOnly: true,
67
+ declaration: true,
68
+ outDir: options.outDir,
69
+ declarationDir: options.projectRoot && options.outDir.indexOf(projectRoot)
70
+ ? options.outDir.replace(projectRoot, '')
71
+ : undefined,
72
+ }
66
73
  : { noEmit: true };
67
74
  const compilerOptions = {
68
75
  ...config.options,
69
76
  skipLibCheck: true,
70
77
  ...emitOptions,
71
78
  incremental,
72
- rootDir: rootDir || config.options.rootDir,
79
+ rootDir: options.rootDir || config.options.rootDir,
73
80
  };
74
81
  return { ts, workspaceRoot, cacheDir, config, compilerOptions };
75
82
  }