@nx/react 18.0.0-beta.1 → 18.0.0-beta.3

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 (33) hide show
  1. package/generators.json +1 -1
  2. package/package.json +6 -7
  3. package/plugins/component-testing/index.js +32 -9
  4. package/plugins/nx-react-webpack-plugin/lib/apply-react-config.js +0 -6
  5. package/src/generators/application/application.js +5 -0
  6. package/src/generators/application/files/nx-welcome/src/app/nx-welcome.tsx +19 -0
  7. package/src/generators/application/lib/add-e2e.js +1 -0
  8. package/src/generators/application/lib/add-project.js +1 -1
  9. package/src/generators/application/lib/normalize-options.js +1 -0
  10. package/src/generators/application/schema.d.ts +2 -0
  11. package/src/generators/cypress-component-configuration/cypress-component-configuration.d.ts +2 -1
  12. package/src/generators/cypress-component-configuration/cypress-component-configuration.js +14 -4
  13. package/src/generators/cypress-component-configuration/lib/add-files.js +1 -6
  14. package/src/generators/cypress-component-configuration/schema.d.ts +1 -0
  15. package/src/generators/host/files/module-federation-ts/webpack.config.prod.ts__tmpl__ +2 -1
  16. package/src/generators/host/files/module-federation-ts/webpack.config.ts__tmpl__ +2 -2
  17. package/src/generators/host/host.js +2 -0
  18. package/src/generators/host/schema.d.ts +2 -0
  19. package/src/generators/library/lib/add-linting.js +1 -0
  20. package/src/generators/library/lib/normalize-options.js +2 -1
  21. package/src/generators/library/library.js +3 -0
  22. package/src/generators/library/schema.d.ts +1 -0
  23. package/src/generators/remote/remote.js +2 -0
  24. package/src/generators/setup-ssr/setup-ssr.js +23 -4
  25. package/src/generators/stories/stories.js +15 -3
  26. package/src/generators/storybook-configuration/configuration.d.ts +1 -0
  27. package/src/generators/storybook-configuration/configuration.js +14 -4
  28. package/src/generators/storybook-configuration/schema.d.ts +1 -0
  29. package/src/migrations/update-18-0-0/add-mf-env-var-to-target-defaults.js +1 -1
  30. package/src/module-federation/with-module-federation-ssr.js +3 -0
  31. package/src/module-federation/with-module-federation.js +3 -0
  32. package/src/utils/ct-utils.d.ts +6 -1
  33. package/src/utils/ct-utils.js +30 -9
package/generators.json CHANGED
@@ -37,7 +37,7 @@
37
37
  "aliases": ["slice"]
38
38
  },
