@nx/storybook 23.2.0-beta.7 → 23.2.0-beta.9

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.
@@ -96,6 +96,9 @@ async function configurationGeneratorInternal(tree, rawSchema) {
96
96
  else {
97
97
  devDeps['storybook'] = (0, utilities_1.getStorybookVersionToInstall)(tree);
98
98
  }
99
+ if (schema.interactionTests) {
100
+ devDeps['@storybook/test-runner'] = (0, versions_1.versions)(tree).testRunnerVersion;
101
+ }
99
102
  if (schema.tsConfiguration) {
100
103
  devDeps['ts-node'] = versions_1.tsNodeVersion;
101
104
  }
@@ -74,7 +74,7 @@ function addAngularStorybookTarget(tree, projectName, interactionTests) {
74
74
  options: {
75
75
  port: 4400,
76
76
  configDir: `${projectConfig.root}/.storybook`,
77
- browserTarget: `${projectName}:${ngBuildTarget ? 'build' : 'build-storybook'}`,
77
+ browserTarget: `${projectName}:${ngBuildTarget ?? 'build-storybook'}`,
78
78
  compodoc: false,
79
79
  },
80
80
  configurations: {
@@ -89,7 +89,7 @@ function addAngularStorybookTarget(tree, projectName, interactionTests) {
89
89
  options: {
90
90
  outputDir: (0, devkit_1.joinPathFragments)('dist/storybook', projectName),
91
91
  configDir: `${projectConfig.root}/.storybook`,
92
- browserTarget: `${projectName}:${ngBuildTarget ? 'build' : 'build-storybook'}`,
92
+ browserTarget: `${projectName}:${ngBuildTarget ?? 'build-storybook'}`,
93
93
  compodoc: false,
94
94
  },
95
95
  configurations: {
@@ -8,6 +8,7 @@ const fs_1 = require("fs");
8
8
  const js_1 = require("@nx/js");
9
9
  const tsquery_1 = require("@phenomnomnominal/tsquery");
10
10
  const internal_2 = require("@nx/js/internal");
11
+ const utilities_1 = require("../utils/utilities");
11
12
  /**
12
13
  * @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'.
13
14
  */
@@ -16,6 +17,23 @@ const createDependencies = () => {
16
17
  };
17
18
  exports.createDependencies = createDependencies;
18
19
  const storybookConfigGlob = '**/.storybook/main.{js,ts,mjs,mts,cjs,cts}';
20
+ // Vite-builder frameworks we infer the addon target for. The addon itself only
21
+ // checks that the resolved builder is Vite, so this list is conservative rather
22
+ // than exhaustive. `@storybook/sveltekit` uses the Vite builder despite the name.
23
+ const VITE_BUILDER_FRAMEWORKS = new Set([
24
+ '@storybook/angular-vite',
25
+ '@storybook/react-vite',
26
+ '@storybook/react-native-web-vite',
27
+ '@storybook/tanstack-react',
28
+ '@storybook/vue-vite',
29
+ '@storybook/vue3-vite',
30
+ '@storybook/svelte-vite',
31
+ '@storybook/sveltekit',
32
+ '@storybook/web-components-vite',
33
+ '@storybook/html-vite',
34
+ '@storybook/preact-vite',
35
+ '@storybook/nextjs-vite',
36
+ ]);
19
37
  exports.createNodes = [
20
38
  storybookConfigGlob,
21
39
  async (configFilePaths, options, context) => {
@@ -69,9 +87,9 @@ function getProjectRootFromConfigPath(configFilePath) {
69
87
  return projectRoot;
70
88
  }
71
89
  async function createNodesInternal(configFilePath, options, context, targetsCache, pmc, projectRoot, hash) {
72
- const projectName = buildProjectName(projectRoot, context.workspaceRoot);
90
+ const { projectName, angularBuildTarget } = getProjectMetadata(projectRoot, context.workspaceRoot);
73
91
  if (!targetsCache.has(hash)) {
74
- targetsCache.set(hash, await buildStorybookTargets(configFilePath, projectRoot, options, context, projectName, pmc));
92
+ targetsCache.set(hash, await buildStorybookTargets(configFilePath, projectRoot, options, context, projectName, angularBuildTarget, pmc));
75
93
  }
76
94
  const result = {
77
95
  projects: {
@@ -83,35 +101,51 @@ async function createNodesInternal(configFilePath, options, context, targetsCach
83
101
  };
84
102
  return result;
85
103
  }
86
- async function buildStorybookTargets(configFilePath, projectRoot, options, context, projectName, pmc) {
104
+ async function buildStorybookTargets(configFilePath, projectRoot, options, context, projectName, angularBuildTarget, pmc) {
87
105
  const buildOutputs = getOutputs();
88
106
  const namedInputs = (0, internal_1.getNamedInputs)(projectRoot, context);
89
107
  // First attempt to do a very fast lookup for the framework
90
108
  // If that fails, the framework might be inherited, so do a very heavyweight lookup
91
- const storybookFramework = (await getStorybookFramework(configFilePath, context)) ||
92
- (await getStorybookFullyResolvedFramework(configFilePath, context));
109
+ // The slow path returns whatever the config holds, which for the common
110
+ // `getAbsolutePath('@storybook/react-vite')` shape is a resolved path.
111
+ const storybookFramework = normalizePackageName((await getStorybookFramework(configFilePath, context)) ||
112
+ (await getStorybookFullyResolvedFramework(configFilePath, context)));
93
113
  const frameworkIsAngular = storybookFramework === '@storybook/angular';
94
114
  if (frameworkIsAngular && !projectName) {
95
115
  throw new Error(`Could not find a name for the project at '${projectRoot}'. Please make sure that the project has a package.json or project.json file with name specified.`);
96
116
  }
97
117
  const targets = {};
98
- targets[options.buildStorybookTargetName] = buildTarget(namedInputs, buildOutputs, projectRoot, frameworkIsAngular, projectName, configFilePath);
99
- targets[options.serveStorybookTargetName] = serveTarget(projectRoot, frameworkIsAngular, projectName, configFilePath);
100
- if (isStorybookTestRunnerInstalled()) {
118
+ // Resolution alone is not enough: the addon lives in the root package.json, so
119
+ // it resolves for every project. Registration in this project's own config is
120
+ // what makes it ours. Resolve first - it is a stat walk, while reading the
121
+ // registration executes the user's config.
122
+ const usesVitestAddon = VITE_BUILDER_FRAMEWORKS.has(storybookFramework) &&
123
+ isInstalled('@storybook/addon-vitest', context.workspaceRoot, projectRoot) &&
124
+ (await registersVitestAddon(configFilePath, context));
125
+ const hasTestRunner = isInstalled('@storybook/test-runner', context.workspaceRoot, projectRoot);
126
+ // Falls back to the storybook build target, whose name is configurable, so
127
+ // resolve it from the options rather than assuming the default.
128
+ const browserTarget = `${projectName}:${angularBuildTarget ?? options.buildStorybookTargetName}`;
129
+ targets[options.buildStorybookTargetName] = buildTarget(namedInputs, buildOutputs, projectRoot, frameworkIsAngular, browserTarget, configFilePath, hasTestRunner);
130
+ targets[options.serveStorybookTargetName] = serveTarget(projectRoot, frameworkIsAngular, browserTarget, configFilePath);
131
+ if (usesVitestAddon) {
132
+ targets[options.testStorybookTargetName] = vitestTestTarget(projectRoot);
133
+ }
134
+ else if (hasTestRunner) {
101
135
  targets[options.testStorybookTargetName] = testTarget(projectRoot);
102
136
  }
103
137
  targets[options.staticStorybookTargetName] = serveStaticTarget(options, projectRoot);
104
138
  (0, internal_2.addBuildAndWatchDepsTargets)(context.workspaceRoot, projectRoot, targets, options, pmc);
105
139
  return targets;
106
140
  }
107
- function buildTarget(namedInputs, outputs, projectRoot, frameworkIsAngular, projectName, configFilePath) {
141
+ function buildTarget(namedInputs, outputs, projectRoot, frameworkIsAngular, browserTarget, configFilePath, hasTestRunner) {
108
142
  let targetConfig;
109
143
  if (frameworkIsAngular) {
110
144
  targetConfig = {
111
145
  executor: '@storybook/angular:build-storybook',
112
146
  options: {
113
147
  configDir: `${(0, path_1.dirname)(configFilePath)}`,
114
- browserTarget: `${projectName}:build-storybook`,
148
+ browserTarget,
115
149
  compodoc: false,
116
150
  outputDir: (0, devkit_1.joinPathFragments)(projectRoot, 'storybook-static'),
117
151
  },
@@ -125,9 +159,7 @@ function buildTarget(namedInputs, outputs, projectRoot, frameworkIsAngular, proj
125
159
  externalDependencies: [
126
160
  'storybook',
127
161
  '@storybook/angular',
128
- isStorybookTestRunnerInstalled()
129
- ? '@storybook/test-runner'
130
- : undefined,
162
+ hasTestRunner ? '@storybook/test-runner' : undefined,
131
163
  ].filter(Boolean),
132
164
  },
133
165
  ],
@@ -146,9 +178,7 @@ function buildTarget(namedInputs, outputs, projectRoot, frameworkIsAngular, proj
146
178
  {
147
179
  externalDependencies: [
148
180
  'storybook',
149
- isStorybookTestRunnerInstalled()
150
- ? '@storybook/test-runner'
151
- : undefined,
181
+ hasTestRunner ? '@storybook/test-runner' : undefined,
152
182
  ].filter(Boolean),
153
183
  },
154
184
  ],
@@ -156,15 +186,18 @@ function buildTarget(namedInputs, outputs, projectRoot, frameworkIsAngular, proj
156
186
  }
157
187
  return targetConfig;
158
188
  }
159
- function serveTarget(projectRoot, frameworkIsAngular, projectName, configFilePath) {
189
+ function serveTarget(projectRoot, frameworkIsAngular, browserTarget, configFilePath) {
160
190
  if (frameworkIsAngular) {
161
191
  return {
162
192
  continuous: true,
163
193
  executor: '@storybook/angular:start-storybook',
164
194
  options: {
165
195
  configDir: `${(0, path_1.dirname)(configFilePath)}`,
166
- browserTarget: `${projectName}:build-storybook`,
196
+ browserTarget,
167
197
  compodoc: false,
198
+ // The builder defaults to 9009, but test-storybook looks for 6006. Match
199
+ // the port the non-Angular `storybook dev` target serves on.
200
+ port: 6006,
168
201
  },
169
202
  };
170
203
  }
@@ -188,6 +221,24 @@ function testTarget(projectRoot) {
188
221
  };
189
222
  return targetConfig;
190
223
  }
224
+ function vitestTestTarget(projectRoot) {
225
+ const targetConfig = {
226
+ // `--passWithNoTests` so the target is green before any stories exist. It does
227
+ // not mask a missing `storybook` project: that is a startup error either way.
228
+ command: `vitest run --project=storybook --passWithNoTests`,
229
+ options: { cwd: projectRoot },
230
+ inputs: [
231
+ {
232
+ externalDependencies: [
233
+ 'storybook',
234
+ '@storybook/addon-vitest',
235
+ 'vitest',
236
+ ],
237
+ },
238
+ ],
239
+ };
240
+ return targetConfig;
241
+ }
191
242
  function serveStaticTarget(options, projectRoot) {
192
243
  const targetConfig = {
193
244
  dependsOn: [`${options.buildStorybookTargetName}`],
@@ -208,10 +259,11 @@ async function getStorybookFramework(configFilePath, context) {
208
259
  return parseFrameworkName(mainTsJs);
209
260
  }
210
261
  const storybookConfigImportPackage = (0, tsquery_1.query)(importDeclarations, 'StringLiteral')?.[0];
211
- if (storybookConfigImportPackage?.getText() === `'@storybook/core-common'`) {
262
+ const frameworkPackage = stringLiteralValue(storybookConfigImportPackage);
263
+ if (frameworkPackage === '@storybook/core-common') {
212
264
  return parseFrameworkName(mainTsJs);
213
265
  }
214
- return storybookConfigImportPackage?.getText();
266
+ return frameworkPackage;
215
267
  }
216
268
  function parseFrameworkName(mainTsJs) {
217
269
  const frameworkPropertyAssignment = (0, tsquery_1.query)(mainTsJs, `PropertyAssignment:has(Identifier:has([text="framework"]))`)?.[0];
@@ -224,9 +276,13 @@ function parseFrameworkName(mainTsJs) {
224
276
  });
225
277
  if (!namePropertyAssignment) {
226
278
  const storybookConfigImportPackage = (0, tsquery_1.query)(frameworkPropertyAssignment, 'StringLiteral')?.[0];
227
- return storybookConfigImportPackage?.getText();
279
+ return stringLiteralValue(storybookConfigImportPackage);
228
280
  }
229
- return (0, tsquery_1.query)(namePropertyAssignment, `StringLiteral`)?.[0]?.getText();
281
+ return stringLiteralValue((0, tsquery_1.query)(namePropertyAssignment, `StringLiteral`)?.[0]);
282
+ }
283
+ function stringLiteralValue(node) {
284
+ const value = node?.getText();
285
+ return value?.slice(1, -1);
230
286
  }
231
287
  async function getStorybookFullyResolvedFramework(configFilePath, context) {
232
288
  const resolvedPath = (0, path_1.join)(context.workspaceRoot, configFilePath);
@@ -274,26 +330,73 @@ function normalizeOptions(options) {
274
330
  watchDepsTargetName: options.watchDepsTargetName ?? 'watch-deps',
275
331
  };
276
332
  }
277
- function buildProjectName(projectRoot, workspaceRoot) {
333
+ function getProjectMetadata(projectRoot, workspaceRoot) {
278
334
  const packageJsonPath = (0, path_1.join)(workspaceRoot, projectRoot, 'package.json');
279
335
  const projectJsonPath = (0, path_1.join)(workspaceRoot, projectRoot, 'project.json');
280
- let name;
336
+ let projectJsonName;
337
+ let projectJsonTargets = {};
338
+ let packageJsonName;
339
+ let packageJsonTargets = {};
281
340
  if ((0, fs_1.existsSync)(projectJsonPath)) {
282
341
  const projectJson = (0, devkit_1.parseJson)((0, fs_1.readFileSync)(projectJsonPath, 'utf-8'));
283
- name = projectJson.name;
342
+ projectJsonName = projectJson.name;
343
+ projectJsonTargets = projectJson.targets ?? {};
284
344
  }
285
- else if ((0, fs_1.existsSync)(packageJsonPath)) {
345
+ if ((0, fs_1.existsSync)(packageJsonPath)) {
286
346
  const packageJson = (0, devkit_1.parseJson)((0, fs_1.readFileSync)(packageJsonPath, 'utf-8'));
287
- name = packageJson.name;
347
+ packageJsonName = packageJson.name;
348
+ packageJsonTargets = packageJson.nx?.targets ?? {};
288
349
  }
289
- return name;
350
+ // Mirror how Nx itself names a project: project.json, then package.json, then
351
+ // the directory. Both files can carry targets, and project.json wins.
352
+ const targets = { ...packageJsonTargets, ...projectJsonTargets };
353
+ return {
354
+ projectName: projectJsonName ?? packageJsonName ?? nameFromRoot(projectRoot),
355
+ angularBuildTarget: (0, utilities_1.findStorybookAndBuildTargetsAndCompiler)(targets).ngBuildTarget,
356
+ };
357
+ }
358
+ // Same derivation as Nx's `toProjectName`. The workspace root has no directory
359
+ // segment to name it after, so leave it undefined and let the caller complain.
360
+ function nameFromRoot(projectRoot) {
361
+ const segment = projectRoot.split(/[\/\\]/g).pop();
362
+ return !segment || segment === '.' ? undefined : segment.toLowerCase();
363
+ }
364
+ async function registersVitestAddon(configFilePath, context) {
365
+ try {
366
+ const { addons } = await (0, internal_1.loadConfigFile)((0, path_1.join)(context.workspaceRoot, configFilePath));
367
+ return addons?.some(isVitestAddon) ?? false;
368
+ }
369
+ catch {
370
+ return false;
371
+ }
372
+ }
373
+ function isVitestAddon(addon) {
374
+ const addonName = typeof addon === 'string'
375
+ ? addon
376
+ : typeof addon === 'object' && addon !== null && 'name' in addon
377
+ ? addon.name
378
+ : undefined;
379
+ return (typeof addonName === 'string' &&
380
+ normalizePackageName(addonName) === '@storybook/addon-vitest');
381
+ }
382
+ function normalizePackageName(packageName) {
383
+ return packageName
384
+ ?.replace(/\\/g, '/')
385
+ .split('/')
386
+ .filter(Boolean)
387
+ .slice(-2)
388
+ .join('/');
290
389
  }
291
- function isStorybookTestRunnerInstalled() {
390
+ // Resolved from the project first so a runner declared in the project's own
391
+ // package.json counts, not just one hoisted to the workspace root.
392
+ function isInstalled(packageName, workspaceRoot, projectRoot) {
292
393
  try {
293
- require.resolve('@storybook/test-runner');
394
+ require.resolve(packageName, {
395
+ paths: [(0, path_1.join)(workspaceRoot, projectRoot), workspaceRoot],
396
+ });
294
397
  return true;
295
398
  }
296
- catch (e) {
399
+ catch {
297
400
  return false;
298
401
  }
299
402
  }
@@ -67,24 +67,21 @@ function findStorybookAndBuildTargetsAndCompiler(targets) {
67
67
  '@angular-devkit/build-angular:application',
68
68
  '@angular-devkit/build-angular:browser',
69
69
  '@angular-devkit/build-angular:browser-esbuild',
70
+ '@angular/build:application',
70
71
  ];
71
72
  for (const target in targets) {
72
73
  if (arrayOfBuilders.includes(targets[target].executor)) {
73
74
  if (targets[target].executor === '@angular-devkit/build-angular:browser' ||
74
75
  targets[target].executor ===
75
76
  '@angular-devkit/build-angular:browser-esbuild' ||
76
- targets[target].executor === '@angular-devkit/build-angular:application') {
77
+ targets[target].executor ===
78
+ '@angular-devkit/build-angular:application' ||
79
+ targets[target].executor === '@angular/build:application') {
77
80
  /**
78
- * Not looking for '@nx/angular:ng-packagr-lite' or any other
79
- * @nx/angular:* executors.
80
- * Only looking for '@angular-devkit/build-angular:browser'
81
- * because the '@nx/angular:ng-packagr-lite' executor
82
- * (and maybe the other custom executors)
83
- * does not support styles and extra options, so the user
84
- * will be forced to switch to build-storybook to add extra options.
85
- *
86
- * So we might as well use the build-storybook by default to
87
- * avoid any errors.
81
+ * Only the official Angular application/browser builders. The
82
+ * '@nx/angular:*' executors (ng-packagr-lite and friends) do not
83
+ * support styles and extra options, so a project on one of those is
84
+ * better served by build-storybook, which can carry those options.
88
85
  */
89
86
  returnObject.ngBuildTarget = target;
90
87
  }
@@ -8,8 +8,10 @@ export declare const storybookVersion = "^10.5.0";
8
8
  export declare const reactVersion = "^18.2.0";
9
9
  export declare const viteVersion = "^6.0.0";
10
10
  export declare const coreJsVersion = "^3.36.1";
11
+ export declare const testRunnerVersion = "^0.24.0";
11
12
  type StorybookVersions = {
12
13
  storybookVersion: string;
14
+ testRunnerVersion: string;
13
15
  };
14
16
  export declare function versions(tree: Tree): StorybookVersions;
15
17
  export declare function storybookMajorVersion(tree?: Tree): number | undefined;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.coreJsVersion = exports.viteVersion = exports.reactVersion = exports.storybookVersion = exports.minSupportedStorybookVersion = exports.tsLibVersion = exports.tsNodeVersion = exports.litVersion = exports.nxVersion = void 0;
3
+ exports.testRunnerVersion = exports.coreJsVersion = exports.viteVersion = exports.reactVersion = exports.storybookVersion = exports.minSupportedStorybookVersion = exports.tsLibVersion = exports.tsNodeVersion = exports.litVersion = exports.nxVersion = void 0;
4
4
  exports.versions = versions;
5
5
  exports.storybookMajorVersion = storybookMajorVersion;
6
6
  exports.getInstalledStorybookVersion = getInstalledStorybookVersion;
@@ -17,20 +17,41 @@ exports.storybookVersion = '^10.5.0';
17
17
  exports.reactVersion = '^18.2.0';
18
18
  exports.viteVersion = '^6.0.0';
19
19
  exports.coreJsVersion = '^3.36.1';
20
+ exports.testRunnerVersion = '^0.24.0';
20
21
  const latestVersions = {
21
22
  storybookVersion: exports.storybookVersion,
23
+ testRunnerVersion: exports.testRunnerVersion,
22
24
  };
23
25
  const versionMap = {
24
- 8: { storybookVersion: '^8.6.11' },
25
- 9: { storybookVersion: '^9.0.5' },
26
- 10: { storybookVersion: '^10.5.0' },
26
+ 8: { storybookVersion: '^8.6.11', testRunnerVersion: '^0.21.0' },
27
+ 9: { storybookVersion: '^9.0.5', testRunnerVersion: '^0.23.0' },
28
+ 10: { storybookVersion: '^10.5.0', testRunnerVersion: exports.testRunnerVersion },
27
29
  };
28
30
  function versions(tree) {
29
31
  const installedStorybookMajor = storybookMajorVersion(tree);
30
32
  if (installedStorybookMajor === undefined) {
31
33
  return latestVersions;
32
34
  }
33
- return (versionMap[installedStorybookMajor] ?? latestVersions);
35
+ const compat = versionMap[installedStorybookMajor] ?? latestVersions;
36
+ // 0.20 is the first runner line to declare a storybook peer, and it peers ^8.2.
37
+ // Ranges that cannot reach 8.2 need 0.17, the earliest line built against 8.
38
+ if (installedStorybookMajor === 8 &&
39
+ !storybookVersionIntersects(tree, '>=8.2.0')) {
40
+ return { ...compat, testRunnerVersion: '^0.17.0' };
41
+ }
42
+ return compat;
43
+ }
44
+ function storybookVersionIntersects(tree, range) {
45
+ const installedVersion = getInstalledStorybookVersion(tree);
46
+ if (!installedVersion) {
47
+ return false;
48
+ }
49
+ try {
50
+ return (0, semver_1.intersects)(installedVersion, range);
51
+ }
52
+ catch {
53
+ return false;
54
+ }
34
55
  }
35
56
  function storybookMajorVersion(tree) {
36
57
  const installedVersion = getInstalledStorybookVersion(tree);
package/migrations.json CHANGED
@@ -435,6 +435,19 @@
435
435
  "alwaysAddToPackageJson": false
436
436
  }
437
437
  }
438
+ },
439
+ "23.2.0-test-runner": {
440
+ "version": "23.2.0-beta.8",
441
+ "requires": {
442
+ "storybook": ">=10.0.0 <11.0.0",
443
+ "@storybook/test-runner": "<0.24.0"
444
+ },
445
+ "packages": {
446
+ "@storybook/test-runner": {
447
+ "version": "^0.24.0",
448
+ "alwaysAddToPackageJson": false
449
+ }
450
+ }
438
451
  }
439
452
  }
440
453
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/storybook",
3
- "version": "23.2.0-beta.7",
3
+ "version": "23.2.0-beta.9",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "files": [
@@ -92,18 +92,18 @@
92
92
  "@phenomnomnominal/tsquery": "~6.2.0",
93
93
  "semver": "^7.6.3",
94
94
  "tslib": "^2.3.0",
95
- "@nx/devkit": "23.2.0-beta.7",
96
- "@nx/cypress": "23.2.0-beta.7",
97
- "@nx/js": "23.2.0-beta.7",
98
- "@nx/eslint": "23.2.0-beta.7"
95
+ "@nx/devkit": "23.2.0-beta.9",
96
+ "@nx/cypress": "23.2.0-beta.9",
97
+ "@nx/js": "23.2.0-beta.9",
98
+ "@nx/eslint": "23.2.0-beta.9"
99
99
  },
100
100
  "devDependencies": {
101
101
  "storybook": "9.0.6",
102
- "nx": "23.2.0-beta.7"
102
+ "nx": "23.2.0-beta.9"
103
103
  },
104
104
  "peerDependencies": {
105
105
  "storybook": ">=8.0.0 <11.0.0",
106
- "@nx/web": "23.2.0-beta.7"
106
+ "@nx/web": "23.2.0-beta.9"
107
107
  },
108
108
  "peerDependenciesMeta": {
109
109
  "@nx/web": {