From d04eed306ecb2914dc6b61916d76dea29cf89066 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 1 Dec 2022 11:39:51 +0100 Subject: [PATCH] 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} | 10 +++- .../server/framework-preset-angular-cli.ts | 4 +- ...torybook-normalize-angular-entry-plugin.js | 49 +++++++++++++++++++ lib/channels/src/index.ts | 2 +- lib/router/src/utils.ts | 12 +++-- 6 files changed, 69 insertions(+), 8 deletions(-) rename app/angular/src/server/{angular-cli-webpack-13.x.x.d.ts => angular-cli-webpack.d.ts} (100%) rename app/angular/src/server/{angular-cli-webpack-13.x.x.js => angular-cli-webpack.js} (91%) create mode 100644 app/angular/src/server/plugins/storybook-normalize-angular-entry-plugin.js diff --git a/app/angular/src/server/angular-cli-webpack-13.x.x.d.ts b/app/angular/src/server/angular-cli-webpack.d.ts similarity index 100% rename from app/angular/src/server/angular-cli-webpack-13.x.x.d.ts rename to app/angular/src/server/angular-cli-webpack.d.ts diff --git a/app/angular/src/server/angular-cli-webpack-13.x.x.js b/app/angular/src/server/angular-cli-webpack.js similarity index 91% rename from app/angular/src/server/angular-cli-webpack-13.x.x.js rename to app/angular/src/server/angular-cli-webpack.js index c8c87041cf20..feb510df501f 100644 --- a/app/angular/src/server/angular-cli-webpack-13.x.x.js +++ b/app/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: StorybookNormalizeAngularEntryPlugin, +} = require('./plugins/storybook-normalize-angular-entry-plugin'); /** * Extract webpack config from angular-cli 13.x.x @@ -66,7 +68,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 StorybookNormalizeAngularEntryPlugin(), + ]; const resolve = { ...baseConfig.resolve, diff --git a/app/angular/src/server/framework-preset-angular-cli.ts b/app/angular/src/server/framework-preset-angular-cli.ts index 2d7d6da3eb1c..2fca1201c2e3 100644 --- a/app/angular/src/server/framework-preset-angular-cli.ts +++ b/app/angular/src/server/framework-preset-angular-cli.ts @@ -8,7 +8,7 @@ import dedent from 'ts-dedent'; import { logging, JsonObject } from '@angular-devkit/core'; import { moduleIsAvailable } from './utils/module-is-available'; import { getWebpackConfig as getWebpackConfig12_2_x } from './angular-cli-webpack-12.2.x'; -import { getWebpackConfig as getWebpackConfig13_x_x } from './angular-cli-webpack-13.x.x'; +import { getWebpackConfig as getCustomWebpackConfig } from './angular-cli-webpack'; import { getWebpackConfig as getWebpackConfigOlder } from './angular-cli-webpack-older'; import { PresetOptions } from './options'; import { @@ -44,7 +44,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/app/angular/src/server/plugins/storybook-normalize-angular-entry-plugin.js b/app/angular/src/server/plugins/storybook-normalize-angular-entry-plugin.js new file mode 100644 index 000000000000..a390d0e4b938 --- /dev/null +++ b/app/angular/src/server/plugins/storybook-normalize-angular-entry-plugin.js @@ -0,0 +1,49 @@ +const PLUGIN_NAME = 'storybook-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 StorybookNormalizeAngularEntryPlugin { + 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/lib/channels/src/index.ts b/lib/channels/src/index.ts index dde99806d09e..3dcd1fd602d5 100644 --- a/lib/channels/src/index.ts +++ b/lib/channels/src/index.ts @@ -41,7 +41,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/lib/router/src/utils.ts b/lib/router/src/utils.ts index ed84228bfa98..f4972eff13c3 100644 --- a/lib/router/src/utils.ts +++ b/lib/router/src/utils.ts @@ -81,8 +81,14 @@ const validateArgs = (key = '', value: unknown): boolean => { COLOR_REGEXP.test(value) ); } - 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 (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; }; @@ -96,7 +102,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) }), {} );