@nx/react 20.5.0 → 20.6.0-beta.1

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.
Files changed (48) hide show
  1. package/package.json +6 -6
  2. package/router-plugin.d.ts +1 -0
  3. package/router-plugin.js +5 -0
  4. package/src/generators/application/application.js +48 -9
  5. package/src/generators/application/files/react-router-ssr/common/app/app-nav.tsx__tmpl__ +15 -0
  6. package/src/generators/application/files/react-router-ssr/common/app/entry.client.tsx__tmpl__ +18 -0
  7. package/src/generators/application/files/react-router-ssr/common/app/entry.server.tsx__tmpl__ +74 -0
  8. package/src/generators/application/files/react-router-ssr/common/app/root.tsx__tmpl__ +51 -0
  9. package/src/generators/application/files/react-router-ssr/common/app/routes/about.tsx__tmpl__ +7 -0
  10. package/src/generators/application/files/react-router-ssr/common/app/routes.tsx__tmpl__ +6 -0
  11. package/src/generators/application/files/react-router-ssr/common/public/favicon.ico +0 -0
  12. package/src/generators/application/files/react-router-ssr/common/react-router.config.ts__tmpl__ +5 -0
  13. package/src/generators/application/files/react-router-ssr/common/tests/routes/_index.spec.tsx__tmpl__ +16 -0
  14. package/src/generators/application/files/react-router-ssr/common/tsconfig.app.json__tmpl__ +23 -0
  15. package/src/generators/application/files/react-router-ssr/common/tsconfig.json__tmpl__ +27 -0
  16. package/src/generators/application/files/react-router-ssr/non-root/.gitignore__tmpl__ +5 -0
  17. package/src/generators/application/files/react-router-ssr/non-root/package.json__tmpl__ +24 -0
  18. package/src/generators/application/files/react-router-ssr/nx-welcome/claimed/app/nx-welcome.tsx__tmpl__ +866 -0
  19. package/src/generators/application/files/react-router-ssr/nx-welcome/not-configured/app/nx-welcome.tsx__tmpl__ +866 -0
  20. package/src/generators/application/files/react-router-ssr/nx-welcome/unclaimed/app/nx-welcome.tsx__tmpl__ +864 -0
  21. package/src/generators/application/files/react-router-ssr/ts-solution/package.json__tmpl__ +24 -0
  22. package/src/generators/application/files/react-router-ssr/ts-solution/tsconfig.app.json__tmpl__ +39 -0
  23. package/src/generators/application/lib/add-e2e.js +29 -20
  24. package/src/generators/application/lib/add-linting.d.ts +1 -0
  25. package/src/generators/application/lib/add-linting.js +38 -0
  26. package/src/generators/application/lib/add-project.js +20 -16
  27. package/src/generators/application/lib/add-routing.js +1 -1
  28. package/src/generators/application/lib/bundlers/add-vite.js +15 -6
  29. package/src/generators/application/lib/create-application-files.js +40 -3
  30. package/src/generators/application/lib/install-common-dependencies.js +13 -2
  31. package/src/generators/application/lib/normalize-options.js +4 -0
  32. package/src/generators/application/schema.d.ts +2 -1
  33. package/src/generators/application/schema.json +10 -1
  34. package/src/generators/host/host.js +2 -2
  35. package/src/generators/init/init.js +23 -0
  36. package/src/generators/init/schema.d.ts +2 -0
  37. package/src/generators/library/lib/create-files.js +2 -1
  38. package/src/generators/library/lib/normalize-options.js +1 -0
  39. package/src/generators/library/library.js +14 -11
  40. package/src/generators/library/schema.d.ts +1 -0
  41. package/src/generators/library/schema.json +4 -0
  42. package/src/generators/remote/remote.js +2 -2
  43. package/src/plugins/router-plugin.d.ts +10 -0
  44. package/src/plugins/router-plugin.js +219 -0
  45. package/src/utils/ast-utils.d.ts +1 -1
  46. package/src/utils/ast-utils.js +2 -2
  47. package/src/utils/versions.d.ts +3 -1
  48. package/src/utils/versions.js +6 -3
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "<%= projectName %>",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {},
6
+ "dependencies": {
7
+ "@react-router/node": "<%= reactRouterVersion %>",
8
+ "@react-router/serve": "<%= reactRouterVersion %>",
9
+ "isbot": "<%= reactRouterIsBotVersion %>",
10
+ "react": "<%= reactVersion %>",
11
+ "react-dom": "<%= reactVersion %>",
12
+ "react-router": "<%= reactRouterVersion %>"
13
+ },
14
+ "devDependencies": {
15
+ "@react-router/dev": "<%= reactRouterVersion %>",
16
+ "@types/node": "<%= typesNodeVersion %>",
17
+ "@types/react": "<%= reactVersion %>",
18
+ "@types/react-dom": "<%= reactVersion %>"
19
+ },
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "sideEffects": false,
24
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "extends": "<%= offsetFromRoot %>tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "lib": ["DOM", "DOM.Iterable", "ES2019"],
6
+ "types": ["@react-router/node", "vite/client"],
7
+ "isolatedModules": true,
8
+ "esModuleInterop": true,
9
+ "jsx": "react-jsx",
10
+ "module": "ESNext",
11
+ "moduleResolution": "Bundler",
12
+ "resolveJsonModule": true,
13
+ "target": "ES2022",
14
+ "strict": true,
15
+ "allowJs": true,
16
+ "skipLibCheck": true,
17
+ "forceConsistentCasingInFileNames": true
18
+ },
19
+ "include": [
20
+ "app/**/*.ts",
21
+ "app/**/*.tsx",
22
+ "app/**/*.js",
23
+ "app/**/*.jsx",
24
+ "**/.server/**/*.ts",
25
+ "**/.server/**/*.tsx",
26
+ "**/.client/**/*.ts",
27
+ "**/.client/**/*.tsx"
28
+ ],
29
+ "exclude": [
30
+ "tests/**/*.spec.ts",
31
+ "tests/**/*.test.ts",
32
+ "tests/**/*.spec.tsx",
33
+ "tests/**/*.test.tsx",
34
+ "tests/**/*.spec.js",
35
+ "tests/**/*.test.js",
36
+ "tests/**/*.spec.jsx",
37
+ "tests/**/*.test.jsx"
38
+ ]
39
+ }
@@ -28,8 +28,10 @@ async function addE2e(tree, options) {
28
28
  e2eWebServerInfo = await getWebpackE2EWebServerInfo(tree, options.projectName, (0, devkit_1.joinPathFragments)(options.appProjectRoot, `webpack.config.${options.js ? 'js' : 'ts'}`), options.addPlugin, options.devServerPort ?? 4200);
29
29
  }
