@nx/js 19.0.0-beta.0 → 19.0.0-beta.10

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/babel.js CHANGED
@@ -13,7 +13,7 @@ module.exports = function (api, options = {}) {
13
13
  const emitDecoratorMetadata = api.caller((caller) => caller?.emitDecoratorMetadata ?? true);
14
14
  // Determine settings for `@babel//babel-plugin-transform-class-properties`,
15
15
  // so that we can sync the `loose` option with `@babel/preset-env`.
16
- // TODO(v19): Remove classProperties since it's no longer needed, now that the class props transform is in preset-env.
16
+ // TODO(v20): Remove classProperties since it's no longer needed, now that the class props transform is in preset-env.
17
17
  const loose = options.classProperties?.loose ?? options.loose ?? true;
18
18
  if (options.classProperties) {
19
19
  devkit_1.logger.warn(`Use =\`loose\` option instead of \`classProperties.loose\`. The \`classProperties\` option will be removed in Nx 19`);
package/generators.json CHANGED
@@ -41,6 +41,12 @@
41
41
  "schema": "./src/generators/setup-build/schema.json",
42
42
  "alias": ["build"],
43
43
  "description": "setup-build generator"
44
+ },
45
+ "sync": {
46
+ "factory": "./src/generators/sync/sync#syncGenerator",
47
+ "schema": "./src/generators/sync/schema.json",
48
+ "description": "Synchronize TypeScript project references based on the project graph",
49
+ "hidden": true
44
50
  }
45
51
  }
46
52
  }
