@nx/js 20.3.1 → 20.4.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2017-2024 Narwhal Technologies Inc.
3
+ Copyright (c) 2017-2025 Narwhal Technologies Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/js",
3
- "version": "20.3.1",
3
+ "version": "20.4.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": {
@@ -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.3.1",
43
- "@nx/workspace": "20.3.1",
42
+ "@nx/devkit": "20.4.0-beta.0",
43
+ "@nx/workspace": "20.4.0-beta.0",
44
44
  "@zkochan/js-yaml": "0.0.7",
45
45
  "babel-plugin-const-enum": "^1.0.1",
46
46
  "babel-plugin-macros": "^2.8.0",
@@ -29,9 +29,7 @@ function normalizeOptions(options, root, sourceRoot, projectRoot) {
29
29
  }
30
30
  }
31
31
  const outputPath = (0, path_1.join)(root, options.outputPath);
32
- if (options.skipTypeCheck == null && !isTsSolutionSetup) {
33
- options.skipTypeCheck = false;
34
- }
32
+ options.skipTypeCheck ??= !isTsSolutionSetup;
35
33
  if (options.watch == null) {
36
34
  options.watch = false;
37
35
  }
@@ -1,17 +1,9 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "allowJs": false,
4
- "allowSyntheticDefaultImports": true,
5
3
  "composite": true,
6
- "declaration": true,
7
4
  "declarationMap": true,
8
5
  "emitDeclarationOnly": true,
9
- "emitDecoratorMetadata": false,
10
- "esModuleInterop": true,
11
- "experimentalDecorators": false,
12
- "forceConsistentCasingInFileNames": true,
13
6
  "importHelpers": true,
14
- "incremental": true,
15
7
  "isolatedModules": true,
16
8
  "lib": ["es2022"],
17
9
  "module": "NodeNext",
@@ -21,14 +13,8 @@
21
13
  "noImplicitOverride": true,
22
14
  "noImplicitReturns": true,
23
15
  "noUnusedLocals": true,
24
- "pretty": true,
25
- "removeComments": false,
26
- "resolveJsonModule": false,
27
- "skipDefaultLibCheck": false,
28
16
  "skipLibCheck": true,
29
- "sourceMap": false,
30
17
  "strict": true,
31
- "target": "es2022",
32
- "verbatimModuleSyntax": false
18
+ "target": "es2022"
33
19
  }
34
20
  }
@@ -64,9 +64,24 @@ async function initGeneratorInternal(tree, schema) {
64
64
  { targetName: 'tsc-typecheck' },
65
65
  ],
66
66
  build: [
67
- { targetName: 'build', configName: 'tsconfig.lib.json' },
68
- { targetName: 'tsc:build', configName: 'tsconfig.lib.json' },
69
- { targetName: 'tsc-build', configName: 'tsconfig.lib.json' },
67
+ {
68
+ targetName: 'build',
69
+ configName: 'tsconfig.lib.json',
70
+ buildDepsName: 'build-deps',
71
+ watchDepsName: 'watch-deps',
72
+ },
73
+ {
74
+ targetName: 'tsc:build',
75
+ configName: 'tsconfig.lib.json',
76
+ buildDepsName: 'tsc:build-deps',
77
+ watchDepsName: 'tsc:watch-deps',
78
+ },
79
+ {
80
+ targetName: 'tsc-build',
81
+ configName: 'tsconfig.lib.json',
82
+ buildDepsName: 'tsc-build-deps',
83
+ watchDepsName: 'tsc-watch-deps',
84
+ },
70
85
  ],
71
86
  }, schema.updatePackageScripts);
72
87
  }
@@ -1,4 +1,4 @@
1
- import { <%= propertyName %> } from './<%= fileName %>';
1
+ import { <%= propertyName %> } from './<%= fileNameImport %>';
2
2
 