39
39
  "storybook-configuration": {
40
- "factory": "./src/generators/storybook-configuration/configuration#storybookConfigurationGenerator",
40
+ "factory": "./src/generators/storybook-configuration/configuration#storybookConfigurationGeneratorInternal",
41
41
  "schema": "./src/generators/storybook-configuration/schema.json",
42
42
  "description": "Set up storybook for a React app or library.",
43
43
  "hidden": false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/react",
3
- "version": "18.0.0-beta.1",
3
+ "version": "18.0.0-beta.3",
4
4
  "private": false,
5
5
  "description": "The React plugin for Nx contains executors and generators for managing React applications and libraries within an Nx workspace. It provides:\n\n\n- Integration with libraries such as Jest, Cypress, and Storybook.\n\n- Generators for applications, libraries, components, hooks, and more.\n\n- Library build support for publishing packages to npm or other registries.\n\n- Utilities for automatic workspace refactoring.",
6
6
  "repository": {
@@ -34,14 +34,13 @@
34
34
  "@phenomnomnominal/tsquery": "~5.0.1",
35
35
  "@svgr/webpack": "^8.0.1",
36
36
  "chalk": "^4.1.0",
37
- "file-loader": "^6.2.0",
38
37
  "minimatch": "9.0.3",
39
38
  "tslib": "^2.3.0",
40
- "@nx/devkit": "18.0.0-beta.1",
41
- "@nx/js": "18.0.0-beta.1",
42
- "@nx/eslint": "18.0.0-beta.1",
43
- "@nx/web": "18.0.0-beta.1",
44
- "@nrwl/react": "18.0.0-beta.1"
39
+ "@nx/devkit": "18.0.0-beta.3",
40
+ "@nx/js": "18.0.0-beta.3",
41
+ "@nx/eslint": "18.0.0-beta.3",
42
+ "@nx/web": "18.0.0-beta.3",
43
+ "@nrwl/react": "18.0.0-beta.3"
45
44
  },
46
45
  "publishConfig": {
47
46
  "access": "public"
@@ -25,12 +25,17 @@ const path_1 = require("path");
25
25
  * @param options override options
26
26
  */
27
27
  function nxComponentTestingPreset(pathToConfig, options) {
28
- const normalizedProjectRootPath = ['.ts', '.js'].some((ext) => pathToConfig.endsWith(ext))
29
- ? pathToConfig
30
- : (0, path_1.dirname)(pathToConfig);
31
28
  const basePresetSettings = (0, cypress_preset_1.nxBaseCypressPreset)(pathToConfig, {
32
29
  testingType: 'component',
33
30
  });
31
+ if (global.NX_GRAPH_CREATION || global.NX_CYPRESS_INIT_GENERATOR_RUNNING) {
32
+ // this is only used by plugins, so we don't need the component testing
33
+ // options, cast to any to avoid type errors
34
+ return basePresetSettings;
35
+ }
36
+ const normalizedProjectRootPath = ['.ts', '.js'].some((ext) => pathToConfig.endsWith(ext))
37
+ ? pathToConfig
38
+ : (0, path_1.dirname)(pathToConfig);
34
39
  if (options?.bundler === 'vite') {
35
40
  return {
36
41
  ...basePresetSettings,
@@ -66,18 +71,24 @@ function nxComponentTestingPreset(pathToConfig, options) {
66
71
  const ctTargetName = options?.ctTargetName || 'component-test';
67
72
  const ctConfigurationName = process.env.NX_CYPRESS_TARGET_CONFIGURATION;
68
73
  const ctExecutorContext = (0, ct_helpers_1.createExecutorContext)(graph, ctTargets, ctProjectName, ctTargetName, ctConfigurationName);
69
- const ctExecutorOptions = (0, devkit_1.readTargetOptions)({
70
- project: ctProjectName,
71
- target: ctTargetName,
72
- configuration: ctConfigurationName,
73
- }, ctExecutorContext);
74
- const buildTarget = ctExecutorOptions.devServerTarget;
74
+ let buildTarget = options?.buildTarget;
75
+ if (!buildTarget) {
76
+ const ctExecutorOptions = (0, devkit_1.readTargetOptions)({
77
+ project: ctProjectName,
78
+ target: ctTargetName,
79
+ configuration: ctConfigurationName,
80
+ }, ctExecutorContext);
81
+ buildTarget = ctExecutorOptions.devServerTarget;
82
+ }
75
83
  if (!buildTarget) {
76
84
  throw new Error(`Unable to find the 'devServerTarget' executor option in the '${ctTargetName}' target of the '${ctProjectName}' project`);
77
85
  }
78
86
  webpackConfig = buildTargetWebpack(ctExecutorContext, buildTarget, ctProjectName);
79
87
  }
80
88
  catch (e) {
89
+ if (e instanceof InvalidExecutorError) {
90
+ throw e;
91
+ }
81
92
  devkit_1.logger.warn((0, devkit_1.stripIndents) `Unable to build a webpack config with the project graph.
82
93
  Falling back to default webpack config.`);
83
94
  devkit_1.logger.warn(e);
@@ -137,6 +148,11 @@ function buildTargetWebpack(ctx, buildTarget, componentTestingProjectName) {
137
148
  Has component config? ${!!ctProjectConfig}
138
149
  `);
139
150
  }
151
+ if (buildableProjectConfig.targets[parsed.target].executor !==
152
+ '@nx/webpack:webpack') {
153
+ throw new InvalidExecutorError(`The '${parsed.target}' target of the '${parsed.project}' project is not using the '@nx/webpack:webpack' executor. ` +
154
+ `Please make sure to use '@nx/webpack:webpack' executor in that target to use Cypress Component Testing.`);
155
+ }
140
156
  const context = (0, ct_helpers_1.createExecutorContext)(graph, buildableProjectConfig.targets, parsed.project, parsed.target, parsed.target);
141
157
  const { normalizeOptions, } = require('@nx/webpack/src/executors/webpack/lib/normalize-options');
142
158
  const { resolveUserDefinedWebpackConfig, } = require('@nx/webpack/src/utils/webpack/resolve-user-defined-webpack-config');
@@ -197,3 +213,10 @@ function findTsConfig(projectRoot) {
197
213
  }
198
214
  }
199
215
  }
216
+ class InvalidExecutorError extends Error {
217
+ constructor(message) {
218
+ super(message);
219
+ this.message = message;
220
+ this.name = 'InvalidExecutorError';
221
+ }
222
+ }
@@ -19,12 +19,6 @@ function applyReactConfig(options, config = {}) {
19
19
  ref: true,
20
20
  },
21
21
  },
22
- {
23
- loader: require.resolve('file-loader'),
24
- options: {
25
- name: '[name].[hash].[ext]',
26
- },
27
- },
28
22
  ],
29
23
  });
30
24
  }
@@ -36,6 +36,7 @@ async function addLinting(host, options) {
36
36
  skipFormat: true,
37
37
  rootProject: options.rootProject,
38
38
  skipPackageJson: options.skipPackageJson,
39
+ addPlugin: options.addPlugin,
39
40
  });
40
41
  tasks.push(lintTask);
41
42
  if ((0, eslint_file_1.isEslintConfigSupported)(host)) {
@@ -51,6 +52,7 @@ async function addLinting(host, options) {
51
52
  }
52
53
  async function applicationGenerator(host, schema) {
53
54
  return await applicationGeneratorInternal(host, {
55
+ addPlugin: false,
54
56
  projectNameAndRootFormat: 'derived',
55
57
  ...schema,
56
58
  });
@@ -76,6 +78,7 @@ async function applicationGeneratorInternal(host, schema) {
76
78
  const webpackInitTask = await webpackInitGenerator(host, {
77
79
  skipPackageJson: options.skipPackageJson,
78
80
  skipFormat: true,
81
+ addPlugin: options.addPlugin,
79
82
  });
80
83
  tasks.push(webpackInitTask);
81
84
  if (!options.skipPackageJson) {
@@ -103,6 +106,7 @@ async function applicationGeneratorInternal(host, schema) {
103
106
  inSourceTests: options.inSourceTests,
104
107
  compiler: options.compiler,
105
108
  skipFormat: true,
109
+ addPlugin: options.addPlugin,
106
110
  });
107
111
  tasks.push(viteTask);
108
112
  createOrEditViteConfig(host, {
@@ -143,6 +147,7 @@ async function applicationGeneratorInternal(host, schema) {
143
147
  project: options.projectName,
144
148
  inSourceTests: options.inSourceTests,
145
149
  skipFormat: true,
150
+ addPlugin: options.addPlugin,
146
151
  });
147
152
  tasks.push(vitestTask);
148
153
  createOrEditViteConfig(host, {
@@ -795,6 +795,25 @@ export function NxWelcome({ title }: { title: string }) {
795
795
  nx g @nx/react:component ui/src/lib/button
796
796
  </pre>
797
797
  </details>
798
+ <details>
799
+ <summary>
800
+ <svg
801
+ fill="none"
802
+ stroke="currentColor"
803
+ viewBox="0 0 24 24"
804
+ xmlns="http://www.w3.org/2000/svg"
805
+ >
806
+ <path
807
+ strokeLinecap="round"
808
+ strokeLinejoin="round"
809
+ strokeWidth="2"
810
+ d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
811
+ />
812
+ </svg>
813
+ View project details
814
+ </summary>
815
+ <pre>nx show project {title} --web</pre>
816
+ </details>
798
817
  <details>
799
818
  <summary>
800
819
  <svg
@@ -68,6 +68,7 @@ async function addE2e(tree, options) {
68
68
  webServerCommand: `${(0, devkit_1.getPackageManagerCommand)().exec} nx serve ${options.name}`,
69
69
  webServerAddress: 'http://localhost:4200',
70
70
  rootProject: options.rootProject,
71
+ addPlugin: options.addPlugin,
71
72
  });
72
73
  }
73
74
  case 'none':
@@ -12,7 +12,7 @@ function addProject(host, options) {
12
12
  tags: options.parsedTags,
13
13
  };
14
14
  if (options.bundler === 'webpack') {
15
- if (!(0, has_webpack_plugin_1.hasWebpackPlugin)(host)) {
15
+ if (!(0, has_webpack_plugin_1.hasWebpackPlugin)(host) || !options.addPlugin) {
16
16
  project.targets = {
17
17
  build: createBuildTarget(options),
18
18
  serve: createServeTarget(options),
@@ -26,6 +26,7 @@ async function normalizeOptions(host, options, callingGenerator = '@nx/react:app
26
26
  rootProject: options.rootProject,
27
27
  callingGenerator,
28
28
  });
29
+ options.addPlugin ??= process.env.NX_ADD_PLUGINS !== 'false';
29
30
  options.rootProject = appProjectRoot === '.';
30
31
  options.projectNameAndRootFormat = projectNameAndRootFormat;
31
32
  const e2eProjectName = options.rootProject ? 'e2e' : `${appProjectName}-e2e`;
@@ -28,6 +28,7 @@ export interface Schema {
28
28
  rootProject?: boolean;
29
29
  bundler?: 'webpack' | 'vite' | 'rspack';
30
30
  minimal?: boolean;
31
+ addPlugin?: boolean;
31
32
  }
32
33
 
33
34
  export interface NormalizedSchema<T extends Schema = Schema> extends T {
@@ -40,4 +41,5 @@ export interface NormalizedSchema<T extends Schema = Schema> extends T {
40
41
  styledModule: null | SupportedStyles;
41
42
  hasStyles: boolean;
42
43
  unitTestRunner: 'jest' | 'vitest' | 'none';
44
+ addPlugin?: boolean;
43
45
  }
@@ -1,9 +1,10 @@
1
1
  import { Tree } from '@nx/devkit';
2
2
  import { CypressComponentConfigurationSchema } from './schema.d';
3
+ export declare function cypressComponentConfigGenerator(tree: Tree, options: CypressComponentConfigurationSchema): Promise<import("@nx/devkit").GeneratorCallback>;
3
4
  /**
4
5
  * This is for using cypresses own Component testing, if you want to use test
5
6
  * storybook components then use componentCypressGenerator instead.
6
7
  *
7
8
  */
8
- export declare function cypressComponentConfigGenerator(tree: Tree, options: CypressComponentConfigurationSchema): Promise<import("@nx/devkit").GeneratorCallback>;
9
+ export declare function cypressComponentConfigGeneratorInternal(tree: Tree, options: CypressComponentConfigurationSchema): Promise<import("@nx/devkit").GeneratorCallback>;
9
10
  export default cypressComponentConfigGenerator;
@@ -1,26 +1,36 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.cypressComponentConfigGenerator = void 0;
3
+ exports.cypressComponentConfigGeneratorInternal = exports.cypressComponentConfigGenerator = void 0;
4
4
  const devkit_1 = require("@nx/devkit");
5
5
  const versions_1 = require("../../utils/versions");
6
6
  const add_files_1 = require("./lib/add-files");
7
7
  const ct_utils_1 = require("../../utils/ct-utils");
8
+ function cypressComponentConfigGenerator(tree, options) {
9
+ return cypressComponentConfigGeneratorInternal(tree, {
10
+ addPlugin: false,
11
+ ...options,
12
+ });
13
+ }
14
+ exports.cypressComponentConfigGenerator = cypressComponentConfigGenerator;
8
15
  /**
9
16
  * This is for using cypresses own Component testing, if you want to use test
10
17
  * storybook components then use componentCypressGenerator instead.
11
18
  *
12
19
  */
13
- async function cypressComponentConfigGenerator(tree, options) {
20
+ async function cypressComponentConfigGeneratorInternal(tree, options) {
14
21
  const { componentConfigurationGenerator: baseCyCtConfig } = (0, devkit_1.ensurePackage)('@nx/cypress', versions_1.nxVersion);
22
+ options.addPlugin ??= process.env.NX_ADD_PLUGINS !== 'false';
15
23
  const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
16
24
  const installTask = await baseCyCtConfig(tree, {
17
25
  project: options.project,
18
26
  skipFormat: true,
19
27
  jsx: true,
28
+ addPlugin: options.addPlugin,
20
29
  });
21
- const found = await (0, ct_utils_1.addCTTargetWithBuildTarget)(tree, {
30
+ const found = await (0, ct_utils_1.configureCypressCT)(tree, {
22
31
  project: options.project,
23
32
  buildTarget: options.buildTarget,
33
+ bundler: options.bundler,
24
34
  validExecutorNames: new Set([
25
35
  '@nx/webpack:webpack',
26
36
  '@nx/vite:build',
@@ -34,5 +44,5 @@ async function cypressComponentConfigGenerator(tree, options) {
34
44
  }
35
45
  return installTask;
36
46
  }
37
- exports.cypressComponentConfigGenerator = cypressComponentConfigGenerator;
47
+ exports.cypressComponentConfigGeneratorInternal = cypressComponentConfigGeneratorInternal;
38
48
  exports.default = cypressComponentConfigGenerator;
@@ -10,9 +10,7 @@ async function addFiles(tree, projectConfig, options, found) {
10
10
  // when importing react
11
11
  const { addMountDefinition, addDefaultCTConfig } = await Promise.resolve().then(() => require('@nx/cypress/src/utils/config'));
12
12
  // Specifically undefined to allow Remix workaround of passing an empty string
13
- const actualBundler = options.buildTarget !== undefined && options.bundler
14
- ? options.bundler
15
- : await (0, ct_utils_1.getBundlerFromTarget)(found, tree);
13
+ const actualBundler = await (0, ct_utils_1.getActualBundler)(tree, options, found);
16
14
  if (options.bundler && options.bundler !== actualBundler) {
17
15
  devkit_1.logger.warn(`You have specified ${options.bundler} as the bundler but this project is configured to use ${actualBundler}.
18
16
  This may cause errors. If you are seeing errors, try removing the --bundler option.`);
@@ -21,9 +19,6 @@ async function addFiles(tree, projectConfig, options, found) {
21
19
  const commandFile = (0, devkit_1.joinPathFragments)(projectConfig.root, 'cypress', 'support', 'component.ts');
22
20
  const updatedCommandFile = await addMountDefinition(tree.read(commandFile, 'utf-8'));
23
21
  tree.write(commandFile, `import { mount } from 'cypress/react18';\n${updatedCommandFile}`);
24
- const cyFile = (0, devkit_1.joinPathFragments)(projectConfig.root, 'cypress.config.ts');
25
- const updatedCyConfig = await addDefaultCTConfig(tree.read(cyFile, 'utf-8'), { bundler: bundlerToUse });
26
- tree.write(cyFile, `import { nxComponentTestingPreset } from '@nx/react/plugins/component-testing';\n${updatedCyConfig}`);
27
22
  if (options.bundler === 'webpack' ||
28
23
  (!options.bundler && actualBundler === 'webpack')) {
29
24
  (0, devkit_1.addDependenciesToPackageJson)(tree, {}, { '@nx/webpack': versions_1.nxVersion });
@@ -4,4 +4,5 @@ export interface CypressComponentConfigurationSchema {
4
4
  skipFormat?: boolean;
5
5
  buildTarget?: string;
6
6
  bundler?: 'webpack' | 'vite';
7
+ addPlugin?: boolean;
7
8
  }
@@ -1,10 +1,11 @@
1
1
  import { composePlugins, withNx } from '@nx/webpack';
2
2
  import { withReact } from '@nx/react';
3
3
  import { withModuleFederation } from '@nx/react/module-federation';
4
+ import { ModuleFederationConfig } from '@nx/webpack';
4
5
 
5
6
  import baseConfig from './module-federation.config';
6
7
 
7
- const prodConfig = {
8
+ const prodConfig: ModuleFederationConfig = {
8
9
  ...baseConfig,
9
10
  /*
10
11
  * Remote overrides for production.
@@ -1,10 +1,10 @@
1
- import {composePlugins, withNx} from '@nx/webpack';
1
+ import {composePlugins, withNx, ModuleFederationConfig} from '@nx/webpack';
2
2
  import {withReact} from '@nx/react';
3
3
  import {withModuleFederation} from '@nx/react/module-federation';
4
4
 
5
5
  import baseConfig from './module-federation.config';
6
6
 
7
- const config = {
7
+ const config: ModuleFederationConfig = {
8
8
  ...baseConfig,
9
9
  };
10
10
 
@@ -25,6 +25,8 @@ async function hostGeneratorInternal(host, schema) {
25
25
  ...(await (0, normalize_options_1.normalizeOptions)(host, schema, '@nx/react:host')),
26
26
  typescriptConfiguration: schema.typescriptConfiguration ?? true,
27
27
  dynamic: schema.dynamic ?? false,
28
+ // TODO(colum): remove when MF works with Crystal
29
+ addPlugin: false,
28
30
  };
29
31
  const initTask = await (0, application_1.default)(host, {
30
32
  ...options,
@@ -26,10 +26,12 @@ export interface Schema {
26
26
  minimal?: boolean;
27
27
  typescriptConfiguration?: boolean;
28
28
  dynamic?: boolean;
29
+ addPlugin?: boolean;
29
30
  }
30
31
 
31
32
  export interface NormalizedSchema extends Schema {
32
33
  appProjectRoot: string;
33
34
  e2eProjectName: string;
34
35
  projectName: string;
36
+ addPlugin?: boolean;
35
37
  }
@@ -18,6 +18,7 @@ async function addLinting(host, options) {
18
18
  skipFormat: true,
19
19
  skipPackageJson: options.skipPackageJson,
20
20
  setParserOptionsProject: options.setParserOptionsProject,
21
+ addPlugin: options.addPlugin,
21
22
  });
22
23
  if ((0, eslint_file_1.isEslintConfigSupported)(host)) {
23
24
  (0, eslint_file_1.addExtendsToLintConfig)(host, options.projectRoot, 'plugin:@nx/react');
@@ -13,6 +13,7 @@ async function normalizeOptions(host, options) {
13
13
  projectNameAndRootFormat: options.projectNameAndRootFormat,
14
14
  callingGenerator: '@nx/react:library',
15
15
  });
16
+ options.addPlugin ??= process.env.NX_ADD_PLUGINS !== 'false';
16
17
  const fileName = options.simpleName
17
18
  ? projectNames.projectSimpleName
18
19
  : projectNames.projectFileName;
@@ -50,7 +51,7 @@ async function normalizeOptions(host, options) {
50
51
  throw new Error(`appProject expected type of "application" but got "${appProjectConfig.projectType}"`);
51
52
  }
52
53
  normalized.appMain =
53
- appProjectConfig.targets.build.options.main ??
54
+ appProjectConfig.targets.build?.options?.main ??
54
55
  findMainEntry(host, appProjectConfig.root);
55
56
  normalized.appSourceRoot = (0, devkit_1.normalizePath)(appProjectConfig.sourceRoot);
56
57
  // TODO(jack): We should use appEntryFile instead of appProject so users can directly set it rather than us inferring it.
@@ -20,6 +20,7 @@ const path_1 = require("path");
20
20
  const log_show_project_command_1 = require("@nx/devkit/src/utils/log-show-project-command");
21
21
  async function libraryGenerator(host, schema) {
22
22
  return await libraryGeneratorInternal(host, {
23
+ addPlugin: false,
23
24
  projectNameAndRootFormat: 'derived',
24
25
  ...schema,
25
26
  });
@@ -67,6 +68,7 @@ async function libraryGeneratorInternal(host, schema) {
67
68
  compiler: options.compiler,
68
69
  skipFormat: true,
69
70
  testEnvironment: 'jsdom',
71
+ addPlugin: options.addPlugin,
70
72
  });
71
73
  tasks.push(viteTask);
72
74
  createOrEditViteConfig(host, {
@@ -121,6 +123,7 @@ async function libraryGeneratorInternal(host, schema) {
121
123
  inSourceTests: options.inSourceTests,
122
124
  skipFormat: true,
123
125
  testEnvironment: 'jsdom',
126
+ addPlugin: options.addPlugin,
124
127
  });
125
128
  tasks.push(vitestTask);
126
129
  createOrEditViteConfig(host, {
@@ -29,6 +29,7 @@ export interface Schema {
29
29
  unitTestRunner?: 'jest' | 'vitest' | 'none';
30
30
  minimal?: boolean;
31
31
  simpleName?: boolean;
32
+ addPlugin?: boolean;
32
33
  }
33
34
 
34
35
  export interface NormalizedSchema extends Schema {
@@ -47,6 +47,8 @@ async function remoteGeneratorInternal(host, schema) {
47
47
  ...(await (0, normalize_options_1.normalizeOptions)(host, schema, '@nx/react:remote')),
48
48
  typescriptConfiguration: schema.typescriptConfiguration ?? false,
49
49
  dynamic: schema.dynamic ?? false,
50
+ // TODO(colum): remove when MF works with Crystal
51
+ addPlugin: false,
50
52
  };
51
53
  const initAppTask = await (0, application_1.default)(host, {
52
54
  ...options,
@@ -17,8 +17,22 @@ function readEntryFile(host, path) {
17
17
  source: tsModule.createSourceFile(path, content, tsModule.ScriptTarget.Latest, true),
18
18
  };
19
19
  }
20
+ async function getProjectConfig(tree, projectName) {
21
+ let maybeProjectConfig = (0, devkit_1.readProjectConfiguration)(tree, projectName);
22
+ if (!maybeProjectConfig.targets?.build) {
23
+ let projectGraph;
24
+ try {
25
+ projectGraph = (0, devkit_1.readCachedProjectGraph)();
26
+ }
27
+ catch {
28
+ projectGraph = await (0, devkit_1.createProjectGraphAsync)();
29
+ }
30
+ maybeProjectConfig = projectGraph.nodes[projectName].data;
31
+ }
32
+ return maybeProjectConfig;
33
+ }
20
34
  async function setupSsrGenerator(tree, options) {
21
- const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
35
+ const projectConfig = await getProjectConfig(tree, options.project);
22
36
  const projectRoot = projectConfig.root;
23
37
  const appImportCandidates = [
24
38
  options.appComponentImportPath ?? 'app/app',
@@ -42,11 +56,15 @@ async function setupSsrGenerator(tree, options) {
42
56
  if (projectConfig.targets.server) {
43
57
  throw new Error(`Project ${options.project} already has a server target.`);
44
58
  }
45
- const originalOutputPath = projectConfig.targets.build?.options?.outputPath;
59
+ const originalOutputPath = projectConfig.targets.build?.options?.outputPath ??
60
+ projectConfig.targets.build?.outputs[0];
46
61
  if (!originalOutputPath) {
47
62
  throw new Error(`Project ${options.project} does not contain a outputPath for the build target.`);
48
63
  }
49
- projectConfig.targets.build.options.outputPath = (0, devkit_1.joinPathFragments)(originalOutputPath, 'browser');
64
+ // TODO(colum): We need to figure out how to handle this for Crystal
65
+ if (projectConfig.targets.build.options?.outputPath) {
66
+ projectConfig.targets.build.options.outputPath = (0, devkit_1.joinPathFragments)(originalOutputPath, 'browser');
67
+ }
50
68
  projectConfig.targets = {
51
69
  ...projectConfig.targets,
52
70
  server: {
@@ -136,7 +154,8 @@ async function setupSsrGenerator(tree, options) {
136
154
  ? `"${options.extraInclude.join('", "')}",`
137
155
  : '',
138
156
  appComponentImport: appComponentInfo.importPath,
139
- browserBuildOutputPath: projectConfig.targets.build.options.outputPath,
157
+ browserBuildOutputPath: projectConfig.targets.build?.options?.outputPath ??
158
+ projectConfig.targets.build?.outputs[0],
140
159
  });
141
160
  // Add <StaticRouter> to server main if needed.
142
161
  // TODO: need to read main.server.tsx not main.tsx.
@@ -11,11 +11,10 @@ const ensure_typescript_1 = require("@nx/js/src/utils/typescript/ensure-typescri
11
11
  const versions_1 = require("../../utils/versions");
12
12
  let tsModule;
13
13
  async function projectRootPath(tree, config) {
14
- const { findStorybookAndBuildTargetsAndCompiler } = await Promise.resolve().then(() => require('@nx/storybook/src/utils/utilities'));
15
14
  let projectDir;
16
15
  if (config.projectType === 'application') {
17
- const { nextBuildTarget } = findStorybookAndBuildTargetsAndCompiler(config.targets);
18
- if (!!nextBuildTarget) {
16
+ const isNextJs = await isNextJsProject(tree, config);
17
+ if (isNextJs) {
19
18
  // Next.js apps
20
19
  projectDir = 'components';
21
20
  }
@@ -115,4 +114,17 @@ async function storiesGenerator(host, schema) {
115
114
  return (0, devkit_1.runTasksInSerial)(...tasks);
116
115
  }
117
116
  exports.storiesGenerator = storiesGenerator;
117
+ async function isNextJsProject(tree, config) {
118
+ const { findStorybookAndBuildTargetsAndCompiler } = await Promise.resolve().then(() => require('@nx/storybook/src/utils/utilities'));
119
+ const { nextBuildTarget } = findStorybookAndBuildTargetsAndCompiler(config.targets);
120
+ if (nextBuildTarget) {
121
+ return true;
122
+ }
123
+ for (const configFile of ['next.config.js', 'next.config.ts']) {
124
+ if (tree.exists((0, path_1.join)(config.root, configFile))) {
125
+ return true;
126
+ }
127
+ }
128
+ return false;
129
+ }
118
130
  exports.default = storiesGenerator;
@@ -1,5 +1,6 @@
1
1
  import { StorybookConfigureSchema } from './schema';
2
2
  import { Tree } from '@nx/devkit';
3
3
  export declare function storybookConfigurationGenerator(host: Tree, schema: StorybookConfigureSchema): Promise<import("@nx/devkit").GeneratorCallback>;
4
+ export declare function storybookConfigurationGeneratorInternal(host: Tree, schema: StorybookConfigureSchema): Promise<import("@nx/devkit").GeneratorCallback>;
4
5
  export default storybookConfigurationGenerator;
5
6
  export declare function findWebpackConfig(tree: Tree, projectRoot: string): string | undefined;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.findWebpackConfig = exports.storybookConfigurationGenerator = void 0;
3
+ exports.findWebpackConfig = exports.storybookConfigurationGeneratorInternal = exports.storybookConfigurationGenerator = void 0;
4
4
  const stories_1 = require("../stories/stories");
5
5
  const devkit_1 = require("@nx/devkit");
6
6
  const versions_1 = require("../../utils/versions");
@@ -20,13 +20,22 @@ async function generateStories(host, schema) {
20
20
  interactionTests: schema.interactionTests ?? true,
21
21
  });
22
22
  }
23
- async function storybookConfigurationGenerator(host, schema) {
23
+ function storybookConfigurationGenerator(host, schema) {
24
+ return storybookConfigurationGeneratorInternal(host, {
25
+ addPlugin: false,
26
+ ...schema,
27
+ });
28
+ }
29
+ exports.storybookConfigurationGenerator = storybookConfigurationGenerator;
30
+ async function storybookConfigurationGeneratorInternal(host, schema) {
31
+ schema.addPlugin ??= process.env.NX_ADD_PLUGINS !== 'false';
24
32
  const { configurationGenerator } = (0, devkit_1.ensurePackage)('@nx/storybook', versions_1.nxVersion);
25
33
  let uiFramework = '@storybook/react-vite';
26
34
  const projectConfig = (0, devkit_1.readProjectConfiguration)(host, schema.project);
27
35
  if (findWebpackConfig(host, projectConfig.root) ||
28
36
  projectConfig.targets['build']?.executor === '@nx/rollup:rollup' ||
29
- projectConfig.targets['build']?.executor === '@nrwl/rollup:rollup') {
37
+ projectConfig.targets['build']?.executor === '@nrwl/rollup:rollup' ||
38
+ projectConfig.targets['build']?.executor === '@nx/expo:build') {
30
39
  uiFramework = '@storybook/react-webpack5';
31
40
  }
32
41
  const installTask = await configurationGenerator(host, {
@@ -40,6 +49,7 @@ async function storybookConfigurationGenerator(host, schema) {
40
49
  configureStaticServe: schema.configureStaticServe,
41
50
  uiFramework: uiFramework, // cannot import UiFramework type dynamically
42
51
  skipFormat: true,
52
+ addPlugin: schema.addPlugin,
43
53
  });
44
54
  if (schema.generateStories) {
45
55
  await generateStories(host, schema);
@@ -47,7 +57,7 @@ async function storybookConfigurationGenerator(host, schema) {
47
57
  await (0, devkit_1.formatFiles)(host);
48
58
  return installTask;
49
59
  }
50
- exports.storybookConfigurationGenerator = storybookConfigurationGenerator;
60
+ exports.storybookConfigurationGeneratorInternal = storybookConfigurationGeneratorInternal;
51
61
  exports.default = storybookConfigurationGenerator;
52
62
  function findWebpackConfig(tree, projectRoot) {
53
63
  const allowsExt = ['js', 'mjs', 'ts', 'cjs', 'mts', 'cts'];
@@ -12,4 +12,5 @@ export interface StorybookConfigureSchema {
12
12
  configureCypress?: boolean;
13
13
  generateCypressSpecs?: boolean;
14
14
  cypressDirectory?: string;
15
+ addPlugin?: boolean;
15
16
  }
@@ -13,7 +13,7 @@ exports.default = default_1;
13
13
  function hasModuleFederationProject(tree) {
14
14
  const projects = (0, devkit_1.getProjects)(tree);
15
15
  for (const project of projects.values()) {
16
- const targets = project.targets;
16
+ const targets = project.targets || {};
17
17
  for (const [_, target] of Object.entries(targets)) {
18
18
  if (target.executor === '@nx/webpack:webpack' &&
19
19
  (tree.exists((0, devkit_1.joinPathFragments)(project.root, 'module-federation.config.ts')) ||
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.withModuleFederationForSSR = void 0;
4
4
  const utils_1 = require("./utils");
5
5
  async function withModuleFederationForSSR(options) {
6
+ if (global.NX_GRAPH_CREATION) {
7
+ return (config) => config;
8
+ }
6
9
  const { sharedLibraries, sharedDependencies, mappedRemotes } = await (0, utils_1.getModuleFederationConfig)(options, {
7
10
  isServer: true,
8
11
  });
@@ -9,6 +9,9 @@ const isVarOrWindow = (libType) => libType === 'var' || libType === 'window';
9
9
  * @return {Promise<AsyncNxComposableWebpackPlugin>}
10
10
  */
11
11
  async function withModuleFederation(options) {
12
+ if (global.NX_GRAPH_CREATION) {
13
+ return (config) => config;
14
+ }
12
15
  const { sharedDependencies, sharedLibraries, mappedRemotes } = await (0, utils_1.getModuleFederationConfig)(options);
13
16
  const isGlobal = isVarOrWindow(options.library?.type);
14
17
  return (config, ctx) => {
@@ -1,9 +1,14 @@
1
1
  import { Tree } from '@nx/devkit';
2
2
  import { type FoundTarget } from '@nx/cypress/src/utils/find-target-options';
3
- export declare function addCTTargetWithBuildTarget(tree: Tree, options: {
3
+ export declare function configureCypressCT(tree: Tree, options: {
4
4
  project: string;
5
5
  buildTarget: string;
6
+ bundler: 'vite' | 'webpack';
6
7
  validExecutorNames: Set<string>;
7
8
  }): Promise<FoundTarget>;
8
9
  export declare function getBundlerFromTarget(found: FoundTarget, tree: Tree): Promise<'vite' | 'webpack'>;
10
+ export declare function getActualBundler(tree: Tree, options: {
11
+ buildTarget?: string;
12
+ bundler?: 'vite' | 'webpack';
13
+ }, found: FoundTarget): Promise<"vite" | "webpack">;
9
14
  export declare function isComponent(tree: Tree, filePath: string): boolean;
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isComponent = exports.getBundlerFromTarget = exports.addCTTargetWithBuildTarget = void 0;
3
+ exports.isComponent = exports.getActualBundler = exports.getBundlerFromTarget = exports.configureCypressCT = void 0;
4
4
  const devkit_1 = require("@nx/devkit");
5
5
  const ensure_typescript_1 = require("@nx/js/src/utils/typescript/ensure-typescript");
6
6
  const ast_utils_1 = require("./ast-utils");
7
7
  let tsModule;
8
8
  const allowedFileExt = new RegExp(/\.[jt]sx?/);
9
9
  const isSpecFile = new RegExp(/(spec|test)\./);
10
- async function addCTTargetWithBuildTarget(tree, options) {
10
+ async function configureCypressCT(tree, options) {
11
11
  let found = { target: options.buildTarget, config: undefined };
12
12
  // Specifically undefined as a workaround for Remix to pass an empty string as the buildTarget
13
13
  if (options.buildTarget === undefined) {
@@ -19,16 +19,29 @@ async function addCTTargetWithBuildTarget(tree, options) {
19
19
  });
20
20
  assertValidConfig(found?.config);
21
21
  }
22
- const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
23
- projectConfig.targets['component-test'].options = {
24
- ...projectConfig.targets['component-test'].options,
25
- devServerTarget: found.target,
26
- skipServe: true,
22
+ const { addDefaultCTConfig, getProjectCypressConfigPath } = await Promise.resolve().then(() => require('@nx/cypress/src/utils/config'));
23
+ const ctConfigOptions = {
24
+ bundler: options.bundler ?? (await getActualBundler(tree, options, found)),
27
25
  };
28
- (0, devkit_1.updateProjectConfiguration)(tree, options.project, projectConfig);
26
+ const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
27
+ if (projectConfig.targets?.['component-test']?.executor ===
28
+ '@nx/cypress:cypress') {
29
+ projectConfig.targets['component-test'].options = {
30
+ ...projectConfig.targets['component-test'].options,
31
+ devServerTarget: found.target,
32
+ skipServe: true,
33
+ };
34
+ (0, devkit_1.updateProjectConfiguration)(tree, options.project, projectConfig);
35
+ }
36
+ else {
37
+ ctConfigOptions.buildTarget = found.target;
38
+ }
39
+ const cypressConfigFilePath = getProjectCypressConfigPath(tree, projectConfig.root);
40
+ const updatedCyConfig = await addDefaultCTConfig(tree.read(cypressConfigFilePath, 'utf-8'), ctConfigOptions);
41
+ tree.write(cypressConfigFilePath, `import { nxComponentTestingPreset } from '@nx/react/plugins/component-testing';\n${updatedCyConfig}`);
29
42
  return found;
30
43
  }
31
- exports.addCTTargetWithBuildTarget = addCTTargetWithBuildTarget;
44
+ exports.configureCypressCT = configureCypressCT;
32
45
  function assertValidConfig(config) {
33
46
  if (!config) {
34
47
  throw new Error('Unable to find a valid build configuration. Try passing in a target for an app. --build-target=<project>:<target>[:<configuration>]');
@@ -49,6 +62,14 @@ async function getBundlerFromTarget(found, tree) {
49
62
  : 'webpack';
50
63
  }
51
64
  exports.getBundlerFromTarget = getBundlerFromTarget;
65
+ async function getActualBundler(tree, options, found) {
66
+ // Specifically undefined to allow Remix workaround of passing an empty string
67
+ const actualBundler = options.buildTarget !== undefined && options.bundler
68
+ ? options.bundler
69
+ : await getBundlerFromTarget(found, tree);
70
+ return actualBundler;
71
+ }
72
+ exports.getActualBundler = getActualBundler;
52
73
  function isComponent(tree, filePath) {
53
74
  if (!tsModule) {
54
75
  tsModule = (0, ensure_typescript_1.ensureTypescript)();