package/migrations.json CHANGED
@@ -1,11 +1,5 @@
1
1
  {
2
2
  "generators": {
3
- "rename-swcrc-config": {
4
- "cli": "nx",
5
- "version": "15.8.0-beta.0",
6
- "description": "Rename .lib.swcrc to .swcrc for better SWC support throughout the workspace",
7
- "factory": "./src/migrations/update-15-8-0/rename-swcrc-config"
8
- },
9
3
  "update-16-0-0-add-nx-packages": {
10
4
  "cli": "nx",
11
5
  "version": "16.0.0-beta.1",
@@ -32,18 +26,6 @@
32
26
  }
33
27
  },
34
28
  "packageJsonUpdates": {
35
- "15.8.0": {
36
- "version": "15.8.0-beta.2",
37
- "x-prompt": "Do you want to update to TypeScript v4.9?",
38
- "requires": {
39
- "typescript": ">=4.8.2 <4.9.0"
40
- },
41
- "packages": {
42
- "typescript": {
43
- "version": "~4.9.5"
44
- }
45
- }
46
- },
47
29
  "16.0.0": {
48
30
  "version": "16.0.0-beta.3",
49
31
  "packages": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/js",
3
- "version": "19.0.0-beta.0",
3
+ "version": "19.0.0-beta.10",
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": {
@@ -37,7 +37,6 @@
37
37
  "@babel/preset-env": "^7.23.2",
38
38
  "@babel/preset-typescript": "^7.22.5",
39
39
  "@babel/runtime": "^7.22.6",
40
- "@phenomnomnominal/tsquery": "~5.0.1",
41
40
  "babel-plugin-const-enum": "^1.0.1",
42
41
  "babel-plugin-macros": "^2.8.0",
43
42
  "babel-plugin-transform-typescript-metadata": "^0.3.1",
@@ -57,9 +56,9 @@
57
56
  "semver": "^7.5.3",
58
57
  "source-map-support": "0.5.19",
59
58
  "tslib": "^2.3.0",
60
- "@nx/devkit": "19.0.0-beta.0",
61
- "@nx/workspace": "19.0.0-beta.0",
62
- "@nrwl/js": "19.0.0-beta.0"
59
+ "@nx/devkit": "19.0.0-beta.10",
60
+ "@nx/workspace": "19.0.0-beta.10",
61
+ "@nrwl/js": "19.0.0-beta.10"
63
62
  },
64
63
  "peerDependencies": {
65
64
  "verdaccio": "^5.0.4"
@@ -297,7 +297,7 @@ function getFileToRun(context, project, buildOptions, buildTargetExecutor) {
297
297
  function fileToRunCorrectPath(fileToRun) {
298
298
  if ((0, fileutils_1.fileExists)(fileToRun))
299
299
  return fileToRun;
300
- const extensionsToTry = ['.cjs', '.mjs', 'cjs.js', '.esm.js'];
300
+ const extensionsToTry = ['.cjs', '.mjs', '.cjs.js', '.esm.js'];
301
301
  for (const ext of extensionsToTry) {
302
302
  const file = fileToRun.replace(/\.js$/, ext);
303
303
  if ((0, fileutils_1.fileExists)(file))
@@ -8,6 +8,10 @@ const detectPort = require("detect-port");
8
8
  const path_1 = require("path");
9
9
  const semver_1 = require("semver");
10
10
  let childProcess;
11
+ let env = {
12
+ SKIP_YARN_COREPACK_CHECK: 'true',
13
+ ...process.env,
14
+ };
11
15
  /**
12
16
  * - set npm and yarn to use local registry
13
17
  * - start verdaccio
@@ -104,19 +108,19 @@ function createVerdaccioOptions(options, workspaceRoot) {
104
108
  }
105
109
  function setupNpm(options) {
106
110
  try {
107
- (0, child_process_1.execSync)('npm --version');
111
+ (0, child_process_1.execSync)('npm --version', { env });
108
112
  }
109
113
  catch (e) {
110
114
  return () => { };
111
115
  }
112
116
  let npmRegistryPath;
113
117
  try {
114
- npmRegistryPath = (0, child_process_1.execSync)(`npm config get registry --location ${options.location}`)
118
+ npmRegistryPath = (0, child_process_1.execSync)(`npm config get registry --location ${options.location}`, { env })
115
119
  ?.toString()
116
120
  ?.trim()
117
121
  ?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes
118
- (0, child_process_1.execSync)(`npm config set registry http://localhost:${options.port}/ --location ${options.location}`);
119
- (0, child_process_1.execSync)(`npm config set //localhost:${options.port}/:_authToken="secretVerdaccioToken" --location ${options.location}`);
122
+ (0, child_process_1.execSync)(`npm config set registry http://localhost:${options.port}/ --location ${options.location}`, { env });
123
+ (0, child_process_1.execSync)(`npm config set //localhost:${options.port}/:_authToken="secretVerdaccioToken" --location ${options.location}`, { env });
120
124
  devkit_1.logger.info(`Set npm registry to http://localhost:${options.port}/`);
121
125
  }
122
126
  catch (e) {
@@ -124,19 +128,21 @@ function setupNpm(options) {
124
128
  }
125
129
  return () => {
126
130
  try {
127
- const currentNpmRegistryPath = (0, child_process_1.execSync)(`npm config get registry --location ${options.location}`)
131
+ const currentNpmRegistryPath = (0, child_process_1.execSync)(`npm config get registry --location ${options.location}`, { env })
128
132
  ?.toString()
129
133
  ?.trim()
130
134
  ?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes
131
135
  if (npmRegistryPath && currentNpmRegistryPath.includes('localhost')) {
132
- (0, child_process_1.execSync)(`npm config set registry ${npmRegistryPath} --location ${options.location}`);
136
+ (0, child_process_1.execSync)(`npm config set registry ${npmRegistryPath} --location ${options.location}`, { env });
133
137
  devkit_1.logger.info(`Reset npm registry to ${npmRegistryPath}`);
134
138
  }
135
139
  else {
136
- (0, child_process_1.execSync)(`npm config delete registry --location ${options.location}`);
140
+ (0, child_process_1.execSync)(`npm config delete registry --location ${options.location}`, {
141
+ env,
142
+ });
137
143
  devkit_1.logger.info('Cleared custom npm registry');
138
144
  }
139
- (0, child_process_1.execSync)(`npm config delete //localhost:${options.port}/:_authToken --location ${options.location}`);
145
+ (0, child_process_1.execSync)(`npm config delete //localhost:${options.port}/:_authToken --location ${options.location}`, { env });
140
146
  }
141
147
  catch (e) {
142
148
  throw new Error(`Failed to reset npm registry: ${e.message}`);
@@ -145,22 +151,25 @@ function setupNpm(options) {
145
151
  }
146
152
  function getYarnUnsafeHttpWhitelist(isYarnV1) {
147
153
  return !isYarnV1
148
- ? new Set(JSON.parse((0, child_process_1.execSync)(`yarn config get unsafeHttpWhitelist --json`).toString()))
154
+ ? new Set(JSON.parse((0, child_process_1.execSync)(`yarn config get unsafeHttpWhitelist --json`, {
155
+ env,
156
+ }).toString()))
149
157
  : null;
150
158
  }
151
159
  function setYarnUnsafeHttpWhitelist(currentWhitelist, options) {
152
160
  if (currentWhitelist.size > 0) {
153
- (0, child_process_1.execSync)(`yarn config set unsafeHttpWhitelist --json '${JSON.stringify(Array.from(currentWhitelist))}'` + (options.location === 'user' ? ' --home' : ''));
161
+ (0, child_process_1.execSync)(`yarn config set unsafeHttpWhitelist --json '${JSON.stringify(Array.from(currentWhitelist))}'` + (options.location === 'user' ? ' --home' : ''), { env });
154
162
  }
155
163
  else {
156
164
  (0, child_process_1.execSync)(`yarn config unset unsafeHttpWhitelist` +
157
- (options.location === 'user' ? ' --home' : ''));
165
+ (options.location === 'user' ? ' --home' : ''), { env });
158
166
  }
159
167
  }
160
168
  function setupYarn(options) {
161
169
  let isYarnV1;
162
170
  try {
163
- isYarnV1 = (0, semver_1.major)((0, child_process_1.execSync)('yarn --version').toString().trim()) === 1;
171
+ isYarnV1 =
172
+ (0, semver_1.major)((0, child_process_1.execSync)('yarn --version', { env }).toString().trim()) === 1;
164
173
  }
165
174
  catch {
166
175
  // This would fail if yarn is not installed which is okay
@@ -168,12 +177,14 @@ function setupYarn(options) {
168
177
  }
169
178
  try {
170
179
  const registryConfigName = isYarnV1 ? 'registry' : 'npmRegistryServer';
171
- const yarnRegistryPath = (0, child_process_1.execSync)(`yarn config get ${registryConfigName}`)
180
+ const yarnRegistryPath = (0, child_process_1.execSync)(`yarn config get ${registryConfigName}`, {
181
+ env,
182
+ })
172
183
  ?.toString()
173
184
  ?.trim()
174
185
  ?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes
175
186
  (0, child_process_1.execSync)(`yarn config set ${registryConfigName} http://localhost:${options.port}/` +
176
- (options.location === 'user' ? ' --home' : ''));
187
+ (options.location === 'user' ? ' --home' : ''), { env });
177
188
  devkit_1.logger.info(`Set yarn registry to http://localhost:${options.port}/`);
178
189
  const currentWhitelist = getYarnUnsafeHttpWhitelist(isYarnV1);
179
190
  let whitelistedLocalhost = false;
@@ -185,18 +196,18 @@ function setupYarn(options) {
185
196
  }
186
197
  return () => {
187
198
  try {
188
- const currentYarnRegistryPath = (0, child_process_1.execSync)(`yarn config get ${registryConfigName}`)
199
+ const currentYarnRegistryPath = (0, child_process_1.execSync)(`yarn config get ${registryConfigName}`, { env })
189
200
  ?.toString()
190
201
  ?.trim()
191
202
  ?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes
192
203
  if (yarnRegistryPath && currentYarnRegistryPath.includes('localhost')) {
193
204
  (0, child_process_1.execSync)(`yarn config set ${registryConfigName} ${yarnRegistryPath}` +
194
- (options.location === 'user' ? ' --home' : ''));
205
+ (options.location === 'user' ? ' --home' : ''), { env });
195
206
  devkit_1.logger.info(`Reset yarn ${registryConfigName} to ${yarnRegistryPath}`);
196
207
  }
197
208
  else {
198
209
  (0, child_process_1.execSync)(`yarn config ${isYarnV1 ? 'delete' : 'unset'} ${registryConfigName}` +
199
- (options.location === 'user' ? ' --home' : ''));
210
+ (options.location === 'user' ? ' --home' : ''), { env });
200
211
  devkit_1.logger.info(`Cleared custom yarn ${registryConfigName}`);
201
212
  }
202
213
  if (whitelistedLocalhost) {
@@ -0,0 +1 @@
1
+ export interface SyncSchema {}
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "action",
4
+ "type": "object",
5
+ "description": "Synchronize TypeScript project references based on the project graph.",
6
+ "properties": {},
7
+ "required": []
8
+ }
@@ -0,0 +1,3 @@
1
+ import { Tree } from '@nx/devkit';
2
+ import { SyncSchema } from './schema';
3
+ export declare function syncGenerator(tree: Tree, options: SyncSchema): Promise<void>;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.syncGenerator = void 0;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const node_path_1 = require("node:path");
6
+ const plugin_1 = require("../../plugins/typescript/plugin");
7
+ async function syncGenerator(tree, options) {
8
+ // Ensure that the plugin has been wired up in nx.json
9
+ const nxJson = (0, devkit_1.readNxJson)(tree);
10
+ let tscPluginConfig = nxJson.plugins.find((p) => {
11
+ if (typeof p === 'string') {
12
+ return p === plugin_1.PLUGIN_NAME;
13
+ }
14
+ return p.plugin === plugin_1.PLUGIN_NAME;
15
+ });
16
+ if (!tscPluginConfig) {
17
+ throw new Error(`The ${plugin_1.PLUGIN_NAME} plugin must be added to the "plugins" array in nx.json before syncing tsconfigs`);
18
+ }
19
+ const projectGraph = await (0, devkit_1.createProjectGraphAsync)();
20
+ const firstPartyDeps = Object.entries(projectGraph.dependencies).filter(([name, data]) => !name.startsWith('npm:') && data.length > 0);
21
+ // Root tsconfig containing project references for the whole workspace
22
+ const rootTsconfigPath = 'tsconfig.json';
23
+ const rootTsconfig = (0, devkit_1.readJson)(tree, rootTsconfigPath);
24
+ const tsconfigProjectNodeValues = Object.values(projectGraph.nodes).filter((node) => {
25
+ const projectTsconfigPath = (0, devkit_1.joinPathFragments)(node.data.root, 'tsconfig.json');
26
+ return tree.exists(projectTsconfigPath);
27
+ });
28
+ if (tsconfigProjectNodeValues.length > 0) {
29
+ // Sync the root tsconfig references from the project graph (do not destroy existing references)
30
+ rootTsconfig.references = rootTsconfig.references || [];
31
+ const referencesSet = new Set(rootTsconfig.references.map((ref) => normalizeReferencePath(ref.path)));
32
+ for (const node of tsconfigProjectNodeValues) {
33
+ const normalizedPath = normalizeReferencePath(node.data.root);
34
+ // Skip the root tsconfig itself
35
+ if (node.data.root !== '.' && !referencesSet.has(normalizedPath)) {
36
+ rootTsconfig.references.push({ path: `./${normalizedPath}` });
37
+ }
38
+ }
39
+ (0, devkit_1.writeJson)(tree, rootTsconfigPath, rootTsconfig);
40
+ }
41
+ for (const [name, data] of firstPartyDeps) {
42
+ // Get the source project nodes for the source and target
43
+ const sourceProjectNode = projectGraph.nodes[name];
44
+ // Find the relevant tsconfig files for the source project
45
+ const sourceProjectTsconfigPath = (0, devkit_1.joinPathFragments)(sourceProjectNode.data.root, 'tsconfig.json');
46
+ if (!tree.exists(sourceProjectTsconfigPath)) {
47
+ console.warn(`Skipping project "${name}" as there is no tsconfig.json file found in the project root "${sourceProjectNode.data.root}"`);
48
+ continue;
49
+ }
50
+ const sourceTsconfig = (0, devkit_1.readJson)(tree, sourceProjectTsconfigPath);
51
+ for (const dep of data) {
52
+ // Get the target project node
53
+ const targetProjectNode = projectGraph.nodes[dep.target];
54
+ if (!targetProjectNode) {
55
+ // It's an external dependency
56
+ continue;
57
+ }
58
+ // Set defaults only in the case where we have at least one dependency so that we don't patch files when not necessary
59
+ sourceTsconfig.references = sourceTsconfig.references || [];
60
+ // Ensure the project reference for the target is set
61
+ const relativePathToTargetRoot = (0, node_path_1.relative)(sourceProjectNode.data.root, targetProjectNode.data.root);
62
+ if (!sourceTsconfig.references.some((ref) => ref.path === relativePathToTargetRoot)) {
63
+ // Make sure we unshift rather than push so that dependencies are built in the right order by TypeScript when it is run directly from the root of the workspace
64
+ sourceTsconfig.references.unshift({ path: relativePathToTargetRoot });
65
+ }
66
+ }
67
+ // Update the source tsconfig files
68
+ (0, devkit_1.writeJson)(tree, sourceProjectTsconfigPath, sourceTsconfig);
69
+ }
70
+ await (0, devkit_1.formatFiles)(tree);
71
+ }
72
+ exports.syncGenerator = syncGenerator;
73
+ // Normalize the paths to strip leading `./` and trailing `/tsconfig.json`
74
+ function normalizeReferencePath(path) {
75
+ return path.replace(/\/tsconfig.json$/, '').replace(/^\.\//, '');
76
+ }
@@ -0,0 +1,13 @@
1
+ import { type CreateDependencies, type CreateNodes } from '@nx/devkit';
2
+ export interface TscPluginOptions {
3
+ typecheck?: boolean | {
4
+ targetName?: string;
5
+ };
6
+ build?: boolean | {
7
+ targetName?: string;
8
+ configName?: string;
9
+ };
10
+ }
11
+ export declare const createDependencies: CreateDependencies;
12
+ export declare const PLUGIN_NAME = "@nx/js/typescript";
13
+ export declare const createNodes: CreateNodes<TscPluginOptions>;
@@ -0,0 +1,348 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNodes = exports.PLUGIN_NAME = exports.createDependencies = void 0;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
6
+ const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
7
+ const node_fs_1 = require("node:fs");
8
+ const node_path_1 = require("node:path");
9
+ const minimatch_1 = require("minimatch");
10
+ // eslint-disable-next-line @typescript-eslint/no-restricted-imports
11
+ const lock_file_1 = require("nx/src/plugins/js/lock-file/lock-file");
12
+ const cache_directory_1 = require("nx/src/utils/cache-directory");
13
+ const ts_config_1 = require("../../utils/typescript/ts-config");
14
+ const cachePath = (0, node_path_1.join)(cache_directory_1.projectGraphCacheDirectory, 'tsc.hash');
15
+ const targetsCache = (0, node_fs_1.existsSync)(cachePath) ? readTargetsCache() : {};
16
+ const calculatedTargets = {};
17
+ function readTargetsCache() {
18
+ return (0, devkit_1.readJsonFile)(cachePath);
19
+ }
20
+ function writeTargetsToCache(targets) {
21
+ (0, devkit_1.writeJsonFile)(cachePath, targets);
22
+ }
23
+ const createDependencies = () => {
24
+ writeTargetsToCache(calculatedTargets);
25
+ return [];
26
+ };
27
+ exports.createDependencies = createDependencies;
28
+ exports.PLUGIN_NAME = '@nx/js/typescript';
29
+ exports.createNodes = [
30
+ '**/tsconfig*.json',
31
+ (configFilePath, options, context) => {
32
+ const pluginOptions = normalizePluginOptions(options);
33
+ const projectRoot = (0, node_path_1.dirname)(configFilePath);
34
+ const fullConfigPath = (0, devkit_1.joinPathFragments)(context.workspaceRoot, configFilePath);
35
+ // Do not create a project if package.json and project.json isn't there.
36
+ const siblingFiles = (0, node_fs_1.readdirSync)((0, node_path_1.join)(context.workspaceRoot, projectRoot));
37
+ if (!siblingFiles.includes('package.json') &&
38
+ !siblingFiles.includes('project.json')) {
39
+ return {};
40
+ }
41
+ // Do not create a project if it's not a tsconfig.json and there is no tsconfig.json in the same directory
42
+ if ((0, node_path_1.basename)(configFilePath) !== 'tsconfig.json' &&
43
+ !siblingFiles.includes('tsconfig.json')) {
44
+ return {};
45
+ }
46
+ const nodeHash = (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, pluginOptions, context, [(0, lock_file_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot))]);
47
+ // The hash is calculated at the node/project level, so we add the config file path to avoid conflicts when caching
48
+ const cacheKey = `${nodeHash}_${configFilePath}`;
49
+ const targets = targetsCache[cacheKey]
50
+ ? targetsCache[cacheKey]
51
+ : buildTscTargets(fullConfigPath, projectRoot, pluginOptions, context);
52
+ calculatedTargets[cacheKey] = targets;
53
+ return {
54
+ projects: {
55
+ [projectRoot]: {
56
+ projectType: 'library',
57
+ targets,
58
+ },
59
+ },
60
+ };
61
+ },
62
+ ];
63
+ function buildTscTargets(configFilePath, projectRoot, options, context) {
64
+ const targets = {};
65
+ const namedInputs = (0, get_named_inputs_1.getNamedInputs)(projectRoot, context);
66
+ const tsConfig = readCachedTsConfig(configFilePath);
67
+ // TODO: check whether we want to always run with --pretty --verbose, it makes replacing scripts harder
68
+ // `--verbose` conflicts with `tsc -b --clean`, might be another reason for not using it, it would
69
+ // prevent users from running the task with `--clean` flag.
70
+ // Should we consider creating a different optional target for `--clean`?
71
+ // Should we consider having a plugin option to disable `--pretty` and `--verbose`?
72
+ let internalProjectReferences;
73
+ // Typecheck target
74
+ if ((0, node_path_1.basename)(configFilePath) === 'tsconfig.json' && options.typecheck) {
75
+ internalProjectReferences = resolveInternalProjectReferences(configFilePath, tsConfig);
76
+ const targetName = options.typecheck.targetName;
77
+ if (!targets[targetName]) {
78
+ let command = `tsc --build --emitDeclarationOnly --pretty --verbose`;
79
+ if (tsConfig.options.noEmit ||
80
+ Object.values(internalProjectReferences).some((ref) => ref.options.noEmit)) {
81
+ // `--emitDeclarationOnly` and `--noEmit` are mutually exclusive, so
82
+ // we remove `--emitDeclarationOnly` if `--noEmit` is set.
83
+ command = `tsc --build --pretty --verbose`;
84
+ }
85
+ targets[targetName] = {
86
+ dependsOn: [`^${targetName}`],
87
+ command,
88
+ options: { cwd: projectRoot },
89
+ cache: true,
90
+ inputs: getInputs(namedInputs, configFilePath, tsConfig, internalProjectReferences, context.workspaceRoot, projectRoot),
91
+ outputs: getOutputs(configFilePath, tsConfig, internalProjectReferences, context.workspaceRoot, projectRoot),
92
+ };
93
+ }
94
+ }
95
+ // Build target
96
+ if (options.build && (0, node_path_1.basename)(configFilePath) === options.build.configName) {
97
+ internalProjectReferences ??= resolveInternalProjectReferences(configFilePath, tsConfig);
98
+ const targetName = options.build.targetName;
99
+ targets[targetName] = {
100
+ dependsOn: [`^${targetName}`],
101
+ command: `tsc --build ${options.build.configName} --pretty --verbose`,
102
+ options: { cwd: projectRoot },
103
+ cache: true,
104
+ inputs: getInputs(namedInputs, configFilePath, tsConfig, internalProjectReferences, context.workspaceRoot, projectRoot),
105
+ outputs: getOutputs(configFilePath, tsConfig, internalProjectReferences, context.workspaceRoot, projectRoot),
106
+ };
107
+ }
108
+ return targets;
109
+ }
110
+ function getInputs(namedInputs, configFilePath, tsConfig, internalProjectReferences, workspaceRoot, projectRoot) {
111
+ const configFiles = new Set();
112
+ const includePaths = new Set();
113
+ const excludePaths = new Set();
114
+ const extendedConfigFiles = getExtendedConfigFiles(configFilePath, tsConfig);
115
+ extendedConfigFiles.forEach((configPath) => {
116
+ configFiles.add(configPath);
117
+ });
118
+ const projectTsConfigFiles = [
119
+ [configFilePath, tsConfig],
120
+ ...Object.entries(internalProjectReferences),
121
+ ];
122
+ projectTsConfigFiles.forEach(([configPath, config]) => {
123
+ configFiles.add(configPath);
124
+ (config.raw?.include ?? []).forEach((p) => includePaths.add(p));
125
+ if (config.raw?.exclude) {
126
+ /**
127
+ * We need to filter out the exclude paths that are already included in
128
+ * other tsconfig files. If they are not included in other tsconfig files,
129
+ * they still correctly apply to the current file and we should keep them.
130
+ */
131
+ const otherFilesInclude = [];
132
+ projectTsConfigFiles.forEach(([path, c]) => {
133
+ if (path !== configPath) {
134
+ otherFilesInclude.push(...(c.raw?.include ?? []));
135
+ }
136
+ });
137
+ const normalize = (p) => (p.startsWith('./') ? p.slice(2) : p);
138
+ config.raw.exclude.forEach((excludePath) => {
139
+ if (!otherFilesInclude.some((includePath) => (0, minimatch_1.minimatch)(normalize(includePath), normalize(excludePath)) ||
140
+ (0, minimatch_1.minimatch)(normalize(excludePath), normalize(includePath)))) {
141
+ excludePaths.add(excludePath);
142
+ }
143
+ });
144
+ }
145
+ });
146
+ const inputs = [];
147
+ if (includePaths.size) {
148
+ inputs.push(...Array.from(configFiles).map((p) => pathToInputOrOutput(p, workspaceRoot, projectRoot)), ...Array.from(includePaths).map((p) => (0, devkit_1.joinPathFragments)('{projectRoot}', p)));
149
+ }
150
+ else {
151
+ // If we couldn't identify any include paths, we default to the default
152
+ // named inputs.
153
+ inputs.push('production' in namedInputs ? 'production' : 'default');
154
+ }
155
+ if (excludePaths.size) {
156
+ inputs.push(...Array.from(excludePaths).map((p) => `!${(0, devkit_1.joinPathFragments)('{projectRoot}', p)}`));
157
+ }
158
+ if (hasExternalProjectReferences(configFilePath, tsConfig)) {
159
+ // Importing modules from a referenced project will load its output declaration files (d.ts)
160
+ // https://www.typescriptlang.org/docs/handbook/project-references.html#what-is-a-project-reference
161
+ inputs.push({ dependentTasksOutputFiles: '**/*.d.ts' });
162
+ }
163
+ else {
164
+ inputs.push('production' in namedInputs ? '^production' : '^default');
165
+ }
166
+ inputs.push({ externalDependencies: ['typescript'] });
167
+ return inputs;
168
+ }
169
+ function getOutputs(configFilePath, tsConfig, internalProjectReferences, workspaceRoot, projectRoot) {
170
+ const outputs = new Set();
171
+ // We could have more surgical outputs based on the tsconfig options, but the
172
+ // user could override them through the command line and that wouldn't be
173
+ // reflected in the outputs. So, we just include everything that could be
174
+ // produced by the tsc command.
175
+ [tsConfig, ...Object.values(internalProjectReferences)].forEach((config) => {
176
+ if (config.options.outFile) {
177
+ const outFileName = (0, node_path_1.basename)(config.options.outFile, '.js');
178
+ const outFileDir = (0, node_path_1.dirname)(config.options.outFile);
179
+ outputs.add((0, devkit_1.joinPathFragments)('{workspaceRoot}', (0, node_path_1.relative)(workspaceRoot, config.options.outFile)));
180
+ // outFile is not be used with .cjs, .mjs, .jsx, so the list is simpler
181
+ const outDir = (0, node_path_1.relative)(workspaceRoot, outFileDir);
182
+ outputs.add((0, devkit_1.joinPathFragments)('{workspaceRoot}', outDir, `${outFileName}.js.map`));
183
+ outputs.add((0, devkit_1.joinPathFragments)('{workspaceRoot}', outDir, `${outFileName}.d.ts`));
184
+ outputs.add((0, devkit_1.joinPathFragments)('{workspaceRoot}', outDir, `${outFileName}.d.ts.map`));
185
+ // https://www.typescriptlang.org/tsconfig#tsBuildInfoFile
186
+ outputs.add(tsConfig.options.tsBuildInfoFile
187
+ ? pathToInputOrOutput(tsConfig.options.tsBuildInfoFile, workspaceRoot, projectRoot)
188
+ : (0, devkit_1.joinPathFragments)('{workspaceRoot}', outDir, `${outFileName}.tsbuildinfo`));
189
+ }
190
+ else if (config.options.outDir) {
191
+ outputs.add((0, devkit_1.joinPathFragments)('{workspaceRoot}', (0, node_path_1.relative)(workspaceRoot, config.options.outDir)));
192
+ }
193
+ else if (config.fileNames.length) {
194
+ // tsc produce files in place when no outDir or outFile is set
195
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.js'));
196
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.cjs'));
197
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.mjs'));
198
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.jsx'));
199
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.js.map')); // should also include .cjs and .mjs data
200
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.jsx.map'));
201
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.d.ts'));
202
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.d.cts'));
203
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.d.mts'));
204
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.d.ts.map'));
205
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.d.cts.map'));
206
+ outputs.add((0, devkit_1.joinPathFragments)('{projectRoot}', '**/*.d.mts.map'));
207
+ // https://www.typescriptlang.org/tsconfig#tsBuildInfoFile
208
+ const name = (0, node_path_1.basename)(configFilePath, '.json');
209
+ outputs.add(tsConfig.options.tsBuildInfoFile
210
+ ? pathToInputOrOutput(tsConfig.options.tsBuildInfoFile, workspaceRoot, projectRoot)
211
+ : (0, devkit_1.joinPathFragments)('{projectRoot}', `${name}.tsbuildinfo`));
212
+ }
213
+ });
214
+ return Array.from(outputs);
215
+ }
216
+ function pathToInputOrOutput(path, workspaceRoot, projectRoot) {
217
+ const pathRelativeToProjectRoot = (0, devkit_1.normalizePath)((0, node_path_1.relative)(projectRoot, path));
218
+ if (pathRelativeToProjectRoot.startsWith('..')) {
219
+ return (0, devkit_1.joinPathFragments)('{workspaceRoot}', (0, node_path_1.relative)(workspaceRoot, path));
220
+ }
221
+ return (0, devkit_1.joinPathFragments)('{projectRoot}', pathRelativeToProjectRoot);
222
+ }
223
+ function getExtendedConfigFiles(tsConfigPath, tsConfig) {
224
+ const extendedConfigFiles = new Set();
225
+ let currentConfigPath = tsConfigPath;
226
+ let currentConfig = tsConfig;
227
+ while (currentConfig.raw?.extends) {
228
+ const extendedConfigPath = (0, node_path_1.join)((0, node_path_1.dirname)(currentConfigPath), currentConfig.raw.extends);
229
+ extendedConfigFiles.add(extendedConfigPath);
230
+ const extendedConfig = readCachedTsConfig(extendedConfigPath);
231
+ currentConfigPath = extendedConfigPath;
232
+ currentConfig = extendedConfig;
233
+ }
234
+ return Array.from(extendedConfigFiles);
235
+ }
236
+ function resolveInternalProjectReferences(configFilePath, tsConfig, projectReferences = {}) {
237
+ if (!tsConfig.projectReferences?.length) {
238
+ return projectReferences;
239
+ }
240
+ const basePath = getTsConfigBasePath(configFilePath);
241
+ for (const ref of tsConfig.projectReferences) {
242
+ let refConfigPath = ref.path;
243
+ if (projectReferences[refConfigPath]) {
244
+ // Already resolved
245
+ continue;
246
+ }
247
+ if (isExternalProjectReference(refConfigPath, basePath)) {
248
+ continue;
249
+ }
250
+ if (!refConfigPath.endsWith('.json')) {
251
+ refConfigPath = (0, node_path_1.join)(refConfigPath, 'tsconfig.json');
252
+ }
253
+ const refTsConfig = readCachedTsConfig(refConfigPath);
254
+ projectReferences[refConfigPath] = refTsConfig;
255
+ resolveInternalProjectReferences(refConfigPath, refTsConfig, projectReferences);
256
+ }
257
+ return projectReferences;
258
+ }
259
+ function hasExternalProjectReferences(tsConfigPath, tsConfig, seen = new Set()) {
260
+ if (!tsConfig.projectReferences?.length) {
261
+ return false;
262
+ }
263
+ seen.add(tsConfigPath);
264
+ const basePath = getTsConfigBasePath(tsConfigPath);
265
+ for (const ref of tsConfig.projectReferences) {
266
+ let refConfigPath = ref.path;
267
+ if (seen.has(refConfigPath)) {
268
+ // Already seen
269
+ continue;
270
+ }
271
+ if (isExternalProjectReference(refConfigPath, basePath)) {
272
+ return true;
273
+ }
274
+ if (!refConfigPath.endsWith('.json')) {
275
+ refConfigPath = (0, node_path_1.join)(refConfigPath, 'tsconfig.json');
276
+ }
277
+ const refTsConfig = readCachedTsConfig(refConfigPath);
278
+ const result = hasExternalProjectReferences(refConfigPath, refTsConfig);
279
+ if (result) {
280
+ return true;
281
+ }
282
+ }
283
+ return false;
284
+ }
285
+ function isExternalProjectReference(refTsConfigPath, basePath) {
286
+ const refBasePath = getTsConfigBasePath(refTsConfigPath);
287
+ // TODO: there could be internal project references in nested dirs (e.g.
288
+ // our storybook generator generates a nested `.storybook/tsconfig.json`),
289
+ // which would be considered an external project reference but it's not.
290
+ // We could instead check if the referenced tsconfig is outside the project
291
+ // root, but that would cause issues with standalone workspaces with nested
292
+ // projects.
293
+ return refBasePath !== basePath;
294
+ }
295
+ function getTsConfigBasePath(tsConfigPath) {
296
+ return (0, node_fs_1.statSync)(tsConfigPath).isFile() ? (0, node_path_1.dirname)(tsConfigPath) : tsConfigPath;
297
+ }
298
+ // TODO: we could probably persist this to disk to avoid reading the same
299
+ // tsconfig files over multiple runs
300
+ const tsConfigCache = new Map();
301
+ function readCachedTsConfig(tsConfigPath) {
302
+ const cacheKey = getTsConfigCacheKey(tsConfigPath);
303
+ if (tsConfigCache.has(cacheKey)) {
304
+ return tsConfigCache.get(cacheKey);
305
+ }
306
+ const tsConfig = (0, ts_config_1.readTsConfig)(tsConfigPath);
307
+ tsConfigCache.set(cacheKey, tsConfig);
308
+ return tsConfig;
309
+ }
310
+ function getTsConfigCacheKey(tsConfigPath) {
311
+ const timestamp = (0, node_fs_1.statSync)(tsConfigPath).mtimeMs;
312
+ return `${tsConfigPath}-${timestamp}`;
313
+ }
314
+ function normalizePluginOptions(pluginOptions = {}) {
315
+ const defaultTypecheckTargetName = 'typecheck';
316
+ let typecheck = {
317
+ targetName: defaultTypecheckTargetName,
318
+ };
319
+ if (pluginOptions.typecheck === false) {
320
+ typecheck = false;
321
+ }
322
+ else if (pluginOptions.typecheck &&
323
+ typeof pluginOptions.typecheck !== 'boolean') {
324
+ typecheck = {
325
+ targetName: pluginOptions.typecheck.targetName ?? defaultTypecheckTargetName,
326
+ };
327
+ }
328
+ const defaultBuildTargetName = 'build';
329
+ const defaultBuildConfigName = 'tsconfig.lib.json';
330
+ let build = {
331
+ targetName: defaultBuildTargetName,
332
+ configName: defaultBuildConfigName,
333
+ };
334
+ // Build target is not enabled by default
335
+ if (!pluginOptions.build) {
336
+ build = false;
337
+ }
338
+ else if (pluginOptions.build && typeof pluginOptions.build !== 'boolean') {
339
+ build = {
340
+ targetName: pluginOptions.build.targetName ?? defaultBuildTargetName,
341
+ configName: pluginOptions.build.configName ?? defaultBuildConfigName,
342
+ };
343
+ }
344
+ return {
345
+ typecheck,
346
+ build,
347
+ };
348
+ }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CopyAssetsHandler = exports.defaultFileEventHandler = void 0;
4
4
  const minimatch_1 = require("minimatch");
5
- const path = require("path");
5
+ const path = require("node:path/posix");
6
6
  const fse = require("fs-extra");
7
7
  const ignore_1 = require("ignore");
8
8
  const fg = require("fast-glob");
@@ -0,0 +1 @@
1
+ export { createDependencies, createNodes, } from './src/plugins/typescript/plugin';
package/typescript.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNodes = exports.createDependencies = void 0;
4
+ var plugin_1 = require("./src/plugins/typescript/plugin");
5
+ Object.defineProperty(exports, "createDependencies", { enumerable: true, get: function () { return plugin_1.createDependencies; } });
6
+ Object.defineProperty(exports, "createNodes", { enumerable: true, get: function () { return plugin_1.createNodes; } });
@@ -1,2 +0,0 @@
1
- import { Tree } from '@nx/devkit';
2
- export default function (tree: Tree): Promise<void>;
@@ -1,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const devkit_1 = require("@nx/devkit");
4
- const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
5
- const tsquery_1 = require("@phenomnomnominal/tsquery");
6
- async function default_1(tree) {
7
- let changesMade = false;
8
- const projects = (0, devkit_1.getProjects)(tree);
9
- (0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nrwl/js:swc', (_, projectName, target, configurationName) => {
10
- const projectConfiguration = projects.get(projectName);
11
- const executorOptions = configurationName
12
- ? projectConfiguration.targets[target].configurations[configurationName]
13
- : projectConfiguration.targets[target].options;
14
- // if the project uses a custom path to swcrc file
15
- // and only if it's the default name
16
- if (executorOptions.swcrc &&
17
- executorOptions.swcrc.includes('.lib.swcrc')) {
18
- const newSwcrc = executorOptions.swcrc.replace('.lib.swcrc', '.swcrc');
19
- // rename the swcrc file first
20
- tree.rename(executorOptions.swcrc, newSwcrc);
21
- // then update the executor options
22
- executorOptions.swcrc = newSwcrc;
23
- changesMade = true;
24
- }
25
- const libSwcrcPath = (0, devkit_1.joinPathFragments)(projectConfiguration.root, '.lib.swcrc') ||
26
- (0, devkit_1.joinPathFragments)(projectConfiguration.sourceRoot, '.lib.swcrc');
27
- const isLibSwcrcExist = tree.exists(libSwcrcPath);
28
- if (isLibSwcrcExist) {
29
- tree.rename(libSwcrcPath, libSwcrcPath.replace('.lib.swcrc', '.swcrc'));
30
- changesMade = true;
31
- }
32
- (0, devkit_1.updateProjectConfiguration)(tree, projectName, projectConfiguration);
33
- });
34
- (0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nrwl/jest:jest', (_, projectName, target, configurationName) => {
35
- const projectConfiguration = projects.get(projectName);
36
- const executorOptions = configurationName
37
- ? projectConfiguration.targets[target].configurations[configurationName]
38
- : projectConfiguration.targets[target].options;
39
- const isJestConfigExist = executorOptions.jestConfig && tree.exists(executorOptions.jestConfig);
40
- if (isJestConfigExist) {
41
- const jestConfig = tree.read(executorOptions.jestConfig, 'utf-8');
42
- const jsonParseNodes = tsquery_1.tsquery.query(jestConfig, ':matches(CallExpression:has(Identifier[name="JSON"]):has(Identifier[name="parse"]))');
43
- if (jsonParseNodes.length) {
44
- // if we already assign false to swcrc, skip
45
- if (jestConfig.includes('.swcrc = false')) {
46
- return;
47
- }
48
- let updatedJestConfig = tsquery_1.tsquery.replace(jestConfig, 'CallExpression:has(Identifier[name="JSON"]):has(Identifier[name="parse"]) TemplateSpan', (templateSpan) => {
49
- if (templateSpan.literal.text === '/.lib.swcrc') {
50
- return templateSpan
51
- .getFullText()
52
- .replace('.lib.swcrc', '.swcrc');
53
- }
54
- return '';
55
- });
56
- updatedJestConfig = tsquery_1.tsquery.replace(updatedJestConfig, ':matches(ExportAssignment, BinaryExpression:has(Identifier[name="module"]):has(Identifier[name="exports"]))', (node) => {
57
- return `
58
-
59
- // disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves.
60
- // If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude"
61
- if (swcJestConfig.swcrc === undefined) {
62
- swcJestConfig.swcrc = false;
63
- }
64
-
65
- ${node.getFullText()}
66
- `;
67
- });
68
- tree.write(executorOptions.jestConfig, updatedJestConfig);
69
- changesMade = true;
70
- }
71
- }
72
- });
73
- if (changesMade) {
74
- await (0, devkit_1.formatFiles)(tree);
75
- }
76
- }
77
- exports.default = default_1;