3
3
  describe('<%= propertyName %>', () => {
4
4
  it('should work', () => {
@@ -11,18 +11,18 @@ const log_show_project_command_1 = require("@nx/devkit/src/utils/log-show-projec
11
11
  const find_matching_projects_1 = require("nx/src/utils/find-matching-projects");
12
12
  const path_1 = require("path");
13
13
  const generator_prompts_1 = require("../../utils/generator-prompts");
14
- const package_manager_workspaces_1 = require("../../utils/package-manager-workspaces");
14
+ const update_package_json_1 = require("../../utils/package-json/update-package-json");
15
15
  const add_swc_config_1 = require("../../utils/swc/add-swc-config");
16
16
  const add_swc_dependencies_1 = require("../../utils/swc/add-swc-dependencies");
17
17
  const configuration_1 = require("../../utils/typescript/configuration");
18
18
  const create_ts_config_1 = require("../../utils/typescript/create-ts-config");
19
+ const ensure_typescript_1 = require("../../utils/typescript/ensure-typescript");
19
20
  const plugin_1 = require("../../utils/typescript/plugin");
20
21
  const ts_config_1 = require("../../utils/typescript/ts-config");
21
22
  const ts_solution_setup_1 = require("../../utils/typescript/ts-solution-setup");
22
23
  const versions_1 = require("../../utils/versions");
23
24
  const init_1 = require("../init/init");
24
25
  const generator_1 = require("../setup-verdaccio/generator");
25
- const ensure_typescript_1 = require("../../utils/typescript/ensure-typescript");
26
26
  const defaultOutputDirectory = 'dist';
27
27
  async function libraryGenerator(tree, schema) {
28
28
  return await libraryGeneratorInternal(tree, {
@@ -55,7 +55,7 @@ async function libraryGeneratorInternal(tree, schema) {
55
55
  await configurationGenerator(tree, {
56
56
  project: options.name,
57
57
  compiler: 'swc',
58
- format: ['cjs', 'esm'],
58
+ format: options.isUsingTsSolutionConfig ? ['esm'] : ['cjs', 'esm'],
59
59
  });
60
60
  }
61
61
  if (options.bundler === 'vite') {
@@ -101,6 +101,7 @@ async function libraryGeneratorInternal(tree, schema) {
101
101
  testEnvironment: options.testEnvironment,
102
102
  runtimeTsconfigFileName: 'tsconfig.lib.json',
103
103
  compiler: options.compiler === 'swc' ? 'swc' : 'babel',
104
+ addPlugin: options.addPlugin,
104
105
  });
105
106
  tasks.push(vitestTask);
106
107
  createOrEditViteConfig(tree, {
@@ -120,17 +121,21 @@ async function libraryGeneratorInternal(tree, schema) {
120
121
  // add project reference to the runtime tsconfig.lib.json file
121
122
  json.references ??= [];
122
123
  json.references.push({ path: './tsconfig.lib.json' });
124
+ if (options.isUsingTsSolutionConfig && options.bundler === 'rollup') {
125
+ json.compilerOptions.module = 'esnext';
126
+ json.compilerOptions.moduleResolution = 'bundler';
127
+ }
123
128
  return json;
124
129
  });
125
130
  }
131
+ // If we are using the new TS solution
132
+ // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project
133
+ if (options.isUsingTsSolutionConfig) {
134
+ (0, ts_solution_setup_1.addProjectToTsSolutionWorkspace)(tree, options.projectRoot);
135
+ }
126
136
  if (!options.skipFormat) {
127
137
  await (0, devkit_1.formatFiles)(tree);
128
138
  }
129
- if (!options.skipWorkspacesWarning &&
130
- options.isUsingTsSolutionConfig &&
131
- options.projectPackageManagerWorkspaceState !== 'included') {
132
- tasks.push((0, package_manager_workspaces_1.getProjectPackageManagerWorkspaceStateWarningTask)(options.projectPackageManagerWorkspaceState, tree.root));
133
- }
134
139
  if (options.publishable) {
135
140
  tasks.push(() => {
136
141
  logNxReleaseDocsInfo();
@@ -138,7 +143,7 @@ async function libraryGeneratorInternal(tree, schema) {
138
143
  }
139
144
  // Always run install to link packages.
140
145
  if (options.isUsingTsSolutionConfig) {
141
- tasks.push(() => (0, devkit_1.installPackagesTask)(tree));
146
+ tasks.push(() => (0, devkit_1.installPackagesTask)(tree, true));
142
147
  }
143
148
  tasks.push(() => {
144
149
  (0, log_show_project_command_1.logShowProjectCommand)(options.name);
@@ -175,15 +180,13 @@ async function configureProject(tree, options) {
175
180
  tsConfig: `${options.projectRoot}/tsconfig.lib.json`,
176
181
  },
177
182
  };
178
- if (options.bundler === 'esbuild') {
179
- projectConfiguration.targets.build.options.format = ['cjs'];
180
- }
181
183
  if (options.bundler === 'swc' &&
182
184
  (options.skipTypeCheck || options.isUsingTsSolutionConfig)) {
183
185
  projectConfiguration.targets.build.options.skipTypeCheck = true;
184
186
  }
185
187
  if (options.isUsingTsSolutionConfig) {
186
188
  if (options.bundler === 'esbuild') {
189
+ projectConfiguration.targets.build.options.format = ['esm'];
187
190
  projectConfiguration.targets.build.options.declarationRootDir = `${options.projectRoot}/src`;
188
191
  }
189
192
  else if (options.bundler === 'swc') {
@@ -193,6 +196,7 @@ async function configureProject(tree, options) {
193
196
  else {
194
197
  projectConfiguration.targets.build.options.assets = [];
195
198
  if (options.bundler === 'esbuild') {
199
+ projectConfiguration.targets.build.options.format = ['cjs'];
196
200
  projectConfiguration.targets.build.options.generatePackageJson = true;
197
201
  }
198
202
  if (!options.minimal) {
@@ -308,7 +312,7 @@ async function addLint(tree, options) {
308
312
  }
309
313
  else if (options.bundler === 'rollup') {
310
314
  ruleOptions.ignoredFiles ??= [];
311
- ruleOptions.ignoredFiles.push('{projectRoot}/rollup.config.{js,ts,mjs,mts}');
315
+ ruleOptions.ignoredFiles.push('{projectRoot}/rollup.config.{js,ts,mjs,mts,cjs,cts}');
312
316
  o.rules['@nx/dependency-checks'] = [ruleSeverity, ruleOptions];
313
317
  }
314
318
  else if (options.bundler === 'esbuild') {
@@ -332,7 +336,7 @@ function createFiles(tree, options) {
332
336
  const { className, name, propertyName } = (0, devkit_1.names)(options.projectNames.projectFileName);
333
337
  createProjectTsConfigs(tree, options);
334
338
  let fileNameImport = options.fileName;
335
- if (options.bundler === 'vite') {
339
+ if (options.bundler === 'vite' || options.isUsingTsSolutionConfig) {
336
340
  const tsConfig = (0, ts_config_1.readTsConfigFromTree)(tree, (0, path_1.join)(options.projectRoot, 'tsconfig.lib.json'));
337
341
  const ts = (0, ensure_typescript_1.ensureTypescript)();
338
342
  if (tsConfig.options.moduleResolution === ts.ModuleResolutionKind.Node16 ||
@@ -376,7 +380,9 @@ function createFiles(tree, options) {
376
380
  });
377
381
  }
378
382
  if (options.bundler === 'swc' || options.bundler === 'rollup') {
379
- (0, add_swc_config_1.addSwcConfig)(tree, options.projectRoot, options.bundler === 'swc' ? 'commonjs' : 'es6');
383
+ (0, add_swc_config_1.addSwcConfig)(tree, options.projectRoot, options.bundler === 'swc' && !options.isUsingTsSolutionConfig
384
+ ? 'commonjs'
385
+ : 'es6');
380
386
  }
381
387
  else if (options.includeBabelRc) {
382
388
  addBabelRc(tree, options);
@@ -405,7 +411,7 @@ function createFiles(tree, options) {
405
411
  // https://docs.npmjs.com/cli/v10/configuring-npm/package-json#files
406
412
  json.files = ['dist', '!**/*.tsbuildinfo'];
407
413
  }
408
- return {
414
+ const updatedPackageJson = {
409
415
  ...json,
410
416
  dependencies: {
411
417
  ...json.dependencies,
@@ -413,10 +419,25 @@ function createFiles(tree, options) {
413
419
  },
414
420
  ...determineEntryFields(options),
415
421
  };
422
+ if (options.bundler === 'none') {
423
+ updatedPackageJson.type = 'module';
424
+ }
425
+ else if (options.bundler !== 'vite' && options.bundler !== 'rollup') {
426
+ return (0, update_package_json_1.getUpdatedPackageJsonContent)(updatedPackageJson, {
427
+ main: (0, path_1.join)(options.projectRoot, 'src/index.ts'),
428
+ outputPath: (0, devkit_1.joinPathFragments)(options.projectRoot, 'dist'),
429
+ projectRoot: options.projectRoot,
430
+ rootDir: (0, path_1.join)(options.projectRoot, 'src'),
431
+ generateExportsField: true,
432
+ packageJsonPath,
433
+ format: ['esm'],
434
+ });
435
+ }
436
+ return updatedPackageJson;
416
437
  });
417
438
  }
418
439
  else {
419
- const packageJson = {
440
+ let packageJson = {
420
441
  name: options.importPath,
421
442
  version: '0.0.1',
422
443
  dependencies: determineDependencies(options),
@@ -430,6 +451,22 @@ function createFiles(tree, options) {
430
451
  // https://docs.npmjs.com/cli/v10/configuring-npm/package-json#files
431
452
  packageJson.files = ['dist', '!**/*.tsbuildinfo'];
432
453
  }
454
+ if (options.isUsingTsSolutionConfig) {
455
+ if (options.bundler === 'none') {
456
+ packageJson.type = 'module';
457
+ }
458
+ else if (options.bundler !== 'vite' && options.bundler !== 'rollup') {
459
+ packageJson = (0, update_package_json_1.getUpdatedPackageJsonContent)(packageJson, {
460
+ main: (0, path_1.join)(options.projectRoot, 'src/index.ts'),
461
+ outputPath: (0, devkit_1.joinPathFragments)(options.projectRoot, 'dist'),
462
+ projectRoot: options.projectRoot,
463
+ rootDir: (0, path_1.join)(options.projectRoot, 'src'),
464
+ generateExportsField: true,
465
+ packageJsonPath,
466
+ format: ['esm'],
467
+ });
468
+ }
469
+ }
433
470
  (0, devkit_1.writeJson)(tree, packageJsonPath, packageJson);
434
471
  }
435
472
  if (options.config === 'npm-scripts') {
@@ -577,7 +614,6 @@ async function normalizeOptions(tree, options) {
577
614
  ? options.tags.split(',').map((s) => s.trim())
578
615
  : [];
579
616
  options.minimal ??= false;
580
- const projectPackageManagerWorkspaceState = (0, package_manager_workspaces_1.getProjectPackageManagerWorkspaceState)(tree, projectRoot);
581
617
  // We default to generate a project.json file if the new setup is not being used
582
618
  options.useProjectJson ??= !isUsingTsSolutionConfig;
583
619
  return {
@@ -590,7 +626,6 @@ async function normalizeOptions(tree, options) {
590
626
  importPath,
591
627
  hasPlugin,
592
628
  isUsingTsSolutionConfig,
593
- projectPackageManagerWorkspaceState,
594
629
  };
595
630
  }
596
631
  function addProjectDependencies(tree, options) {
@@ -774,75 +809,87 @@ function determineDependencies(options) {
774
809
  function determineEntryFields(options) {
775
810
  switch (options.bundler) {
776
811
  case 'tsc':
777
- return {
778
- type: 'commonjs',
779
- main: options.isUsingTsSolutionConfig
780
- ? './dist/index.js'
781
- : './src/index.js',
782
- typings: options.isUsingTsSolutionConfig
783
- ? './dist/index.d.ts'
784
- : './src/index.d.ts',
785
- };
786
812
  case 'swc':
787
- return {
788
- type: 'commonjs',
789
- main: options.isUsingTsSolutionConfig
790
- ? './dist/index.js'
791
- : './src/index.js',
792
- typings: options.isUsingTsSolutionConfig
793
- ? './dist/index.d.ts'
794
- : './src/index.d.ts',
795
- };
813
+ if (options.isUsingTsSolutionConfig) {
814
+ return {
815
+ type: 'module',
816
+ main: './dist/index.js',
817
+ types: './dist/index.d.ts',
818
+ };
819
+ }
820
+ else {
821
+ return {
822
+ type: 'commonjs',
823
+ main: './src/index.js',
824
+ types: './src/index.d.ts',
825
+ };
826
+ }
796
827
  case 'rollup':
797
- return {
798
- // Since we're publishing both formats, skip the type field.
799
- // Bundlers or Node will determine the entry point to use.
800
- main: options.isUsingTsSolutionConfig
801
- ? './dist/index.cjs'
802
- : './index.cjs',
803
- module: options.isUsingTsSolutionConfig
804
- ? './dist/index.js'
805
- : './index.js',
806
- };
828
+ if (options.isUsingTsSolutionConfig) {
829
+ // the rollup configuration generator already handles this
830
+ return {};
831
+ }
832
+ else {
833
+ return {
834
+ // Since we're publishing both formats, skip the type field.
835
+ // Bundlers or Node will determine the entry point to use.
836
+ main: './index.cjs',
837
+ module: './index.js',
838
+ };
839
+ }
807
840
  case 'vite':
808
- return {
809
- type: 'module',
810
- main: options.isUsingTsSolutionConfig
811
- ? './dist/index.js'
812
- : './index.js',
813
- typings: options.isUsingTsSolutionConfig
814
- ? './dist/index.d.ts'
815
- : './index.d.ts',
816
- };
841
+ if (options.isUsingTsSolutionConfig) {
842
+ // the vite configuration generator already handle this
843
+ return {};
844
+ }
845
+ else {
846
+ return {
847
+ type: 'module',
848
+ main: './index.js',
849
+ types: './index.d.ts',
850
+ };
851
+ }
817
852
  case 'esbuild':
818
- // For libraries intended for Node, use CJS.
819
- return {
820
- type: 'commonjs',
821
- main: options.isUsingTsSolutionConfig
822
- ? './dist/index.cjs'
823
- : './index.cjs',
824
- typings: options.isUsingTsSolutionConfig
825
- ? './dist/index.d.ts'
826
- : './index.d.ts',
827
- };
828
- default: {
853
+ if (options.isUsingTsSolutionConfig) {
854
+ return {
855
+ type: 'module',
856
+ main: './dist/index.js',
857
+ types: './dist/index.d.ts',
858
+ };
859
+ }
860
+ else {
861
+ return {
862
+ type: 'commonjs',
863
+ main: './index.cjs',
864
+ types: './index.d.ts',
865
+ };
866
+ }
867
+ case 'none': {
868
+ if (options.isUsingTsSolutionConfig) {
869
+ return {
870
+ main: options.js ? './src/index.js' : './src/index.ts',
871
+ types: options.js ? './src/index.js' : './src/index.ts',
872
+ exports: {
873
+ '.': options.js
874
+ ? './src/index.js'
875
+ : {
876
+ types: './src/index.ts',
877
+ import: './src/index.ts',
878
+ default: './src/index.ts',
879
+ },
880
+ './package.json': './package.json',
881
+ },
882
+ };
883
+ }
829
884
  return {
830
885
  // Safest option is to not set a type field.
831
886
  // Allow the user to decide which module format their library is using
832
887
  type: undefined,
833
- // For non-buildable libraries, point to source so we can still use them in apps via bundlers like Vite.
834
- main: options.isUsingTsSolutionConfig
835
- ? options.js
836
- ? './src/index.js'
837
- : './src/index.ts'
838
- : undefined,
839
- types: options.isUsingTsSolutionConfig
840
- ? options.js
841
- ? './src/index.js'
842
- : './src/index.ts'
843
- : undefined,
844
888
  };
845
889
  }
890
+ default: {
891
+ return {};
892
+ }
846
893
  }
847
894
  }
848
895
  function projectsConfigMatchesProject(projectsConfig, project) {
@@ -33,7 +33,6 @@ export interface LibraryGeneratorSchema {
33
33
  simpleName?: boolean;
34
34
  addPlugin?: boolean;
35
35
  useProjectJson?: boolean;
36
- skipWorkspacesWarning?: boolean;
37
36
  useTscExecutor?: boolean;
38
37
  }
39
38
 
@@ -47,5 +46,4 @@ export interface NormalizedLibraryGeneratorOptions
47
46
  importPath?: string;
48
47
  hasPlugin: boolean;
49
48
  isUsingTsSolutionConfig: boolean;
50
- projectPackageManagerWorkspaceState: ProjectPackageManagerWorkspaceState;
51
49
  }
@@ -98,7 +98,7 @@ async function setupBuildGenerator(tree, options) {
98
98
  project: options.project,
99
99
  skipFormat: true,
100
100
  skipValidation: true,
101
- format: ['cjs'],
101
+ format: isTsSolutionSetup ? ['esm'] : ['cjs'],
102
102
  });
103
103
  tasks.push(task);
104
104
  break;
@@ -111,7 +111,7 @@ async function setupBuildGenerator(tree, options) {
111
111
  tsConfig: tsConfigFile,
112
112
  project: options.project,
113
113
  compiler: 'tsc',
114
- format: ['cjs', 'esm'],
114
+ format: isTsSolutionSetup ? ['esm'] : ['cjs', 'esm'],
115
115
  addPlugin,
116
116
  skipFormat: true,
117
117
  skipValidation: true,
@@ -165,7 +165,7 @@ async function setupBuildGenerator(tree, options) {
165
165
  }
166
166
  (0, devkit_1.updateProjectConfiguration)(tree, options.project, project);
167
167
  tasks.push((0, add_swc_dependencies_1.addSwcDependencies)(tree));
168
- (0, add_swc_config_1.addSwcConfig)(tree, project.root, 'commonjs');
168
+ (0, add_swc_config_1.addSwcConfig)(tree, project.root, isTsSolutionSetup ? 'es6' : 'commonjs');
169
169
  if (isTsSolutionSetup) {
170
170
  updatePackageJsonForSwc(tree, options, project);
171
171
  }
@@ -199,8 +199,9 @@ function updateTsConfigReferences(tree, tsSysFromTree, tsconfigInfoCaches, tsCon
199
199
  }
200
200
  let hasChanges = false;
201
201
  for (const dep of dependencies) {
202
- // Ensure the project reference for the target is set
203
- let referencePath = dep.data.root;
202
+ // Ensure the project reference for the target is set if we can find the
203
+ // relevant tsconfig file
204
+ let referencePath;
204
205
  if (runtimeTsConfigFileName) {
205
206
  const runtimeTsConfigPath = (0, devkit_1.joinPathFragments)(dep.data.root, runtimeTsConfigFileName);
206
207
  if (tsconfigExists(tree, tsconfigInfoCaches, runtimeTsConfigPath)) {
@@ -233,6 +234,14 @@ function updateTsConfigReferences(tree, tsSysFromTree, tsconfigInfoCaches, tsCon
233
234
  continue;
234
235
  }
235
236
  }
237
+ if (!referencePath) {
238
+ if (tsconfigExists(tree, tsconfigInfoCaches, (0, devkit_1.joinPathFragments)(dep.data.root, 'tsconfig.json'))) {
239
+ referencePath = dep.data.root;
240
+ }
241
+ else {
242
+ continue;
243
+ }
244
+ }
236
245
  const relativePathToTargetRoot = (0, posix_1.relative)(projectRoot, referencePath);
237
246
  if (!newReferencesSet.has(relativePathToTargetRoot)) {
238
247
  newReferencesSet.add(relativePathToTargetRoot);
@@ -270,7 +279,7 @@ function collectProjectDependencies(tree, projectName, projectGraph, collectedDe
270
279
  .some((d) => d.name === targetProjectNode.name)) {
271
280
  collectedDependencies.get(projectName).push(targetProjectNode);
272
281
  }
273
- if (process.env.NX_DISABLE_TS_SYNC_TRANSITIVE_DEPENDENCIES === 'true') {
282
+ if (process.env.NX_ENABLE_TS_SYNC_TRANSITIVE_DEPENDENCIES !== 'true') {
274
283
  continue;
275
284
  }
276
285
  // Recursively get the dependencies of the target project
@@ -6,6 +6,8 @@ export interface TscPluginOptions {
6
6
  build?: boolean | {
7
7
  targetName?: string;
8
8
  configName?: string;
9
+ buildDepsName?: string;
10
+ watchDepsName?: string;
9
11
  };
10
12
  }
11
13
  /**
@@ -11,6 +11,7 @@ const file_hasher_1 = require("nx/src/hasher/file-hasher");
11
11
  const lock_file_1 = require("nx/src/plugins/js/lock-file/lock-file");
12
12
  const cache_directory_1 = require("nx/src/utils/cache-directory");
13
13
  const ts_config_1 = require("../../utils/typescript/ts-config");
14
+ const util_1 = require("./util");
14
15
  const pmc = (0, devkit_1.getPackageManagerCommand)();
15
16
  function readTargetsCache(cachePath) {
16
17
  return process.env.NX_CACHE_PROJECT_GRAPH !== 'false' && (0, node_fs_1.existsSync)(cachePath)
@@ -94,6 +95,10 @@ async function createNodesInternal(configFilePath, options, context, lockFileNam
94
95
  const extendedConfigFiles = getExtendedConfigFiles(fullConfigPath, tsConfig);
95
96
  const internalReferencedFiles = resolveInternalProjectReferences(tsConfig, context.workspaceRoot, projectRoot);
96
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;
97
102
  const nodeHash = (0, file_hasher_1.hashArray)([
98
103
  ...[
99
104
  fullConfigPath,
@@ -103,6 +108,7 @@ async function createNodesInternal(configFilePath, options, context, lockFileNam
103
108
  (0, node_path_1.join)(context.workspaceRoot, lockFileName),
104
109
  ].map(file_hasher_1.hashFile),
105
110
  (0, file_hasher_1.hashObject)(options),
111
+ ...(packageJson ? [(0, file_hasher_1.hashObject)(packageJson)] : []),
106
112
  ]);
107
113
  const cacheKey = `${nodeHash}_${configFilePath}`;
108
114
  targetsCache[cacheKey] ??= buildTscTargets(fullConfigPath, projectRoot, options, context);
@@ -163,7 +169,7 @@ function buildTscTargets(configFilePath, projectRoot, options, context) {
163
169
  // Build target
164
170
  if (options.build &&
165
171
  (0, node_path_1.basename)(configFilePath) === options.build.configName &&
166
- isValidPackageJsonBuildConfig(tsConfig, context.workspaceRoot, projectRoot, configFilePath)) {
172
+ isValidPackageJsonBuildConfig(tsConfig, context.workspaceRoot, projectRoot)) {
167
173
  internalProjectReferences ??= resolveInternalProjectReferences(tsConfig, context.workspaceRoot, projectRoot);
168
174
  const targetName = options.build.targetName;
169
175
  targets[targetName] = {
@@ -185,6 +191,10 @@ function buildTscTargets(configFilePath, projectRoot, options, context) {
185
191
  },
186
192
  },
187
193
  };
194
+ (0, util_1.addBuildAndWatchDepsTargets)(context.workspaceRoot, projectRoot, targets, {
195
+ buildDepsTargetName: options.build.buildDepsName,
196
+ watchDepsTargetName: options.build.watchDepsName,
197
+ }, pmc);
188
198
  }
189
199
  return { targets };
190
200
  }
@@ -230,6 +240,9 @@ function getInputs(namedInputs, configFilePath, tsConfig, internalProjectReferen
230
240
  });
231
241
  const inputs = [];
232
242
  if (includePaths.size) {
243
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(workspaceRoot, projectRoot, 'package.json'))) {
244
+ inputs.push('{projectRoot}/package.json');
245
+ }
233
246
  inputs.push(...Array.from(configFiles).map((p) => pathToInputOrOutput(p, workspaceRoot, projectRoot)), ...Array.from(includePaths).map((p) => pathToInputOrOutput((0, devkit_1.joinPathFragments)(projectRoot, p), workspaceRoot, projectRoot)));
234
247
  }
235
248
  else {
@@ -310,49 +323,45 @@ function getOutputs(configFilePath, tsConfig, internalProjectReferences, workspa
310
323
  return Array.from(outputs);
311
324
  }
312
325
  /**
313
- * Checks whether a `package.json` file has a valid build configuration by ensuring
314
- * that the `main`, `module`, or `exports` do not include paths from the `rootDir`.
315
- * Or if `outFile` is defined, it should not be within the `rootDir`.
326
+ * Validates the build configuration of a `package.json` file by ensuring that paths in the `exports`, `module`,
327
+ * and `main` fields reference valid output paths within the `outDir` defined in the TypeScript configuration.
328
+ * Priority is given to the `exports` field, specifically the `.` export if defined. If `exports` is not defined,
329
+ * the function falls back to validating `main` and `module` fields. If `outFile` is specified, it validates that the file
330
+ * is located within the output directory.
331
+ * If no `package.json` file exists, it assumes the configuration is valid.
316
332
  *
317
333
  * @param tsConfig The TypeScript configuration object.
318
334
  * @param workspaceRoot The workspace root path.
319
335
  * @param projectRoot The project root path.
320
- * @param tsConfigPath The path to the TypeScript configuration file.
321
336
  * @returns `true` if the package has a valid build configuration; otherwise, `false`.
322
337
  */
323
- function isValidPackageJsonBuildConfig(tsConfig, workspaceRoot, projectRoot, tsConfigPath) {
324
- if (!(0, node_fs_1.existsSync)((0, devkit_1.joinPathFragments)(projectRoot, 'package.json'))) {
338
+ function isValidPackageJsonBuildConfig(tsConfig, workspaceRoot, projectRoot) {
339
+ const packageJsonPath = (0, node_path_1.join)(workspaceRoot, projectRoot, 'package.json');
340
+ if (!(0, node_fs_1.existsSync)(packageJsonPath)) {
325
341
  // If the package.json file does not exist.
326
342
  // Assume it's valid because it would be using `project.json` instead.
327
343
  return true;
328
344
  }
329
- const packageJson = (0, devkit_1.readJsonFile)((0, devkit_1.joinPathFragments)(projectRoot, 'package.json'));
330
- const rootDir = tsConfig.options.rootDir ?? 'src/';
331
- if (!tsConfig.options.rootDir) {
332
- console.warn(`The 'rootDir' option is not set in the tsconfig file at ${tsConfigPath}. Assuming 'src/' as the root directory.`);
333
- }
334
- const isPathWithinSrc = (path) => {
335
- const resolvedRootDir = (0, node_path_1.resolve)(workspaceRoot, projectRoot, rootDir);
336
- const pathToCheck = (0, node_path_1.resolve)(workspaceRoot, projectRoot, path);
337
- return pathToCheck.startsWith(resolvedRootDir);
338
- };
339
- // If `outFile` is defined, check the validity of the path.
340
- if (tsConfig.options.outFile) {
341
- if (isPathWithinSrc(tsConfig.options.outFile)) {
342
- return false;
343
- }
344
- }
345
- const buildPaths = ['main', 'module'];
346
- for (const field of buildPaths) {
347
- if (packageJson[field] && isPathWithinSrc(packageJson[field])) {
348
- return false;
345
+ const packageJson = (0, devkit_1.readJsonFile)(packageJsonPath);
346
+ const outDir = tsConfig.options.outFile
347
+ ? (0, node_path_1.dirname)(tsConfig.options.outFile)
348
+ : tsConfig.options.outDir;
349
+ const resolvedOutDir = outDir
350
+ ? (0, node_path_1.resolve)(workspaceRoot, projectRoot, outDir)
351
+ : undefined;
352
+ const isPathSourceFile = (path) => {
353
+ if (resolvedOutDir) {
354
+ const pathToCheck = (0, node_path_1.resolve)(workspaceRoot, projectRoot, path);
355
+ return !pathToCheck.startsWith(resolvedOutDir);
349
356
  }
350
- }
351
- const exports = packageJson?.exports;
357
+ const ext = (0, node_path_1.extname)(path);
358
+ // Check that the file extension is a TS file extension. As the source files are in the same directory as the output files.
359
+ return ['.ts', '.tsx', '.cts', '.mts'].includes(ext);
360
+ };
352
361
  // Checks if the value is a path within the `src` directory.
353
362
  const containsInvalidPath = (value) => {
354
363
  if (typeof value === 'string') {
355
- return isPathWithinSrc(value);
364
+ return isPathSourceFile(value);
356
365
  }
357
366
  else if (typeof value === 'object') {
358
367
  return Object.entries(value).some(([currentKey, subValue]) => {
@@ -361,30 +370,45 @@ function isValidPackageJsonBuildConfig(tsConfig, workspaceRoot, projectRoot, tsC
361
370
  return false;
362
371
  }
363
372
  if (typeof subValue === 'string') {
364
- return isPathWithinSrc(subValue);
373
+ return isPathSourceFile(subValue);
365
374
  }
366
375
  return false;
367
376
  });
368
377
  }
369
378
  return false;
370
379
  };
371
- if (typeof exports === 'string' && isPathWithinSrc(exports)) {
372
- return false;
373
- }
374
- // Check nested exports if `exports` is an object.
375
- if (typeof exports === 'object') {
380
+ const exports = packageJson?.exports;
381
+ // Check the `.` export if `exports` is defined.
382
+ if (exports) {
383
+ if (typeof exports === 'string') {
384
+ return !isPathSourceFile(exports);
385
+ }
386
+ if (typeof exports === 'object' && '.' in exports) {
387
+ return !containsInvalidPath(exports['.']);
388
+ }
389
+ // Check other exports if `.` is not defined or valid.
376
390
  for (const key in exports) {
377
- if (containsInvalidPath(exports[key])) {
391
+ if (key !== '.' && containsInvalidPath(exports[key])) {
378
392
  return false;
379
393
  }
380
394
  }
395
+ return true;
396
+ }
397
+ // If `exports` is not defined, fallback to `main` and `module` fields.
398
+ const buildPaths = ['main', 'module'];
399
+ for (const field of buildPaths) {
400
+ if (packageJson[field] && isPathSourceFile(packageJson[field])) {
401
+ return false;
402
+ }
381
403
  }
382
404
  return true;
383
405
  }
384
406
  function pathToInputOrOutput(path, workspaceRoot, projectRoot) {
385
- const pathRelativeToProjectRoot = (0, devkit_1.normalizePath)((0, node_path_1.relative)(projectRoot, path));
407
+ const fullProjectRoot = (0, node_path_1.resolve)(workspaceRoot, projectRoot);
408
+ const fullPath = (0, node_path_1.resolve)(workspaceRoot, path);
409
+ const pathRelativeToProjectRoot = (0, devkit_1.normalizePath)((0, node_path_1.relative)(fullProjectRoot, fullPath));
386
410
  if (pathRelativeToProjectRoot.startsWith('..')) {
387
- return (0, devkit_1.joinPathFragments)('{workspaceRoot}', (0, node_path_1.relative)(workspaceRoot, path));
411
+ return (0, devkit_1.joinPathFragments)('{workspaceRoot}', (0, node_path_1.relative)(workspaceRoot, fullPath));
388
412
  }
389
413
  return (0, devkit_1.joinPathFragments)('{projectRoot}', pathRelativeToProjectRoot);
390
414
  }
@@ -542,6 +566,8 @@ function normalizePluginOptions(pluginOptions = {}) {
542
566
  let build = {
543
567
  targetName: defaultBuildTargetName,
544
568
  configName: defaultBuildConfigName,
569
+ buildDepsName: 'build-deps',
570
+ watchDepsName: 'watch-deps',
545
571
  };
546
572
  // Build target is not enabled by default
547
573
  if (!pluginOptions.build) {
@@ -551,6 +577,8 @@ function normalizePluginOptions(pluginOptions = {}) {
551
577
  build = {
552
578
  targetName: pluginOptions.build.targetName ?? defaultBuildTargetName,
553
579
  configName: pluginOptions.build.configName ?? defaultBuildConfigName,
580
+ buildDepsName: pluginOptions.build.buildDepsName ?? 'build-deps',
581
+ watchDepsName: pluginOptions.build.watchDepsName ?? 'watch-deps',
554
582
  };
555
583
  }
556
584
  return {
@@ -0,0 +1,9 @@
1
+ import { type TargetConfiguration } from '@nx/devkit';
2
+ import { type PackageManagerCommands } from 'nx/src/utils/package-manager';
3
+ /**
4
+ * Allow uses that use incremental builds to run `nx watch-deps` to continuously build all dependencies.
5
+ */
6
+ export declare function addBuildAndWatchDepsTargets(workspaceRoot: string, projectRoot: string, targets: Record<string, TargetConfiguration>, options: {
7
+ buildDepsTargetName?: string;
8
+ watchDepsTargetName?: string;
9
+ }, pmc: PackageManagerCommands): void;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addBuildAndWatchDepsTargets = addBuildAndWatchDepsTargets;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const node_fs_1 = require("node:fs");
6
+ const path_1 = require("path");
7
+ /**
8
+ * Allow uses that use incremental builds to run `nx watch-deps` to continuously build all dependencies.
9
+ */
10
+ function addBuildAndWatchDepsTargets(workspaceRoot, projectRoot, targets, options, pmc) {
11
+ let projectName;
12
+ const projectJsonPath = (0, path_1.join)(workspaceRoot, projectRoot, 'project.json');
13
+ const packageJsonPath = (0, path_1.join)(workspaceRoot, projectRoot, 'package.json');
14
+ if ((0, node_fs_1.existsSync)(projectJsonPath)) {
15
+ const projectJson = (0, devkit_1.readJsonFile)(projectJsonPath);
16
+ projectName = projectJson.name;
17
+ }
18
+ else if ((0, node_fs_1.existsSync)(packageJsonPath)) {
19
+ const packageJson = (0, devkit_1.readJsonFile)(packageJsonPath);
20
+ projectName = packageJson.nx?.name ?? packageJson.name;
21
+ }
22
+ if (!projectName)
23
+ return;
24
+ if (projectName) {
25
+ const buildDepsTargetName = options.buildDepsTargetName ?? 'build-deps';
26
+ targets[buildDepsTargetName] = {
27
+ dependsOn: ['^build'],
28
+ };
29
+ targets[options.watchDepsTargetName ?? 'watch-deps'] = {
30
+ dependsOn: [buildDepsTargetName],
31
+ command: `${pmc.exec} nx watch --projects ${projectName} --includeDependentProjects -- ${pmc.exec} nx ${buildDepsTargetName} ${projectName}`,
32
+ };
33
+ }
34
+ }
@@ -181,6 +181,9 @@ function getUpdatedPackageJsonContent(packageJson, options) {
181
181
  }
182
182
  else if (typeof packageJson.exports[exportEntry] === 'object') {
183
183
  packageJson.exports[exportEntry].import ??= filePath;
184
+ if (!hasCjsFormat) {
185
+ packageJson.exports[exportEntry].default ??= filePath;
186
+ }
184
187
  }
185
188
  }
186
189
  }
@@ -52,6 +52,9 @@ function getTypeCheckOptions(normalizedOptions) {
52
52
  typeCheckOptions.incremental = true;
53
53
  typeCheckOptions.cacheDir = devkit_1.cacheDir;
54
54
  }
55
+ if (normalizedOptions.isTsSolutionSetup && normalizedOptions.skipTypeCheck) {
56
+ typeCheckOptions.ignoreDiagnostics = true;
57
+ }
55
58
  return typeCheckOptions;
56
59
  }
57
60
  async function compileSwc(context, normalizedOptions, postCompilationCallback) {
@@ -59,16 +62,25 @@ async function compileSwc(context, normalizedOptions, postCompilationCallback) {
59
62
  if (normalizedOptions.clean) {
60
63
  (0, node_fs_1.rmSync)(normalizedOptions.outputPath, { recursive: true, force: true });
61
64
  }
62
- const swcCmdLog = (0, node_child_process_1.execSync)(getSwcCmd(normalizedOptions), {
63
- encoding: 'utf8',
64
- cwd: normalizedOptions.swcCliOptions.swcCwd,
65
- windowsHide: false,
66
- });
67
- devkit_1.logger.log(swcCmdLog.replace(/\n/, ''));
68
- const isCompileSuccess = swcCmdLog.includes('Successfully compiled');
69
- if (normalizedOptions.skipTypeCheck || normalizedOptions.isTsSolutionSetup) {
65
+ try {
66
+ const swcCmdLog = (0, node_child_process_1.execSync)(getSwcCmd(normalizedOptions), {
67
+ encoding: 'utf8',
68
+ cwd: normalizedOptions.swcCliOptions.swcCwd,
69
+ windowsHide: false,
70
+ stdio: 'pipe',
71
+ });
72
+ devkit_1.logger.log(swcCmdLog.replace(/\n/, ''));
73
+ }
74
+ catch (error) {
75
+ devkit_1.logger.error('SWC compilation failed');
76
+ if (error.stderr) {
77
+ devkit_1.logger.error(error.stderr.toString());
78
+ }
79
+ return { success: false };
80
+ }
81
+ if (normalizedOptions.skipTypeCheck && !normalizedOptions.isTsSolutionSetup) {
70
82
  await postCompilationCallback();
71
- return { success: isCompileSuccess };
83
+ return { success: true };
72
84
  }
73
85
  const { errors, warnings } = await (0, run_type_check_1.runTypeCheck)(getTypeCheckOptions(normalizedOptions));
74
86
  const hasErrors = errors.length > 0;
@@ -78,7 +90,7 @@ async function compileSwc(context, normalizedOptions, postCompilationCallback) {
78
90
  }
79
91
  await postCompilationCallback();
80
92
  return {
81
- success: !hasErrors && isCompileSuccess,
93
+ success: !hasErrors,
82
94
  outfile: normalizedOptions.mainOutputPath,
83
95
  };
84
96
  }
@@ -14,6 +14,7 @@ interface BaseTypeCheckOptions {
14
14
  incremental?: boolean;
15
15
  rootDir?: string;
16
16
  projectRoot?: string;
17
+ ignoreDiagnostics?: boolean;
17
18
  }
18
19
  type Mode = NoEmitMode | EmitDeclarationOnlyMode;
19
20
  interface NoEmitMode {
@@ -18,7 +18,9 @@ async function runTypeCheckWatch(options, callback) {
18
18
  };
19
19
  const watchProgram = ts.createWatchProgram(host);
20
20
  const program = watchProgram.getProgram().getProgram();
21
- const diagnostics = ts.getPreEmitDiagnostics(program);
21
+ const diagnostics = options.ignoreDiagnostics
22
+ ? []
23
+ : ts.getPreEmitDiagnostics(program);
22
24
  return {
23
25
  close: watchProgram.close.bind(watchProgram),
24
26
  preEmitErrors: diagnostics
@@ -48,9 +50,9 @@ async function runTypeCheck(options) {
48
50
  program = ts.createProgram(config.fileNames, compilerOptions);
49
51
  }
50
52
  const result = program.emit();
51
- const allDiagnostics = ts
52
- .getPreEmitDiagnostics(program)
53
- .concat(result.diagnostics);
53
+ const allDiagnostics = options.ignoreDiagnostics
54
+ ? []
55
+ : ts.getPreEmitDiagnostics(program).concat(result.diagnostics);
54
56
  return getTypeCheckResult(ts, allDiagnostics, workspaceRoot, config.fileNames.length, program.getSourceFiles().length, incremental);
55
57
  }
56
58
  async function setupTypeScript(options) {
@@ -7,9 +7,9 @@ exports.findRuntimeTsConfigName = findRuntimeTsConfigName;
7
7
  exports.updateTsconfigFiles = updateTsconfigFiles;
8
8
  exports.addProjectToTsSolutionWorkspace = addProjectToTsSolutionWorkspace;
9
9
  const devkit_1 = require("@nx/devkit");
10
+ const posix_1 = require("node:path/posix");
10
11
  const tree_1 = require("nx/src/generators/tree");
11
12
  const package_manager_workspaces_1 = require("../package-manager-workspaces");
12
- const posix_1 = require("node:path/posix");
13
13
  function isUsingTypeScriptPlugin(tree) {
14
14
  const nxJson = (0, devkit_1.readNxJson)(tree);
15
15
  return (nxJson?.plugins?.some((p) => typeof p === 'string'
@@ -44,7 +44,7 @@ function isWorkspaceSetupWithTsSolution(tree) {
44
44
  const baseTsconfigJson = (0, devkit_1.readJson)(tree, 'tsconfig.base.json');
45
45
  if (!baseTsconfigJson.compilerOptions ||
46
46
  !baseTsconfigJson.compilerOptions.composite ||
47
- !baseTsconfigJson.compilerOptions.declaration) {
47
+ baseTsconfigJson.compilerOptions.declaration === false) {
48
48
  return false;
49
49
  }
50
50
  const { compilerOptions, ...rest } = baseTsconfigJson;
@@ -139,17 +139,16 @@ function updateTsconfigFiles(tree, projectRoot, runtimeTsconfigFileName, compile
139
139
  }
140
140
  }
141
141
  function addProjectToTsSolutionWorkspace(tree, projectDir) {
142
- // If dir is "libs/foo" then use "libs/**" so we don't need so many entries in the workspace file.
142
+ // If dir is "libs/foo" then use "libs/*" so we don't need so many entries in the workspace file.
143
+ // If dir is nested like "libs/shared/foo" then we add "libs/shared/*".
143
144
  // If the dir is just "foo" then we have to add it as is.
144
145
  const baseDir = (0, posix_1.dirname)(projectDir);
145
- const pattern = baseDir === '.' ? projectDir : `${baseDir}/**`;
146
+ const pattern = baseDir === '.' ? projectDir : `${baseDir}/*`;
146
147
  if (tree.exists('pnpm-workspace.yaml')) {
147
148
  const { load, dump } = require('@zkochan/js-yaml');
148
149
  const workspaceFile = tree.read('pnpm-workspace.yaml', 'utf-8');
149
- const yamlData = load(workspaceFile);
150
- if (!yamlData?.packages) {
151
- yamlData.packages = [];
152
- }
150
+ const yamlData = load(workspaceFile) ?? {};
151
+ yamlData.packages ??= [];
153
152
  if (!yamlData.packages.includes(pattern)) {
154
153
  yamlData.packages.push(pattern);
155
154
  tree.write('pnpm-workspace.yaml', dump(yamlData, { indent: 2, quotingType: '"', forceQuotes: true }));