From 1c50c2be165ffc920729b0511137c0a075687031 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 23 Nov 2022 13:03:31 +0100 Subject: [PATCH 1/6] Fix Angular 15 incompatibility --- ...k-13.x.x.d.ts => angular-cli-webpack.d.ts} | 0 ...bpack-13.x.x.js => angular-cli-webpack.js} | 16 +++--- .../server/framework-preset-angular-cli.ts | 4 +- .../plugins/normalize-angular-entry-plugin.js | 49 +++++++++++++++++++ code/lib/channels/src/index.ts | 2 +- code/lib/router/src/utils.ts | 5 +- 6 files changed, 64 insertions(+), 12 deletions(-) rename code/frameworks/angular/src/server/{angular-cli-webpack-13.x.x.d.ts => angular-cli-webpack.d.ts} (100%) rename code/frameworks/angular/src/server/{angular-cli-webpack-13.x.x.js => angular-cli-webpack.js} (89%) create mode 100644 code/frameworks/angular/src/server/plugins/normalize-angular-entry-plugin.js diff --git a/code/frameworks/angular/src/server/angular-cli-webpack-13.x.x.d.ts b/code/frameworks/angular/src/server/angular-cli-webpack.d.ts similarity index 100% rename from code/frameworks/angular/src/server/angular-cli-webpack-13.x.x.d.ts rename to code/frameworks/angular/src/server/angular-cli-webpack.d.ts diff --git a/code/frameworks/angular/src/server/angular-cli-webpack-13.x.x.js b/code/frameworks/angular/src/server/angular-cli-webpack.js similarity index 89% rename from code/frameworks/angular/src/server/angular-cli-webpack-13.x.x.js rename to code/frameworks/angular/src/server/angular-cli-webpack.js index c8c87041cf20..b28487635f70 100644 --- a/code/frameworks/angular/src/server/angular-cli-webpack-13.x.x.js +++ b/code/frameworks/angular/src/server/angular-cli-webpack.js @@ -9,8 +9,10 @@ const { getTypeScriptConfig, } = require('@angular-devkit/build-angular/src/webpack/configs'); const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); - const { filterOutStylingRules } = require('./utils/filter-out-styling-rules'); +const { + default: NormalizeAngularEntryPlugin, +} = require('./plugins/normalize-angular-entry-plugin'); /** * Extract webpack config from angular-cli 13.x.x @@ -52,11 +54,7 @@ exports.getWebpackConfig = async (baseConfig, { builderOptions, builderContext } /** * Merge baseConfig Webpack with angular-cli Webpack */ - const entry = [ - ...baseConfig.entry, - ...(cliConfig.entry.styles ?? []), - ...(cliConfig.entry.polyfills ?? []), - ]; + const entry = [...baseConfig.entry, ...(cliConfig.entry.polyfills ?? [])]; // Don't use storybooks styling rules because we have to use rules created by @angular-devkit/build-angular // because @angular-devkit/build-angular created rules have include/exclude for global style files. @@ -66,7 +64,11 @@ exports.getWebpackConfig = async (baseConfig, { builderOptions, builderContext } rules: [...cliConfig.module.rules, ...rulesExcludingStyles], }; - const plugins = [...(cliConfig.plugins ?? []), ...baseConfig.plugins]; + const plugins = [ + ...(cliConfig.plugins ?? []), + ...baseConfig.plugins, + new NormalizeAngularEntryPlugin(), + ]; const resolve = { ...baseConfig.resolve, diff --git a/code/frameworks/angular/src/server/framework-preset-angular-cli.ts b/code/frameworks/angular/src/server/framework-preset-angular-cli.ts index fcc4a17aea81..30fb420b0d0f 100644 --- a/code/frameworks/angular/src/server/framework-preset-angular-cli.ts +++ b/code/frameworks/angular/src/server/framework-preset-angular-cli.ts @@ -7,8 +7,8 @@ import semver from 'semver'; import { dedent } from 'ts-dedent'; import { JsonObject, logging } from '@angular-devkit/core'; +import { getWebpackConfig as getCustomWebpackConfig } from './angular-cli-webpack'; import { moduleIsAvailable } from './utils/module-is-available'; -import { getWebpackConfig as getWebpackConfig13_x_x } from './angular-cli-webpack-13.x.x'; import { PresetOptions } from './preset-options'; import { getDefaultProjectName, @@ -43,7 +43,7 @@ export async function webpackFinal(baseConfig: webpack.Configuration, options: P const builderOptions = await getBuilderOptions(_options, builderContext); const legacyDefaultOptions = await getLegacyDefaultBuildOptions(_options); - return getWebpackConfig13_x_x(_baseConfig, { + return getCustomWebpackConfig(_baseConfig, { builderOptions: { watch: options.configType === 'DEVELOPMENT', ...legacyDefaultOptions, diff --git a/code/frameworks/angular/src/server/plugins/normalize-angular-entry-plugin.js b/code/frameworks/angular/src/server/plugins/normalize-angular-entry-plugin.js new file mode 100644 index 000000000000..d95fa70505a6 --- /dev/null +++ b/code/frameworks/angular/src/server/plugins/normalize-angular-entry-plugin.js @@ -0,0 +1,49 @@ +const PLUGIN_NAME = 'normalize-angular-entry-plugin'; + +/** + * Angular's webpack plugin @angular-devkit/build-angular/src/webpack/plugins/styles-webpack-plugin.js + * transforms the original webpackOptions.entry point array into a structure like this: + * + * ```js + * { + * main: { + * import: [...] + * }, + * + * styles: { + * import: [...] + * }, + * } + * ``` + * + * Storybook throws an __webpack_require__.nmd is not a function error, when another runtime bundle (styles~runtime.iframe.bundle.js) is loaded. + * To prevent this error, we have to normalize the entry point to only generate one runtime bundle (main~runtime.iframe.bundle.js). + */ +export default class NormalizeAngularEntryPlugin { + constructor(options) { + this.options = options; + } + + apply(compiler) { + const webpackOptions = compiler.options; + const entry = + typeof webpackOptions.entry === 'function' ? webpackOptions.entry() : webpackOptions.entry; + + webpackOptions.entry = async () => { + const entryResult = await entry; + + if (entryResult.main && entryResult.styles) { + return { + main: { + import: Array.from(new Set([...entryResult.main.import, ...entryResult.styles.import])), + }, + }; + } + + return entry; + }; + compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => { + this.compilation = compilation; + }); + } +} diff --git a/code/lib/channels/src/index.ts b/code/lib/channels/src/index.ts index 99a90a5a9e75..9c7425690f05 100644 --- a/code/lib/channels/src/index.ts +++ b/code/lib/channels/src/index.ts @@ -40,7 +40,7 @@ export class Channel { private data: Record = {}; - private readonly transport: ChannelTransport; + private readonly transport: ChannelTransport | undefined = undefined; constructor({ transport, async = false }: ChannelArgs = {}) { this.isAsync = async; diff --git a/code/lib/router/src/utils.ts b/code/lib/router/src/utils.ts index e4c5f0148790..b2bed7aae47f 100644 --- a/code/lib/router/src/utils.ts +++ b/code/lib/router/src/utils.ts @@ -83,7 +83,8 @@ const validateArgs = (key = '', value: unknown): boolean => { ); } if (Array.isArray(value)) return value.every((v) => validateArgs(key, v)); - if (isPlainObject(value)) return Object.entries(value).every(([k, v]) => validateArgs(k, v)); + if (isPlainObject(value)) + return Object.entries(value as Record).every(([k, v]) => validateArgs(k, v)); return false; }; @@ -97,7 +98,7 @@ const encodeSpecialValues = (value: unknown): any => { } if (Array.isArray(value)) return value.map(encodeSpecialValues); if (isPlainObject(value)) { - return Object.entries(value).reduce( + return Object.entries(value as Record).reduce( (acc, [key, val]) => Object.assign(acc, { [key]: encodeSpecialValues(val) }), {} ); From b654726c15c8778d85bd6d9d3e7e5c772783bc68 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 23 Nov 2022 13:03:57 +0100 Subject: [PATCH 2/6] Run Angular 15 tests on CI/CD pipeline --- .circleci/config.yml | 20 ++++++++++---------- code/lib/cli/src/repro-templates.ts | 5 ++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f6674fc9c46c..51ec2480f630 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -689,23 +689,23 @@ workflows: requires: - build - create-sandboxes: - parallelism: 15 + parallelism: 16 requires: - build - build-sandboxes: - parallelism: 15 + parallelism: 16 requires: - create-sandboxes - test-runner-sandboxes: - parallelism: 14 + parallelism: 16 requires: - build-sandboxes - chromatic-sandboxes: - parallelism: 15 + parallelism: 16 requires: - build-sandboxes - e2e-sandboxes: - parallelism: 15 + parallelism: 16 requires: - build-sandboxes daily: @@ -714,25 +714,25 @@ workflows: jobs: - build - create-sandboxes: - parallelism: 25 + parallelism: 26 requires: - build # - smoke-test-sandboxes: # disabled for now # requires: # - create-sandboxes - build-sandboxes: - parallelism: 25 + parallelism: 26 requires: - create-sandboxes - test-runner-sandboxes: - parallelism: 24 + parallelism: 26 requires: - build-sandboxes - chromatic-sandboxes: - parallelism: 25 + parallelism: 26 requires: - build-sandboxes - e2e-sandboxes: - parallelism: 25 + parallelism: 26 requires: - build-sandboxes diff --git a/code/lib/cli/src/repro-templates.ts b/code/lib/cli/src/repro-templates.ts index aad239b4d0fe..c678fb0bb719 100644 --- a/code/lib/cli/src/repro-templates.ts +++ b/code/lib/cli/src/repro-templates.ts @@ -141,7 +141,6 @@ export const allTemplates = { name: 'Angular CLI (latest)', script: 'npx -p @angular/cli ng new angular-latest --directory . --routing=true --minimal=true --style=scss --strict --skip-git --skip-install --package-manager=yarn', - skipTasks: ['test-runner'], expected: { framework: '@storybook/angular', renderer: '@storybook/angular', @@ -262,8 +261,7 @@ type TemplateKey = keyof typeof allTemplates; export const ci: TemplateKey[] = ['cra/default-ts', 'react-vite/default-ts']; export const pr: TemplateKey[] = [ ...ci, - // TODO: swap with 'angular-cli/default-ts' once angular 15 fully works with Storybook - 'angular-cli/14-ts', + 'angular-cli/default-ts', 'vue3-vite/default-ts', 'vue-cli/vue2-default-js', 'lit-vite/default-ts', @@ -276,6 +274,7 @@ export const merged: TemplateKey[] = [ 'react-webpack/18-ts', 'react-webpack/17-ts', 'angular-cli/default-ts', + 'angular-cli/14-ts', 'angular-cli/13-ts', 'preact-webpack5/default-ts', 'html-webpack/default', From 13e54db1992028c93ab0aae6f52a3c380ada8d95 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 23 Nov 2022 13:21:08 +0100 Subject: [PATCH 3/6] Readd styles entry --- code/frameworks/angular/src/server/angular-cli-webpack.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/frameworks/angular/src/server/angular-cli-webpack.js b/code/frameworks/angular/src/server/angular-cli-webpack.js index b28487635f70..0710cdda3aa9 100644 --- a/code/frameworks/angular/src/server/angular-cli-webpack.js +++ b/code/frameworks/angular/src/server/angular-cli-webpack.js @@ -54,7 +54,11 @@ exports.getWebpackConfig = async (baseConfig, { builderOptions, builderContext } /** * Merge baseConfig Webpack with angular-cli Webpack */ - const entry = [...baseConfig.entry, ...(cliConfig.entry.polyfills ?? [])]; + const entry = [ + ...baseConfig.entry, + ...(cliConfig.entry.styles ?? []), + ...(cliConfig.entry.polyfills ?? []), + ]; // Don't use storybooks styling rules because we have to use rules created by @angular-devkit/build-angular // because @angular-devkit/build-angular created rules have include/exclude for global style files. From 3516f140be9419181e10398f5218697cb041258d Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 23 Nov 2022 13:33:43 +0100 Subject: [PATCH 4/6] Fix lint issue --- code/frameworks/angular/src/server/angular-cli-webpack.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/frameworks/angular/src/server/angular-cli-webpack.js b/code/frameworks/angular/src/server/angular-cli-webpack.js index 0710cdda3aa9..5bf584d0ee47 100644 --- a/code/frameworks/angular/src/server/angular-cli-webpack.js +++ b/code/frameworks/angular/src/server/angular-cli-webpack.js @@ -54,7 +54,7 @@ exports.getWebpackConfig = async (baseConfig, { builderOptions, builderContext } /** * Merge baseConfig Webpack with angular-cli Webpack */ - const entry = [ + const entry = [ ...baseConfig.entry, ...(cliConfig.entry.styles ?? []), ...(cliConfig.entry.polyfills ?? []), From e20eb29a0bed276279dc513451cfa1cf5c98ef79 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 23 Nov 2022 13:57:51 +0100 Subject: [PATCH 5/6] Fix CI/CD --- .circleci/config.yml | 72 ++++++++++++++--------------- code/lib/cli/src/repro-templates.ts | 1 - 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 51ec2480f630..4f909bda35a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,8 +4,8 @@ parameters: workflow: description: Which workflow to run type: enum - enum: ['ci', 'pr', 'merged', 'daily'] - default: 'ci' + enum: ["ci", "pr", "merged", "daily"] + default: "ci" executors: sb_node_16_classic: @@ -13,8 +13,8 @@ executors: class: description: The Resource class type: enum - enum: ['small', 'medium', 'medium+', 'large', 'xlarge'] - default: 'small' + enum: ["small", "medium", "medium+", "large", "xlarge"] + default: "small" working_directory: /tmp/storybook docker: - image: cimg/node:16.17.1 @@ -26,8 +26,8 @@ executors: class: description: The Resource class type: enum - enum: ['small', 'medium', 'medium+', 'large', 'xlarge'] - default: 'small' + enum: ["small", "medium", "medium+", "large", "xlarge"] + default: "small" working_directory: /tmp/storybook docker: - image: cimg/node:16.17.1-browsers @@ -39,8 +39,8 @@ executors: class: description: The Resource class type: enum - enum: ['small', 'medium', 'medium+', 'large', 'xlarge'] - default: 'small' + enum: ["small", "medium", "medium+", "large", "xlarge"] + default: "small" working_directory: /tmp/storybook docker: - image: mcr.microsoft.com/playwright:v1.28.0-focal @@ -167,7 +167,7 @@ commands: git reset --hard "$CIRCLE_SHA1" name: Checkout code shallow cancel-workflow-on-failure: - description: 'Cancels the entire workflow in case the previous step has failed' + description: "Cancels the entire workflow in case the previous step has failed" steps: - run: name: Cancel current workflow @@ -177,13 +177,13 @@ commands: echo "To execute all checks locally, please run yarn ci-tests" curl -X POST --header "Content-Type: application/json" "https://circleci.com/api/v2/workflow/${CIRCLE_WORKFLOW_ID}/cancel?circle-token=${WORKFLOW_CANCELER}" report-workflow-on-failure: - description: 'Reports failures to discord' + description: "Reports failures to discord" parameters: template: description: | Which template to report in discord. Applicable for parallel sandbox jobs type: string - default: 'none' + default: "none" steps: - run: when: on_fail @@ -199,7 +199,7 @@ jobs: name: sb_node_16_classic steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - run: name: Prettier command: | @@ -212,7 +212,7 @@ jobs: name: sb_node_16_classic steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - restore_cache: name: Restore Yarn cache keys: @@ -254,7 +254,7 @@ jobs: working_directory: /tmp/storybook steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -295,7 +295,7 @@ jobs: working_directory: /tmp/storybook steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -335,7 +335,7 @@ jobs: name: sb_node_16_classic steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -351,7 +351,7 @@ jobs: name: sb_node_16_classic steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -365,7 +365,7 @@ jobs: executor: sb_node_16_browsers steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -383,7 +383,7 @@ jobs: name: sb_node_16_browsers steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -405,7 +405,7 @@ jobs: name: sb_node_16_browsers steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -419,7 +419,7 @@ jobs: class: medium+ name: sb_node_16_browsers environment: - NODE_OPTIONS: --max_old_space_size=6144 + NODE_OPTIONS: --max_old_space_size=6144 steps: # switched this to the CircleCI helper to get the full git history for TurboSnap - checkout @@ -446,7 +446,7 @@ jobs: parallelism: << parameters.parallelism >> steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -471,7 +471,7 @@ jobs: parallelism: << parameters.parallelism >> steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -492,7 +492,7 @@ jobs: parallelism: << parameters.parallelism >> steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -517,7 +517,7 @@ jobs: parallelism: << parameters.parallelism >> steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -558,7 +558,7 @@ jobs: parallelism: << parameters.parallelism >> steps: - checkout_advanced: - clone_options: '--depth 1 --verbose' + clone_options: "--depth 1 --verbose" - attach_workspace: at: . - run: @@ -689,23 +689,23 @@ workflows: requires: - build - create-sandboxes: - parallelism: 16 + parallelism: 15 requires: - build - build-sandboxes: - parallelism: 16 + parallelism: 15 requires: - create-sandboxes - test-runner-sandboxes: - parallelism: 16 + parallelism: 15 requires: - build-sandboxes - chromatic-sandboxes: - parallelism: 16 + parallelism: 15 requires: - build-sandboxes - e2e-sandboxes: - parallelism: 16 + parallelism: 15 requires: - build-sandboxes daily: @@ -714,25 +714,25 @@ workflows: jobs: - build - create-sandboxes: - parallelism: 26 + parallelism: 25 requires: - build # - smoke-test-sandboxes: # disabled for now # requires: # - create-sandboxes - build-sandboxes: - parallelism: 26 + parallelism: 25 requires: - create-sandboxes - test-runner-sandboxes: - parallelism: 26 + parallelism: 25 requires: - build-sandboxes - chromatic-sandboxes: - parallelism: 26 + parallelism: 25 requires: - build-sandboxes - e2e-sandboxes: - parallelism: 26 + parallelism: 25 requires: - build-sandboxes diff --git a/code/lib/cli/src/repro-templates.ts b/code/lib/cli/src/repro-templates.ts index c678fb0bb719..ec243a090c2d 100644 --- a/code/lib/cli/src/repro-templates.ts +++ b/code/lib/cli/src/repro-templates.ts @@ -273,7 +273,6 @@ export const merged: TemplateKey[] = [ ...pr, 'react-webpack/18-ts', 'react-webpack/17-ts', - 'angular-cli/default-ts', 'angular-cli/14-ts', 'angular-cli/13-ts', 'preact-webpack5/default-ts', From a2df68dbb7a5ef73cd3dcf3842847afbacb98f3c Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 23 Nov 2022 16:51:23 +0100 Subject: [PATCH 6/6] Apply in review requested changes --- code/frameworks/angular/src/server/angular-cli-webpack.js | 6 +++--- ...gin.js => storybook-normalize-angular-entry-plugin.js} | 4 ++-- code/lib/router/src/utils.ts | 8 ++++++-- 3 files changed, 11 insertions(+), 7 deletions(-) rename code/frameworks/angular/src/server/plugins/{normalize-angular-entry-plugin.js => storybook-normalize-angular-entry-plugin.js} (91%) diff --git a/code/frameworks/angular/src/server/angular-cli-webpack.js b/code/frameworks/angular/src/server/angular-cli-webpack.js index 5bf584d0ee47..feb510df501f 100644 --- a/code/frameworks/angular/src/server/angular-cli-webpack.js +++ b/code/frameworks/angular/src/server/angular-cli-webpack.js @@ -11,8 +11,8 @@ const { const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); const { filterOutStylingRules } = require('./utils/filter-out-styling-rules'); const { - default: NormalizeAngularEntryPlugin, -} = require('./plugins/normalize-angular-entry-plugin'); + default: StorybookNormalizeAngularEntryPlugin, +} = require('./plugins/storybook-normalize-angular-entry-plugin'); /** * Extract webpack config from angular-cli 13.x.x @@ -71,7 +71,7 @@ exports.getWebpackConfig = async (baseConfig, { builderOptions, builderContext } const plugins = [ ...(cliConfig.plugins ?? []), ...baseConfig.plugins, - new NormalizeAngularEntryPlugin(), + new StorybookNormalizeAngularEntryPlugin(), ]; const resolve = { diff --git a/code/frameworks/angular/src/server/plugins/normalize-angular-entry-plugin.js b/code/frameworks/angular/src/server/plugins/storybook-normalize-angular-entry-plugin.js similarity index 91% rename from code/frameworks/angular/src/server/plugins/normalize-angular-entry-plugin.js rename to code/frameworks/angular/src/server/plugins/storybook-normalize-angular-entry-plugin.js index d95fa70505a6..a390d0e4b938 100644 --- a/code/frameworks/angular/src/server/plugins/normalize-angular-entry-plugin.js +++ b/code/frameworks/angular/src/server/plugins/storybook-normalize-angular-entry-plugin.js @@ -1,4 +1,4 @@ -const PLUGIN_NAME = 'normalize-angular-entry-plugin'; +const PLUGIN_NAME = 'storybook-normalize-angular-entry-plugin'; /** * Angular's webpack plugin @angular-devkit/build-angular/src/webpack/plugins/styles-webpack-plugin.js @@ -19,7 +19,7 @@ const PLUGIN_NAME = 'normalize-angular-entry-plugin'; * Storybook throws an __webpack_require__.nmd is not a function error, when another runtime bundle (styles~runtime.iframe.bundle.js) is loaded. * To prevent this error, we have to normalize the entry point to only generate one runtime bundle (main~runtime.iframe.bundle.js). */ -export default class NormalizeAngularEntryPlugin { +export default class StorybookNormalizeAngularEntryPlugin { constructor(options) { this.options = options; } diff --git a/code/lib/router/src/utils.ts b/code/lib/router/src/utils.ts index b2bed7aae47f..916240c8e67f 100644 --- a/code/lib/router/src/utils.ts +++ b/code/lib/router/src/utils.ts @@ -82,9 +82,13 @@ const validateArgs = (key = '', value: unknown): boolean => { COLOR_REGEXP.test(value) ); } - if (Array.isArray(value)) return value.every((v) => validateArgs(key, v)); - if (isPlainObject(value)) + if (Array.isArray(value)) { + return value.every((v) => validateArgs(key, v)); + } + + if (isPlainObject(value)) { return Object.entries(value as Record).every(([k, v]) => validateArgs(k, v)); + } return false; };