30
30
  else if (options.bundler === 'vite') {
31
- const { getViteE2EWebServerInfo } = (0, devkit_1.ensurePackage)('@nx/vite', versions_1.nxVersion);
32
- e2eWebServerInfo = await getViteE2EWebServerInfo(tree, options.projectName, (0, devkit_1.joinPathFragments)(options.appProjectRoot, `vite.config.${options.js ? 'js' : 'ts'}`), options.addPlugin, options.devServerPort ?? 4200);
31
+ const { getViteE2EWebServerInfo, getReactRouterE2EWebServerInfo } = (0, devkit_1.ensurePackage)('@nx/vite', versions_1.nxVersion);
32
+ e2eWebServerInfo = options.useReactRouter
33
+ ? await getReactRouterE2EWebServerInfo(tree, options.projectName, (0, devkit_1.joinPathFragments)(options.appProjectRoot, `vite.config.${options.js ? 'js' : 'ts'}`), options.addPlugin, options.devServerPort ?? 4200)
34
+ : await getViteE2EWebServerInfo(tree, options.projectName, (0, devkit_1.joinPathFragments)(options.appProjectRoot, `vite.config.${options.js ? 'js' : 'ts'}`), options.addPlugin, options.devServerPort ?? 4200);
33
35
  }
34
36
  else if (options.bundler === 'rsbuild') {
35
37
  (0, devkit_1.ensurePackage)('@nx/rsbuild', versions_1.nxVersion);
@@ -46,15 +48,15 @@ async function addE2e(tree, options) {
46
48
  switch (options.e2eTestRunner) {
47
49
  case 'cypress': {
48
50
  const { configurationGenerator } = (0, devkit_1.ensurePackage)('@nx/cypress', versions_1.nxVersion);
49
- if (options.isUsingTsSolutionConfig) {
50
- (0, devkit_1.writeJson)(tree, (0, devkit_1.joinPathFragments)(options.e2eProjectRoot, 'package.json'), {
51
- name: options.e2eProjectName,
52
- version: '0.0.1',
53
- private: true,
54
- nx: {
55
- implicitDependencies: [options.projectName],
56
- },
57
- });
51
+ const packageJson = {
52
+ name: options.e2eProjectName,
53
+ version: '0.0.1',
54
+ private: true,
55
+ };
56
+ if (!options.useProjectJson) {
57
+ packageJson.nx = {
58
+ implicitDependencies: [options.projectName],
59
+ };
58
60
  }
59
61
  else {
60
62
  (0, devkit_1.addProjectConfiguration)(tree, options.e2eProjectName, {
@@ -66,6 +68,9 @@ async function addE2e(tree, options) {
66
68
  tags: [],
67
69
  });
68
70
  }
71
+ if (!options.useProjectJson || options.isUsingTsSolutionConfig) {
72
+ (0, devkit_1.writeJson)(tree, (0, devkit_1.joinPathFragments)(options.e2eProjectRoot, 'package.json'), packageJson);
73
+ }
69
74
  const e2eTask = await configurationGenerator(tree, {
70
75
  ...options,
71
76
  project: options.e2eProjectName,
@@ -114,15 +119,15 @@ async function addE2e(tree, options) {
114
119
  }
115
120
  case 'playwright': {
116
121
  const { configurationGenerator } = (0, devkit_1.ensurePackage)('@nx/playwright', versions_1.nxVersion);
117
- if (options.isUsingTsSolutionConfig) {
118
- (0, devkit_1.writeJson)(tree, (0, devkit_1.joinPathFragments)(options.e2eProjectRoot, 'package.json'), {
119
- name: options.e2eProjectName,
120
- version: '0.0.1',
121
- private: true,
122
- nx: {
123
- implicitDependencies: [options.projectName],
124
- },
125
- });
122
+ const packageJson = {
123
+ name: options.e2eProjectName,
124
+ version: '0.0.1',
125
+ private: true,
126
+ };
127
+ if (!options.useProjectJson) {
128
+ packageJson.nx = {
129
+ implicitDependencies: [options.projectName],
130
+ };
126
131
  }
127
132
  else {
128
133
  (0, devkit_1.addProjectConfiguration)(tree, options.e2eProjectName, {
@@ -131,8 +136,12 @@ async function addE2e(tree, options) {
131
136
  sourceRoot: (0, devkit_1.joinPathFragments)(options.e2eProjectRoot, 'src'),
132
137
  targets: {},
133
138
  implicitDependencies: [options.projectName],
139
+ tags: [],
134
140
  });
135
141
  }
142
+ if (!options.useProjectJson || options.isUsingTsSolutionConfig) {
143
+ (0, devkit_1.writeJson)(tree, (0, devkit_1.joinPathFragments)(options.e2eProjectRoot, 'package.json'), packageJson);
144
+ }
136
145
  const e2eTask = await configurationGenerator(tree, {
137
146
  project: options.e2eProjectName,
138
147
  skipFormat: true,
@@ -1,3 +1,4 @@
1
1
  import { type Tree, type GeneratorCallback } from '@nx/devkit';
2
2
  import { NormalizedSchema } from '../schema';
3
3
  export declare function addLinting(host: Tree, options: NormalizedSchema): Promise<GeneratorCallback>;
4
+ export declare function isEslintInstalled(tree: Tree): boolean;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.addLinting = addLinting;
4
+ exports.isEslintInstalled = isEslintInstalled;
4
5
  const devkit_1 = require("@nx/devkit");
5
6
  const eslint_1 = require("@nx/eslint");
6
7
  const eslint_file_1 = require("@nx/eslint/src/generators/utils/eslint-file");
@@ -8,6 +9,7 @@ const flat_config_1 = require("@nx/eslint/src/utils/flat-config");
8
9
  const devkit_2 = require("@nx/devkit");
9
10
  const add_swc_dependencies_1 = require("@nx/js/src/utils/swc/add-swc-dependencies");
10
11
  const lint_1 = require("../../../utils/lint");
12
+ const versions_1 = require("../../../utils/versions");
11
13
  async function addLinting(host, options) {
12
14
  const tasks = [];
13
15
  if (options.linter === eslint_1.Linter.EsLint) {
@@ -43,6 +45,42 @@ async function addLinting(host, options) {
43
45
  const addSwcTask = (0, add_swc_dependencies_1.addSwcDependencies)(host);
44
46
  tasks.push(installTask, addSwcTask);
45
47
  }
48
+ if (options.useReactRouter) {
49
+ await ignoreReactRouterFilesInEslintConfig(host, options.appProjectRoot);
50
+ }
46
51
  }
47
52
  return (0, devkit_2.runTasksInSerial)(...tasks);
48
53
  }
54
+ async function ignoreReactRouterFilesInEslintConfig(tree, projectRoot) {
55
+ if (!isEslintInstalled(tree)) {
56
+ return;
57
+ }
58
+ (0, devkit_1.ensurePackage)('@nx/eslint', versions_1.nxVersion);
59
+ const { addIgnoresToLintConfig, isEslintConfigSupported } = await Promise.resolve().then(() => require('@nx/eslint/src/generators/utils/eslint-file'));
60
+ if (!isEslintConfigSupported(tree)) {
61
+ return;
62
+ }
63
+ const { useFlatConfig } = await Promise.resolve().then(() => require('@nx/eslint/src/utils/flat-config'));
64
+ const isUsingFlatConfig = useFlatConfig(tree);
65
+ if (!projectRoot && !isUsingFlatConfig) {
66
+ // root eslintrc files ignore all files and the root eslintrc files add
67
+ // back all the project files, so we only add the ignores to the project
68
+ // eslintrc files
69
+ return;
70
+ }
71
+ // for flat config, we update the root config file
72
+ const directory = isUsingFlatConfig ? '' : projectRoot ?? '';
73
+ addIgnoresToLintConfig(tree, directory, ['**/build', '**/.react-router']);
74
+ }
75
+ function isEslintInstalled(tree) {
76
+ try {
77
+ require('eslint');
78
+ return true;
79
+ }
80
+ catch { }
81
+ // it might not be installed yet, but it might be in the tree pending install
82
+ const { devDependencies, dependencies } = tree.exists('package.json')
83
+ ? (0, devkit_1.readJson)(tree, 'package.json')
84
+ : {};
85
+ return !!devDependencies?.['eslint'] || !!dependencies?.['eslint'];
86
+ }
@@ -28,12 +28,12 @@ function addProject(host, options) {
28
28
  serve: createRspackServeTarget(options),
29
29
  };
30
30
  }
31
- if (options.isUsingTsSolutionConfig) {
32
- const packageJson = {
33
- name: options.importPath,
34
- version: '0.0.1',
35
- private: true,
36
- };
31
+ const packageJson = {
32
+ name: options.importPath,
33
+ version: '0.0.1',
34
+ private: true,
35
+ };
36
+ if (!options.useProjectJson) {
37
37
  if (options.projectName !== options.importPath) {
38
38
  packageJson.nx = { name: options.projectName };
39
39
  }
@@ -45,21 +45,25 @@ function addProject(host, options) {
45
45
  packageJson.nx ??= {};
46
46
  packageJson.nx.tags = options.parsedTags;
47
47
  }
48
- (0, devkit_1.writeJson)(host, (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'package.json'), packageJson);
49
48
  }
50
- if (!options.isUsingTsSolutionConfig || options.alwaysGenerateProjectJson) {
49
+ else {
51
50
  (0, devkit_1.addProjectConfiguration)(host, options.projectName, {
52
51
  ...project,
53
52
  });
54
53
  }
55
- else if (options.parsedTags?.length ||
56
- Object.keys(project.targets).length) {
57
- const updatedProject = {
58
- root: options.appProjectRoot,
59
- targets: project.targets,
60
- tags: options.parsedTags?.length ? options.parsedTags : undefined,
61
- };
62
- (0, devkit_1.updateProjectConfiguration)(host, options.projectName, updatedProject);
54
+ if (!options.useProjectJson || options.isUsingTsSolutionConfig) {
55
+ // React Router already adds a package.json to the project root
56
+ if (options.useReactRouter) {
57
+ (0, devkit_1.updateJson)(host, (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'package.json'), (json) => {
58
+ return {
59
+ name: packageJson.name,
60
+ ...json,
61
+ };
62
+ });
63
+ }
64
+ else {
65
+ (0, devkit_1.writeJson)(host, (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'package.json'), packageJson);
66
+ }
63
67
  }
64
68
  }
65
69
  function createRspackBuildTarget(options) {
@@ -20,7 +20,7 @@ function addRouting(host, options) {
20
20
  }, `src/app/${options.fileName}.tsx`));
21
21
  const appFileContent = host.read(appPath, 'utf-8');
22
22
  const appSource = tsModule.createSourceFile(appPath, appFileContent, tsModule.ScriptTarget.Latest, true);
23
- const changes = (0, devkit_1.applyChangesToString)(appFileContent, (0, ast_utils_1.addInitialRoutes)(appPath, appSource));
23
+ const changes = (0, devkit_1.applyChangesToString)(appFileContent, (0, ast_utils_1.addInitialRoutes)(appPath, appSource, options.inSourceTests));
24
24
  host.write(appPath, changes);
25
25
  if (!options.skipPackageJson) {
26
26
  return (0, devkit_1.addDependenciesToPackageJson)(host, { 'react-router-dom': versions_1.reactRouterDomVersion }, {});
@@ -11,6 +11,18 @@ async function setupViteConfiguration(tree, options, tasks) {
11
11
  if (tree.exists((0, devkit_1.joinPathFragments)(options.appProjectRoot, 'src/environments'))) {
12
12
  tree.delete((0, devkit_1.joinPathFragments)(options.appProjectRoot, 'src/environments'));
13
13
  }
14
+ const reactRouterFrameworkConfig = {
15
+ imports: [`import { reactRouter } from '@react-router/dev/vite'`],
16
+ plugins: ['!process.env.VITEST && reactRouter()'],
17
+ };
18
+ const baseReactConfig = {
19
+ imports: [
20
+ options.compiler === 'swc'
21
+ ? `import react from '@vitejs/plugin-react-swc'`
22
+ : `import react from '@vitejs/plugin-react'`,
23
+ ],
24
+ plugins: ['react()'],
25
+ };
14
26
  const viteTask = await viteConfigurationGenerator(tree, {
15
27
  uiFramework: 'react',
16
28
  project: options.projectName,
@@ -29,12 +41,9 @@ async function setupViteConfiguration(tree, options, tasks) {
29
41
  includeVitest: options.unitTestRunner === 'vitest',
30
42
  inSourceTests: options.inSourceTests,
31
43
  rollupOptionsExternal: ["'react'", "'react-dom'", "'react/jsx-runtime'"],
32
- imports: [
33
- options.compiler === 'swc'
34
- ? `import react from '@vitejs/plugin-react-swc'`
35
- : `import react from '@vitejs/plugin-react'`,
36
- ],
37
- plugins: ['react()'],
44
+ ...(options.useReactRouter
45
+ ? reactRouterFrameworkConfig
46
+ : baseReactConfig),
38
47
  }, false);
39
48
  }
40
49
  async function setupVitestConfiguration(tree, options, tasks) {
@@ -12,6 +12,7 @@ const get_app_tests_1 = require("./get-app-tests");
12
12
  const onboarding_1 = require("nx/src/nx-cloud/utilities/onboarding");
13
13
  const has_rspack_plugin_1 = require("../../../utils/has-rspack-plugin");
14
14
  const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup");
15
+ const versions_1 = require("../../../utils/versions");
15
16
  async function createApplicationFiles(host, options) {
16
17
  let styleSolutionSpecificAppFiles;
17
18
  if (options.styledModule && options.style !== 'styled-jsx') {
@@ -41,6 +42,13 @@ async function createApplicationFiles(host, options) {
41
42
  const templateVariables = {
42
43
  ...options.names,
43
44
  ...options,
45
+ typesNodeVersion: versions_1.typesNodeVersion,
46
+ typesReactDomVersion: versions_1.typesReactDomVersion,
47
+ reactRouterVersion: versions_1.reactRouterVersion,
48
+ typesReactVersion: versions_1.typesReactVersion,
49
+ reactDomVersion: versions_1.reactDomVersion,
50
+ reactVersion: versions_1.reactVersion,
51
+ reactRouterIsBotVersion: versions_1.reactRouterIsBotVersion,
44
52
  js: !!options.js, // Ensure this is defined in template
45
53
  tmpl: '',
46
54
  offsetFromRoot: (0, devkit_1.offsetFromRoot)(options.appProjectRoot),
@@ -50,9 +58,12 @@ async function createApplicationFiles(host, options) {
50
58
  hasStyleFile,
51
59
  isUsingTsSolutionSetup: (0, ts_solution_setup_1.isUsingTsSolutionSetup)(host),
52
60
  };
53
- if (options.bundler === 'vite') {
61
+ if (options.bundler === 'vite' && !options.useReactRouter) {
54
62
  (0, devkit_1.generateFiles)(host, (0, path_1.join)(__dirname, '../files/base-vite'), options.appProjectRoot, templateVariables);
55
63
  }
64
+ else if (options.bundler === 'vite' && options.useReactRouter) {
65
+ generateReactRouterFiles(host, options, templateVariables);
66
+ }
56
67
  else if (options.bundler === 'webpack') {
57
68
  (0, devkit_1.generateFiles)(host, (0, path_1.join)(__dirname, '../files/base-webpack'), options.appProjectRoot, {
58
69
  ...templateVariables,
@@ -137,9 +148,12 @@ async function createApplicationFiles(host, options) {
137
148
  const tutorialUrl = options.rootProject
138
149
  ? 'https://nx.dev/getting-started/tutorials/react-standalone-tutorial'
139
150
  : 'https://nx.dev/react-tutorial/1-code-generation?utm_source=nx-project';
140
- (0, devkit_1.generateFiles)(host, (0, path_1.join)(__dirname, '../files/nx-welcome', onBoardingStatus), options.appProjectRoot, { ...templateVariables, connectCloudUrl, tutorialUrl });
151
+ const path = options.useReactRouter
152
+ ? '../files/react-router-ssr/nx-welcome'
153
+ : '../files/nx-welcome';
154
+ (0, devkit_1.generateFiles)(host, (0, path_1.join)(__dirname, path, onBoardingStatus), options.appProjectRoot, { ...templateVariables, connectCloudUrl, tutorialUrl });
141
155
  }
142
- (0, devkit_1.generateFiles)(host, (0, path_1.join)(__dirname, styleSolutionSpecificAppFiles), options.appProjectRoot, templateVariables);
156
+ (0, devkit_1.generateFiles)(host, (0, path_1.join)(__dirname, styleSolutionSpecificAppFiles, options.useReactRouter ? 'src' : ''), options.appProjectRoot, templateVariables);
143
157
  if (options.js) {
144
158
  (0, devkit_1.toJS)(host, {
145
159
  useJsx: options.bundler === 'vite' || options.bundler === 'rspack',
@@ -194,3 +208,26 @@ function createNxRspackPluginOptions(options, rootOffset) {
194
208
  ],
195
209
  };
196
210
  }
211
+ function generateReactRouterFiles(tree, options, templateVariables) {
212
+ (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, '../files/react-router-ssr/common'), options.appProjectRoot, templateVariables);
213
+ if (options.rootProject) {
214
+ const gitignore = tree.read('.gitignore', 'utf-8');
215
+ tree.write('.gitignore', `${gitignore}\n.cache\nbuild\npublic/build\n.env\n\.react-router\n`);
216
+ }
217
+ else {
218
+ (0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, '../files/react-router-ssr/non-root'), options.appProjectRoot, templateVariables);
219
+ }
220
+ if (options.isUsingTsSolutionConfig) {
221
+ (0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, '../files/react-router-ssr/ts-solution'), options.appProjectRoot, templateVariables);
222
+ (0, devkit_1.updateJson)(tree, (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'package.json'), (json) => {
223
+ if (options.projectName !== options.importPath) {
224
+ json.nx = { name: options.projectName };
225
+ }
226
+ if (options.parsedTags?.length) {
227
+ json.nx ??= {};
228
+ json.nx.tags = options.parsedTags;
229
+ }
230
+ return json;
231
+ });
232
+ }
233
+ }
@@ -11,13 +11,24 @@ async function installCommonDependencies(host, options) {
11
11
  const reactDeps = await (0, version_utils_1.getReactDependenciesVersionsToInstall)(host);
12
12
  const dependencies = {};
13
13
  const devDependencies = {
14
- '@types/node': versions_1.typesNodeVersion,
15
14
  '@types/react': reactDeps['@types/react'],
16
15
  '@types/react-dom': reactDeps['@types/react-dom'],
16
+ '@types/node': versions_1.typesNodeVersion,
17
+ ...(options.useReactRouter
18
+ ? {
19
+ '@react-router/dev': versions_1.reactRouterVersion,
20
+ }
21
+ : {}),
17
22
  };
18
23
  if (options.bundler !== 'vite') {
19
24
  dependencies['tslib'] = versions_1.tsLibVersion;
20
25
  }
26
+ if (options.useReactRouter) {
27
+ dependencies['react-router'] = versions_1.reactRouterVersion;
28
+ dependencies['@react-router/node'] = versions_1.reactRouterVersion;
29
+ dependencies['@react-router/serve'] = versions_1.reactRouterVersion;
30
+ dependencies['isbot'] = versions_1.reactRouterIsBotVersion;
31
+ }
21
32
  // Vite requires style preprocessors to be installed manually.
22
33
  // `@nx/webpack` installs them automatically for now.
23
34
  if (options.bundler === 'vite' || options.unitTestRunner === 'vitest') {
@@ -45,5 +56,5 @@ async function installCommonDependencies(host, options) {
45
56
  devDependencies['@testing-library/react'] = versions_1.testingLibraryReactVersion;
46
57
  devDependencies['@testing-library/dom'] = versions_1.testingLibraryDomVersion;
47
58
  }
48
- return (0, devkit_1.addDependenciesToPackageJson)(host, {}, devDependencies);
59
+ return (0, devkit_1.addDependenciesToPackageJson)(host, dependencies, devDependencies);
49
60
  }
@@ -31,6 +31,8 @@ async function normalizeOptions(host, options) {
31
31
  ? null
32
32
  : options.style;
33
33
  (0, assertion_1.assertValidStyle)(options.style);
34
+ options.bundler = options.useReactRouter ? 'vite' : options.bundler;
35
+ options.useReactRouter = options.routing ? options.useReactRouter : false;
34
36
  const normalized = {
35
37
  ...options,
36
38
  projectName: appProjectName,
@@ -44,8 +46,10 @@ async function normalizeOptions(host, options) {
44
46
  hasStyles: options.style !== 'none',
45
47
  names: (0, devkit_1.names)(projectNames.projectSimpleName),
46
48
  isUsingTsSolutionConfig,
49
+ useProjectJson: options.useProjectJson ?? !isUsingTsSolutionConfig,
47
50
  };
48
51
  normalized.routing = normalized.routing ?? false;
52
+ normalized.useReactRouter = normalized.useReactRouter ?? false;
49
53
  normalized.strict = normalized.strict ?? true;
50
54
  normalized.classComponent = normalized.classComponent ?? false;
51
55
  normalized.compiler = normalized.compiler ?? 'babel';
@@ -14,6 +14,7 @@ export interface Schema {
14
14
  linter: Linter | LinterType;
15
15
  classComponent?: boolean;
16
16
  routing?: boolean;
17
+ useReactRouter?: boolean;
17
18
  skipNxJson?: boolean;
18
19
  js?: boolean;
19
20
  globalCss?: boolean;
@@ -31,7 +32,7 @@ export interface Schema {
31
32
  nxCloudToken?: string;
32
33
  useTsSolution?: boolean;
33
34
  formatter?: 'prettier' | 'none';
34
- alwaysGenerateProjectJson?: boolean; // this is needed for MF currently
35
+ useProjectJson?: boolean;
35
36
  }
36
37
 
37
38
  export interface NormalizedSchema<T extends Schema = Schema> extends T {
@@ -83,7 +83,12 @@
83
83
  "routing": {
84
84
  "type": "boolean",
85
85
  "description": "Generate application with routes.",
86
- "x-prompt": "Would you like to add React Router to this application?",
86
+ "x-prompt": "Would you like to add routing to this application?",
87
+ "default": false
88
+ },
89
+ "useReactRouter": {
90
+ "description": "Use React Router for routing.",
91
+ "type": "boolean",
87
92
  "default": false
88
93
  },
89
94
  "skipFormat": {
@@ -187,6 +192,10 @@
187
192
  "description": "Generate a React app with a minimal setup, no separate test files.",
188
193
  "type": "boolean",
189
194
  "default": false
195
+ },
196
+ "useProjectJson": {
197
+ "type": "boolean",
198
+ "description": "Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file."
190
199
  }
191
200
  },
192
201
  "required": ["directory"],
@@ -21,7 +21,7 @@ async function hostGenerator(host, schema) {
21
21
  const options = {
22
22
  ...(await (0, normalize_options_1.normalizeOptions)(host, {
23
23
  ...schema,
24
- alwaysGenerateProjectJson: true,
24
+ useProjectJson: true,
25
25
  })),
26
26
  js: schema.js ?? false,
27
27
  typescriptConfiguration: schema.js
@@ -50,7 +50,7 @@ async function hostGenerator(host, schema) {
50
50
  // The target use-case is loading remotes as child routes, thus always enable routing.
51
51
  routing: true,
52
52
  skipFormat: true,
53
- alwaysGenerateProjectJson: true,
53
+ useProjectJson: true,
54
54
  });
55
55
  tasks.push(initTask);
56
56
  const remotesWithPorts = [];
@@ -4,6 +4,8 @@ exports.reactInitGenerator = reactInitGenerator;
4
4
  const devkit_1 = require("@nx/devkit");
5
5
  const versions_1 = require("../../utils/versions");
6
6
  const version_utils_1 = require("../../utils/version-utils");
7
+ const add_plugin_1 = require("@nx/devkit/src/utils/add-plugin");
8
+ const router_plugin_1 = require("../../plugins/router-plugin");
7
9
  async function reactInitGenerator(tree, schema) {
8
10
  const tasks = [];
9
11
  if (!schema.skipPackageJson) {
@@ -16,6 +18,27 @@ async function reactInitGenerator(tree, schema) {
16
18
  '@nx/react': versions_1.nxVersion,
17
19
  }, undefined, schema.keepExistingVersions));
18
20
  }
21
+ const nxJson = (0, devkit_1.readNxJson)(tree);
22
+ schema.addPlugin ??=
23
+ process.env.NX_ADD_PLUGINS !== 'false' &&
24
+ nxJson.useInferencePlugins !== false;
25
+ if (schema.addPlugin) {
26
+ await (0, add_plugin_1.addPlugin)(tree, await (0, devkit_1.createProjectGraphAsync)(), '@nx/react/router-plugin', router_plugin_1.createNodesV2, {
27
+ buildTargetName: ['build', 'react-router:build', 'react-router-build'],
28
+ devTargetName: ['dev', 'react-router:dev', 'react-router-dev'],
29
+ startTargetName: ['start', 'react-router-serve', 'react-router-start'],
30
+ watchDepsTargetName: [
31
+ 'watch-deps',
32
+ 'react-router:watch-deps',
33
+ 'react-router-watch-deps',
34
+ ],
35
+ buildDepsTargetName: [
36
+ 'build-deps',
37
+ 'react-router:build-deps',
38
+ 'react-router-build-deps',
39
+ ],
40
+ }, schema.updatePackageScripts);
41
+ }
19
42
  if (!schema.skipFormat) {
20
43
  await (0, devkit_1.formatFiles)(tree);
21
44
  }
@@ -2,4 +2,6 @@ export interface InitSchema {
2
2
  skipFormat?: boolean;
3
3
  skipPackageJson?: boolean;
4
4
  keepExistingVersions?: boolean;
5
+ updatePackageScripts?: boolean;
6
+ addPlugin?: boolean;
5
7
  }
@@ -43,7 +43,8 @@ function createFiles(host, options) {
43
43
  });
44
44
  }
45
45
  if ((options.publishable || options.buildable) &&
46
- !options.isUsingTsSolutionConfig) {
46
+ !options.isUsingTsSolutionConfig &&
47
+ options.useProjectJson) {
47
48
  if (options.bundler === 'vite') {
48
49
  (0, devkit_1.writeJson)(host, `${options.projectRoot}/package.json`, {
49
50
  name: options.importPath,
@@ -45,6 +45,7 @@ async function normalizeOptions(host, options) {
45
45
  projectRoot,
46
46
  parsedTags,
47
47
  importPath,
48
+ useProjectJson: options.useProjectJson ?? !isUsingTsSolutionConfig,
48
49
  };
49
50
  // Libraries with a bundler or is publishable must also be buildable.
50
51
  normalized.buildable = Boolean(normalized.bundler !== 'none' || options.buildable || options.publishable);
@@ -27,6 +27,7 @@ const add_release_config_1 = require("@nx/js/src/generators/library/utils/add-re
27
27
  async function libraryGenerator(host, schema) {
28
28
  return await libraryGeneratorInternal(host, {
29
29
  addPlugin: false,
30
+ useProjectJson: true,
30
31
  ...schema,
31
32
  });
32
33
  }
@@ -38,9 +39,6 @@ async function libraryGeneratorInternal(host, schema) {
38
39
  });
39
40
  tasks.push(jsInitTask);
40
41
  const options = await (0, normalize_options_1.normalizeOptions)(host, schema);
41
- if (options.isUsingTsSolutionConfig) {
42
- (0, ts_solution_setup_1.addProjectToTsSolutionWorkspace)(host, options.projectRoot);
43
- }
44
42
  if (options.publishable === true && !schema.importPath) {
45
43
  throw new Error(`For publishable libs you have to provide a proper "--importPath" which needs to be a valid npm package name (e.g. my-awesome-lib or @myorg/my-lib)`);
46
44
  }
@@ -52,13 +50,13 @@ async function libraryGeneratorInternal(host, schema) {
52
50
  skipFormat: true,
53
51
  });
54
52
  tasks.push(initTask);
55
- if (options.isUsingTsSolutionConfig) {
56
- const packageJson = {
57
- name: options.importPath,
58
- version: '0.0.1',
59
- ...(0, determine_entry_fields_1.determineEntryFields)(options),
60
- files: options.publishable ? ['dist', '!**/*.tsbuildinfo'] : undefined,
61
- };
53
+ const packageJson = {
54
+ name: options.importPath,
55
+ version: '0.0.1',
56
+ ...(0, determine_entry_fields_1.determineEntryFields)(options),
57
+ files: options.publishable ? ['dist', '!**/*.tsbuildinfo'] : undefined,
58
+ };
59
+ if (!options.useProjectJson) {
62
60
  if (options.name !== options.importPath) {
63
61
  packageJson.nx = { name: options.name };
64
62
  }
@@ -66,7 +64,6 @@ async function libraryGeneratorInternal(host, schema) {
66
64
  packageJson.nx ??= {};
67
65
  packageJson.nx.tags = options.parsedTags;
68
66
  }
69
- (0, devkit_1.writeJson)(host, `${options.projectRoot}/package.json`, packageJson);
70
67
  }
71
68
  else {
72
69
  (0, devkit_1.addProjectConfiguration)(host, options.name, {
@@ -77,7 +74,13 @@ async function libraryGeneratorInternal(host, schema) {
77
74
  targets: {},
78
75
  });
79
76
  }
77
+ if (!options.useProjectJson || options.isUsingTsSolutionConfig) {
78
+ (0, devkit_1.writeJson)(host, `${options.projectRoot}/package.json`, packageJson);
79
+ }
80
80
  (0, create_files_1.createFiles)(host, options);
81
+ if (options.isUsingTsSolutionConfig) {
82
+ await (0, ts_solution_setup_1.addProjectToTsSolutionWorkspace)(host, options.projectRoot);
83
+ }
81
84
  const lintTask = await (0, add_linting_1.addLinting)(host, options);
82
85
  tasks.push(lintTask);
83
86
  // Set up build target
@@ -27,6 +27,7 @@ export interface Schema {
27
27
  minimal?: boolean;
28
28
  simpleName?: boolean;
29
29
  addPlugin?: boolean;
30
+ useProjectJson?: boolean;
30
31
  }
31
32
 
32
33
  export interface NormalizedSchema extends Schema {