From 2255ede945feba18f46c1a524ef23f9632c26d82 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 24 May 2024 16:00:56 +0200 Subject: [PATCH] Revert "Angular: Consider style inject option" This reverts commit cca91fcfc6ccd59b9931a3c4868ce7d32b147d2e. --- .../src/preview/iframe-webpack.config.ts | 9 +- .../src/builders/build-storybook/index.ts | 183 +++++++++------ .../src/builders/start-storybook/index.ts | 221 +++++++++++------- .../src/builders/utils/run-compodoc.spec.ts | 31 ++- .../src/builders/utils/run-compodoc.ts | 46 ++-- .../angular/src/builders/utils/setup.ts | 118 ---------- .../src/builders/utils/standalone-options.ts | 16 +- .../angular/src/server/angular-cli-webpack.js | 14 +- .../server/framework-preset-angular-cli.ts | 66 +++++- .../angular/src/server/preset-options.ts | 8 +- 10 files changed, 377 insertions(+), 335 deletions(-) delete mode 100644 code/frameworks/angular/src/builders/utils/setup.ts diff --git a/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts b/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts index 6eca9b6a3dff..ef510ef2378f 100644 --- a/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts +++ b/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts @@ -2,7 +2,6 @@ import { dirname, join, resolve } from 'path'; import { DefinePlugin, HotModuleReplacementPlugin, ProgressPlugin, ProvidePlugin } from 'webpack'; import type { Configuration } from 'webpack'; import HtmlWebpackPlugin from 'html-webpack-plugin'; - // @ts-expect-error (I removed this on purpose, because it's incorrect) import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin'; import TerserWebpackPlugin from 'terser-webpack-plugin'; @@ -54,11 +53,7 @@ const storybookPaths: Record = { }; export default async ( - options: Options & { - typescriptOptions: TypescriptOptions; - /* Build entries, which should not be linked in the iframe HTML file */ - excludeChunks?: string[]; - } + options: Options & { typescriptOptions: TypescriptOptions } ): Promise => { const { outputDir = join('.', 'public'), @@ -69,7 +64,6 @@ export default async ( previewUrl, typescriptOptions, features, - excludeChunks = [], } = options; const isProd = configType === 'PRODUCTION'; @@ -178,7 +172,6 @@ export default async ( alwaysWriteToDisk: true, inject: false, template, - excludeChunks, templateParameters: { version: packageJson.version, globals: { diff --git a/code/frameworks/angular/src/builders/build-storybook/index.ts b/code/frameworks/angular/src/builders/build-storybook/index.ts index 97cc2985488f..25f7faeb5268 100644 --- a/code/frameworks/angular/src/builders/build-storybook/index.ts +++ b/code/frameworks/angular/src/builders/build-storybook/index.ts @@ -1,8 +1,18 @@ -import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect'; +import { + BuilderContext, + BuilderHandlerFn, + BuilderOutput, + BuilderOutputLike, + Target, + createBuilder, + targetFromTargetString, +} from '@angular-devkit/architect'; import { JsonObject } from '@angular-devkit/core'; +import { from, of, throwError } from 'rxjs'; +import { catchError, map, mapTo, switchMap } from 'rxjs/operators'; import { sync as findUpSync } from 'find-up'; import { sync as readUpSync } from 'read-pkg-up'; -import { StylePreprocessorOptions } from '@angular-devkit/build-angular'; +import { BrowserBuilderOptions, StylePreprocessorOptions } from '@angular-devkit/build-angular'; import { CLIOptions } from '@storybook/types'; import { getEnvConfig, versions } from '@storybook/core-common'; @@ -12,13 +22,11 @@ import { buildStaticStandalone, withTelemetry } from '@storybook/core-server'; import { AssetPattern, SourceMapUnion, - StyleClass, StyleElement, } from '@angular-devkit/build-angular/src/builders/browser/schema'; import { StandaloneOptions } from '../utils/standalone-options'; import { runCompodoc } from '../utils/run-compodoc'; import { errorSummary, printErrorDetails } from '../utils/error-handler'; -import { setup } from '../utils/setup'; addToGlobalContext('cliVersion', versions.storybook); @@ -51,77 +59,112 @@ export type StorybookBuilderOptions = JsonObject & { export type StorybookBuilderOutput = JsonObject & BuilderOutput & { [key: string]: any }; -type StandaloneBuildOptions = StandaloneOptions & { outputDir: string; excludeChunks: string[] }; +type StandaloneBuildOptions = StandaloneOptions & { outputDir: string }; + +const commandBuilder: BuilderHandlerFn = ( + options, + context +): BuilderOutputLike => { + const builder = from(setup(options, context)).pipe( + switchMap(({ tsConfig }) => { + const docTSConfig = findUpSync('tsconfig.doc.json', { cwd: options.configDir }); + const runCompodoc$ = options.compodoc + ? runCompodoc( + { compodocArgs: options.compodocArgs, tsconfig: docTSConfig ?? tsConfig }, + context + ).pipe(mapTo({ tsConfig })) + : of({}); + + return runCompodoc$.pipe(mapTo({ tsConfig })); + }), + map(({ tsConfig }) => { + getEnvConfig(options, { + staticDir: 'SBCONFIG_STATIC_DIR', + outputDir: 'SBCONFIG_OUTPUT_DIR', + configDir: 'SBCONFIG_CONFIG_DIR', + }); + + const { + browserTarget, + stylePreprocessorOptions, + styles, + configDir, + docs, + loglevel, + test, + outputDir, + quiet, + enableProdMode = true, + webpackStatsJson, + statsJson, + debugWebpack, + disableTelemetry, + assets, + previewUrl, + sourceMap = false, + } = options; + + const standaloneOptions: StandaloneBuildOptions = { + packageJson: readUpSync({ cwd: __dirname }).packageJson, + configDir, + ...(docs ? { docs } : {}), + loglevel, + outputDir, + test, + quiet, + enableProdMode, + disableTelemetry, + angularBrowserTarget: browserTarget, + angularBuilderContext: context, + angularBuilderOptions: { + ...(stylePreprocessorOptions ? { stylePreprocessorOptions } : {}), + ...(styles ? { styles } : {}), + ...(assets ? { assets } : {}), + sourceMap, + }, + tsConfig, + webpackStatsJson, + statsJson, + debugWebpack, + previewUrl, + }; + + return standaloneOptions; + }), + switchMap((standaloneOptions) => runInstance({ ...standaloneOptions, mode: 'static' })), + map(() => { + return { success: true }; + }) + ); + + return builder as any as BuilderOutput; +}; -const commandBuilder = async ( - options: StorybookBuilderOptions, - context: BuilderContext -): Promise => { - const { tsConfig, angularBuilderContext, angularBuilderOptions } = await setup(options, context); +export default createBuilder(commandBuilder); - const docTSConfig = findUpSync('tsconfig.doc.json', { cwd: options.configDir }); +async function setup(options: StorybookBuilderOptions, context: BuilderContext) { + let browserOptions: (JsonObject & BrowserBuilderOptions) | undefined; + let browserTarget: Target | undefined; - if (options.compodoc) { - await runCompodoc( - { compodocArgs: options.compodocArgs, tsconfig: docTSConfig ?? tsConfig }, - context + if (options.browserTarget) { + browserTarget = targetFromTargetString(options.browserTarget); + browserOptions = await context.validateOptions( + await context.getTargetOptions(browserTarget), + await context.getBuilderNameForTarget(browserTarget) ); } - getEnvConfig(options, { - staticDir: 'SBCONFIG_STATIC_DIR', - outputDir: 'SBCONFIG_OUTPUT_DIR', - configDir: 'SBCONFIG_CONFIG_DIR', - }); - - const { - configDir, - docs, - loglevel, - test, - outputDir, - quiet, - enableProdMode = true, - webpackStatsJson, - statsJson, - debugWebpack, - disableTelemetry, - previewUrl, - } = options; - - const standaloneOptions: StandaloneBuildOptions = { - packageJson: readUpSync({ cwd: __dirname }).packageJson, - configDir, - ...(docs ? { docs } : {}), - excludeChunks: angularBuilderOptions.styles - ?.filter((style) => typeof style !== 'string' && style.inject === false) - .map((s: StyleClass) => s.bundleName), - loglevel, - outputDir, - test, - quiet, - enableProdMode, - disableTelemetry, - angularBrowserTarget: options.browserTarget, - angularBuilderContext, - angularBuilderOptions, - tsConfig, - webpackStatsJson, - statsJson, - debugWebpack, - previewUrl, + return { + tsConfig: + options.tsConfig ?? + findUpSync('tsconfig.json', { cwd: options.configDir }) ?? + browserOptions.tsConfig, }; +} - await runInstance({ ...standaloneOptions, mode: 'static' }); - - return { success: true }; -}; - -export default createBuilder(commandBuilder); - -async function runInstance(options: StandaloneBuildOptions) { - try { - await withTelemetry( +function runInstance(options: StandaloneBuildOptions) { + return from( + withTelemetry( 'build', { cliOptions: options, @@ -129,8 +172,6 @@ async function runInstance(options: StandaloneBuildOptions) { printError: printErrorDetails, }, () => buildStaticStandalone(options) - ); - } catch (error) { - throw new Error(errorSummary(error)); - } + ) + ).pipe(catchError((error: any) => throwError(errorSummary(error)))); } diff --git a/code/frameworks/angular/src/builders/start-storybook/index.ts b/code/frameworks/angular/src/builders/start-storybook/index.ts index cf33cd4b4e1c..2ecbb63c8a0f 100644 --- a/code/frameworks/angular/src/builders/start-storybook/index.ts +++ b/code/frameworks/angular/src/builders/start-storybook/index.ts @@ -1,6 +1,15 @@ -import { BuilderHandlerFn, BuilderOutput, createBuilder } from '@angular-devkit/architect'; +import { + BuilderContext, + BuilderHandlerFn, + BuilderOutput, + Target, + createBuilder, + targetFromTargetString, +} from '@angular-devkit/architect'; import { JsonObject } from '@angular-devkit/core'; -import { StylePreprocessorOptions } from '@angular-devkit/build-angular'; +import { BrowserBuilderOptions, StylePreprocessorOptions } from '@angular-devkit/build-angular'; +import { from, Observable, of } from 'rxjs'; +import { map, switchMap, mapTo } from 'rxjs/operators'; import { sync as findUpSync } from 'find-up'; import { sync as readUpSync } from 'read-pkg-up'; @@ -11,13 +20,11 @@ import { buildDevStandalone, withTelemetry } from '@storybook/core-server'; import { AssetPattern, SourceMapUnion, - StyleClass, StyleElement, } from '@angular-devkit/build-angular/src/builders/browser/schema'; import { StandaloneOptions } from '../utils/standalone-options'; import { runCompodoc } from '../utils/run-compodoc'; import { printErrorDetails, errorSummary } from '../utils/error-handler'; -import { setup } from '../utils/setup'; addToGlobalContext('cliVersion', versions.storybook); @@ -57,96 +64,131 @@ export type StorybookBuilderOptions = JsonObject & { export type StorybookBuilderOutput = JsonObject & BuilderOutput & {}; -const commandBuilder: BuilderHandlerFn = async (options, context) => { - const { tsConfig, angularBuilderContext, angularBuilderOptions } = await setup(options, context); +const commandBuilder: BuilderHandlerFn = (options, context) => { + const builder = from(setup(options, context)).pipe( + switchMap(({ tsConfig }) => { + const docTSConfig = findUpSync('tsconfig.doc.json', { cwd: options.configDir }); - const docTSConfig = findUpSync('tsconfig.doc.json', { cwd: options.configDir }); + const runCompodoc$ = options.compodoc + ? runCompodoc( + { + compodocArgs: [...options.compodocArgs, ...(options.quiet ? ['--silent'] : [])], + tsconfig: docTSConfig ?? tsConfig, + }, + context + ).pipe(mapTo({ tsConfig })) + : of({}); - if (options.compodoc) { - await runCompodoc( - { - compodocArgs: [...options.compodocArgs, ...(options.quiet ? ['--silent'] : [])], - tsconfig: docTSConfig ?? tsConfig, - }, - context - ); - } + return runCompodoc$.pipe(mapTo({ tsConfig })); + }), + map(({ tsConfig }) => { + getEnvConfig(options, { + port: 'SBCONFIG_PORT', + host: 'SBCONFIG_HOSTNAME', + staticDir: 'SBCONFIG_STATIC_DIR', + configDir: 'SBCONFIG_CONFIG_DIR', + ci: 'CI', + }); - getEnvConfig(options, { - port: 'SBCONFIG_PORT', - host: 'SBCONFIG_HOSTNAME', - staticDir: 'SBCONFIG_STATIC_DIR', - configDir: 'SBCONFIG_CONFIG_DIR', - ci: 'CI', - }); + options.port = parseInt(`${options.port}`, 10); - options.port = parseInt(`${options.port}`, 10); - - const { - browserTarget, - ci, - configDir, - docs, - host, - https, - port, - quiet, - enableProdMode = false, - smokeTest, - sslCa, - sslCert, - sslKey, - disableTelemetry, - initialPath, - open, - debugWebpack, - loglevel, - webpackStatsJson, - statsJson, - previewUrl, - } = options; - - const standaloneOptions: StandaloneOptions = { - packageJson: readUpSync({ cwd: __dirname }).packageJson, - ci, - configDir, - ...(docs ? { docs } : {}), - excludeChunks: angularBuilderOptions.styles - ?.filter((style) => typeof style !== 'string' && style.inject === false) - .map((s: StyleClass) => s.bundleName), - host, - https, - port, - quiet, - enableProdMode, - smokeTest, - sslCa, - sslCert, - sslKey, - disableTelemetry, - angularBrowserTarget: browserTarget, - angularBuilderContext, - angularBuilderOptions, - tsConfig, - initialPath, - open, - debugWebpack, - webpackStatsJson, - statsJson, - loglevel, - previewUrl, - }; + const { + browserTarget, + stylePreprocessorOptions, + styles, + ci, + configDir, + docs, + host, + https, + port, + quiet, + enableProdMode = false, + smokeTest, + sslCa, + sslCert, + sslKey, + disableTelemetry, + assets, + initialPath, + open, + debugWebpack, + loglevel, + webpackStatsJson, + statsJson, + previewUrl, + sourceMap = false, + } = options; - const devPort = await runInstance(standaloneOptions); + const standaloneOptions: StandaloneOptions = { + packageJson: readUpSync({ cwd: __dirname }).packageJson, + ci, + configDir, + ...(docs ? { docs } : {}), + host, + https, + port, + quiet, + enableProdMode, + smokeTest, + sslCa, + sslCert, + sslKey, + disableTelemetry, + angularBrowserTarget: browserTarget, + angularBuilderContext: context, + angularBuilderOptions: { + ...(stylePreprocessorOptions ? { stylePreprocessorOptions } : {}), + ...(styles ? { styles } : {}), + ...(assets ? { assets } : {}), + sourceMap, + }, + tsConfig, + initialPath, + open, + debugWebpack, + webpackStatsJson, + statsJson, + loglevel, + previewUrl, + }; - return { success: true, info: { port: devPort } }; + return standaloneOptions; + }), + switchMap((standaloneOptions) => runInstance(standaloneOptions)), + map((port: number) => { + return { success: true, info: { port } }; + }) + ); + + return builder as any as BuilderOutput; }; export default createBuilder(commandBuilder); -async function runInstance(options: StandaloneOptions): Promise { - try { - const { port } = await withTelemetry( +async function setup(options: StorybookBuilderOptions, context: BuilderContext) { + let browserOptions: (JsonObject & BrowserBuilderOptions) | undefined; + let browserTarget: Target | undefined; + + if (options.browserTarget) { + browserTarget = targetFromTargetString(options.browserTarget); + browserOptions = await context.validateOptions( + await context.getTargetOptions(browserTarget), + await context.getBuilderNameForTarget(browserTarget) + ); + } + + return { + tsConfig: + options.tsConfig ?? + findUpSync('tsconfig.json', { cwd: options.configDir }) ?? + browserOptions.tsConfig, + }; +} +function runInstance(options: StandaloneOptions) { + return new Observable((observer) => { + // This Observable intentionally never complete, leaving the process running ;) + withTelemetry( 'dev', { cliOptions: options, @@ -154,9 +196,10 @@ async function runInstance(options: StandaloneOptions): Promise { printError: printErrorDetails, }, () => buildDevStandalone(options) - ); - return port; - } catch (error) { - throw new Error(errorSummary(error)); - } + ) + .then(({ port }) => observer.next(port)) + .catch((error) => { + observer.error(errorSummary(error)); + }); + }); } diff --git a/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts b/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts index a8f7428e7cec..cf0686b11b1d 100644 --- a/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts +++ b/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts @@ -1,6 +1,7 @@ // eslint-disable-next-line import/no-extraneous-dependencies import { vi, describe, afterEach, it, expect } from 'vitest'; import { LoggerApi } from '@angular-devkit/core/src/logger'; +import { take } from 'rxjs/operators'; import { BuilderContext } from '@angular-devkit/architect'; import { runCompodoc } from './run-compodoc'; @@ -36,13 +37,15 @@ describe('runCompodoc', () => { } as BuilderContext; it('should run compodoc with tsconfig from context', async () => { - await runCompodoc( + runCompodoc( { compodocArgs: [], tsconfig: 'path/to/tsconfig.json', }, builderContextMock - ); + ) + .pipe(take(1)) + .subscribe(); expect(mockRunScript).toHaveBeenCalledWith( 'compodoc', @@ -53,13 +56,15 @@ describe('runCompodoc', () => { }); it('should run compodoc with tsconfig from compodocArgs', async () => { - await runCompodoc( + runCompodoc( { compodocArgs: ['-p', 'path/to/tsconfig.stories.json'], tsconfig: 'path/to/tsconfig.json', }, builderContextMock - ); + ) + .pipe(take(1)) + .subscribe(); expect(mockRunScript).toHaveBeenCalledWith( 'compodoc', @@ -70,13 +75,15 @@ describe('runCompodoc', () => { }); it('should run compodoc with default output folder.', async () => { - await runCompodoc( + runCompodoc( { compodocArgs: [], tsconfig: 'path/to/tsconfig.json', }, builderContextMock - ); + ) + .pipe(take(1)) + .subscribe(); expect(mockRunScript).toHaveBeenCalledWith( 'compodoc', @@ -87,13 +94,15 @@ describe('runCompodoc', () => { }); it('should run with custom output folder specified with --output compodocArgs', async () => { - await runCompodoc( + runCompodoc( { compodocArgs: ['--output', 'path/to/customFolder'], tsconfig: 'path/to/tsconfig.json', }, builderContextMock - ); + ) + .pipe(take(1)) + .subscribe(); expect(mockRunScript).toHaveBeenCalledWith( 'compodoc', @@ -104,13 +113,15 @@ describe('runCompodoc', () => { }); it('should run with custom output folder specified with -d compodocArgs', async () => { - await runCompodoc( + runCompodoc( { compodocArgs: ['-d', 'path/to/customFolder'], tsconfig: 'path/to/tsconfig.json', }, builderContextMock - ); + ) + .pipe(take(1)) + .subscribe(); expect(mockRunScript).toHaveBeenCalledWith( 'compodoc', diff --git a/code/frameworks/angular/src/builders/utils/run-compodoc.ts b/code/frameworks/angular/src/builders/utils/run-compodoc.ts index f916840e4631..e926c041bfa8 100644 --- a/code/frameworks/angular/src/builders/utils/run-compodoc.ts +++ b/code/frameworks/angular/src/builders/utils/run-compodoc.ts @@ -13,30 +13,34 @@ const toRelativePath = (pathToTsConfig: string) => { return path.isAbsolute(pathToTsConfig) ? path.relative('.', pathToTsConfig) : pathToTsConfig; }; -export const runCompodoc = async ( +export const runCompodoc = ( { compodocArgs, tsconfig }: { compodocArgs: string[]; tsconfig: string }, context: BuilderContext -): Promise => { - const tsConfigPath = toRelativePath(tsconfig); - const finalCompodocArgs = [ - ...(hasTsConfigArg(compodocArgs) ? [] : ['-p', tsConfigPath]), - ...(hasOutputArg(compodocArgs) ? [] : ['-d', `${context.workspaceRoot || '.'}`]), - ...compodocArgs, - ]; +): Observable => { + return new Observable((observer) => { + const tsConfigPath = toRelativePath(tsconfig); + const finalCompodocArgs = [ + ...(hasTsConfigArg(compodocArgs) ? [] : ['-p', tsConfigPath]), + ...(hasOutputArg(compodocArgs) ? [] : ['-d', `${context.workspaceRoot || '.'}`]), + ...compodocArgs, + ]; - const packageManager = JsPackageManagerFactory.getPackageManager(); + const packageManager = JsPackageManagerFactory.getPackageManager(); - try { - const stdout = packageManager.runPackageCommandSync( - 'compodoc', - finalCompodocArgs, - context.workspaceRoot, - 'inherit' - ); + try { + const stdout = packageManager.runPackageCommandSync( + 'compodoc', + finalCompodocArgs, + context.workspaceRoot, + 'inherit' + ); - context.logger.info(stdout); - } catch (e) { - context.logger.error(e); - throw e; - } + context.logger.info(stdout); + observer.next(); + observer.complete(); + } catch (e) { + context.logger.error(e); + observer.error(); + } + }); }; diff --git a/code/frameworks/angular/src/builders/utils/setup.ts b/code/frameworks/angular/src/builders/utils/setup.ts deleted file mode 100644 index b7187260e6c7..000000000000 --- a/code/frameworks/angular/src/builders/utils/setup.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { Target, targetFromTargetString } from '@angular-devkit/architect'; -import { BuilderContext } from '@angular-devkit/architect'; -import { JsonObject, logging } from '@angular-devkit/core'; -import { sync as findUpSync } from 'find-up'; -import { BrowserBuilderOptions, StylePreprocessorOptions } from '@angular-devkit/build-angular'; -import { logger } from '@storybook/node-logger'; -import { - AssetPattern, - SourceMapUnion, - StyleElement, -} from '@angular-devkit/build-angular/src/builders/browser/schema'; - -type AngularBuilderOptions = { - stylePreprocessorOptions?: StylePreprocessorOptions; - styles?: StyleElement[]; - assets?: AssetPattern[]; - sourceMap?: SourceMapUnion; -}; - -type Options = AngularBuilderOptions & { - browserTarget?: string | null; - tsConfig?: string; - configDir?: string; -}; - -export async function setup(options: Options, context: BuilderContext) { - let browserOptions: (JsonObject & BrowserBuilderOptions) | undefined; - let browserTarget: Target | undefined; - - if (options.browserTarget) { - browserTarget = targetFromTargetString(options.browserTarget); - browserOptions = await context.validateOptions( - await context.getTargetOptions(browserTarget), - await context.getBuilderNameForTarget(browserTarget) - ); - } - - const tsConfig = - options.tsConfig ?? - findUpSync('tsconfig.json', { cwd: options.configDir }) ?? - browserOptions.tsConfig; - - const angularBuilderContext = getBuilderContext(context); - - const angularBuilderOptions = await getBuilderOptions( - options.browserTarget, - { - ...(options.stylePreprocessorOptions - ? { stylePreprocessorOptions: options.stylePreprocessorOptions } - : {}), - ...(options.styles ? { styles: options.styles } : {}), - ...(options.assets ? { assets: options.assets } : {}), - sourceMap: options.sourceMap ?? false, - }, - tsConfig, - options.configDir, - angularBuilderContext - ); - - return { - tsConfig, - angularBuilderContext, - angularBuilderOptions, - }; -} - -/** - * Get Builder Context - * If storybook is not start by angular builder create dumb BuilderContext - */ -function getBuilderContext(builderContext: BuilderContext): BuilderContext { - return ( - builderContext ?? - ({ - target: { project: 'noop-project', builder: '', options: {} }, - workspaceRoot: process.cwd(), - getProjectMetadata: () => ({}), - getTargetOptions: () => ({}), - logger: new logging.Logger('Storybook'), - } as unknown as BuilderContext) - ); -} - -/** - * Get builder options - * Merge target options from browser target and from storybook options - */ -async function getBuilderOptions( - angularBrowserTarget: string, - angularBuilderOptions: AngularBuilderOptions, - tsConfig: string, - configDir: string, - builderContext: BuilderContext -) { - /** - * Get Browser Target options - */ - let browserTargetOptions: JsonObject = {}; - - if (angularBrowserTarget) { - const browserTarget = targetFromTargetString(angularBrowserTarget); - - browserTargetOptions = await builderContext.getTargetOptions(browserTarget); - } - - /** - * Merge target options from browser target options and from storybook options - */ - const builderOptions = { - ...browserTargetOptions, - ...angularBuilderOptions, - tsConfig: - tsConfig ?? findUpSync('tsconfig.json', { cwd: configDir }) ?? browserTargetOptions.tsConfig, - }; - logger.info(`=> Using angular project with "tsConfig:${builderOptions.tsConfig}"`); - - return builderOptions; -} diff --git a/code/frameworks/angular/src/builders/utils/standalone-options.ts b/code/frameworks/angular/src/builders/utils/standalone-options.ts index 42f8a417b0c8..ef73d78f01b5 100644 --- a/code/frameworks/angular/src/builders/utils/standalone-options.ts +++ b/code/frameworks/angular/src/builders/utils/standalone-options.ts @@ -1,7 +1,11 @@ import { BuilderContext } from '@angular-devkit/architect'; - +import { + AssetPattern, + SourceMapUnion, + StyleElement, + StylePreprocessorOptions, +} from '@angular-devkit/build-angular/src/builders/browser/schema'; import { LoadOptions, CLIOptions, BuilderOptions } from '@storybook/types'; -import { AngularBuilderOptions } from '../../server/framework-preset-angular-cli'; export type StandaloneOptions = CLIOptions & LoadOptions & @@ -9,8 +13,12 @@ export type StandaloneOptions = CLIOptions & mode?: 'static' | 'dev'; enableProdMode: boolean; angularBrowserTarget?: string | null; - angularBuilderOptions?: AngularBuilderOptions; + angularBuilderOptions?: Record & { + styles?: StyleElement[]; + stylePreprocessorOptions?: StylePreprocessorOptions; + assets?: AssetPattern[]; + sourceMap?: SourceMapUnion; + }; angularBuilderContext?: BuilderContext | null; tsConfig?: string; - excludeChunks?: string[]; }; diff --git a/code/frameworks/angular/src/server/angular-cli-webpack.js b/code/frameworks/angular/src/server/angular-cli-webpack.js index 32b31c1418a1..621680125536 100644 --- a/code/frameworks/angular/src/server/angular-cli-webpack.js +++ b/code/frameworks/angular/src/server/angular-cli-webpack.js @@ -56,7 +56,6 @@ exports.getWebpackConfig = async (baseConfig, { builderOptions, builderContext } */ const { getCommonConfig, getStylesConfig, getDevServerConfig, getTypeScriptConfig } = getAngularWebpackUtils(); - const { config: cliConfig } = await generateI18nBrowserWebpackConfigFromContext( { // Default options @@ -66,15 +65,10 @@ exports.getWebpackConfig = async (baseConfig, { builderOptions, builderContext } // Options provided by user ...builderOptions, - styles: builderOptions.styles?.map((style) => - typeof style === 'string' - ? { - input: style, - inject: true, - bundleName: style.split('/').pop(), - } - : style - ), + styles: builderOptions.styles + ?.map((style) => (typeof style === 'string' ? style : style.input)) + .filter((style) => typeof style === 'string' || style.inject !== false), + // Fixed options optimization: false, namedChunks: false, 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 9c52447b3faf..059d8b30f4d1 100644 --- a/code/frameworks/angular/src/server/framework-preset-angular-cli.ts +++ b/code/frameworks/angular/src/server/framework-preset-angular-cli.ts @@ -1,6 +1,9 @@ import webpack from 'webpack'; import { logger } from '@storybook/node-logger'; import { AngularLegacyBuildOptionsError } from '@storybook/core-events/server-errors'; +import { BuilderContext, targetFromTargetString } from '@angular-devkit/architect'; +import { sync as findUpSync } from 'find-up'; +import { JsonObject, logging } from '@angular-devkit/core'; import { getWebpackConfig as getCustomWebpackConfig } from './angular-cli-webpack'; import { moduleIsAvailable } from './utils/module-is-available'; @@ -14,15 +17,74 @@ export async function webpackFinal(baseConfig: webpack.Configuration, options: P checkForLegacyBuildOptions(options); + const builderContext = getBuilderContext(options); + const builderOptions = await getBuilderOptions(options, builderContext); + return getCustomWebpackConfig(baseConfig, { builderOptions: { watch: options.configType === 'DEVELOPMENT', - ...options.angularBuilderOptions, + ...builderOptions, }, - builderContext: options.angularBuilderContext, + builderContext, }); } +/** + * Get Builder Context + * If storybook is not start by angular builder create dumb BuilderContext + */ +function getBuilderContext(options: PresetOptions): BuilderContext { + return ( + options.angularBuilderContext ?? + ({ + target: { project: 'noop-project', builder: '', options: {} }, + workspaceRoot: process.cwd(), + getProjectMetadata: () => ({}), + getTargetOptions: () => ({}), + logger: new logging.Logger('Storybook'), + } as unknown as BuilderContext) + ); +} + +/** + * Get builder options + * Merge target options from browser target and from storybook options + */ +async function getBuilderOptions( + options: PresetOptions, + builderContext: BuilderContext +): Promise { + /** + * Get Browser Target options + */ + let browserTargetOptions: JsonObject = {}; + if (options.angularBrowserTarget) { + const browserTarget = targetFromTargetString(options.angularBrowserTarget); + + logger.info( + `=> Using angular browser target options from "${browserTarget.project}:${ + browserTarget.target + }${browserTarget.configuration ? `:${browserTarget.configuration}` : ''}"` + ); + browserTargetOptions = await builderContext.getTargetOptions(browserTarget); + } + + /** + * Merge target options from browser target options and from storybook options + */ + const builderOptions = { + ...browserTargetOptions, + ...(options.angularBuilderOptions as JsonObject), + tsConfig: + options.tsConfig ?? + findUpSync('tsconfig.json', { cwd: options.configDir }) ?? + browserTargetOptions.tsConfig, + }; + logger.info(`=> Using angular project with "tsConfig:${builderOptions.tsConfig}"`); + + return builderOptions; +} + /** * Checks if using legacy configuration that doesn't use builder and logs message referring to migration docs. */ diff --git a/code/frameworks/angular/src/server/preset-options.ts b/code/frameworks/angular/src/server/preset-options.ts index 6b6c19d5ad39..5412d5a19482 100644 --- a/code/frameworks/angular/src/server/preset-options.ts +++ b/code/frameworks/angular/src/server/preset-options.ts @@ -1,13 +1,17 @@ import { Options as CoreOptions } from '@storybook/types'; import { BuilderContext } from '@angular-devkit/architect'; -import { JsonObject } from '@angular-devkit/core'; +import { StylePreprocessorOptions } from '@angular-devkit/build-angular'; +import { StyleElement } from '@angular-devkit/build-angular/src/builders/browser/schema'; export type PresetOptions = CoreOptions & { /* Allow to get the options of a targeted "browser builder" */ angularBrowserTarget?: string | null; /* Defined set of options. These will take over priority from angularBrowserTarget options */ - angularBuilderOptions?: JsonObject; + angularBuilderOptions?: { + styles?: StyleElement[]; + stylePreprocessorOptions?: StylePreprocessorOptions; + }; /* Angular context from builder */ angularBuilderContext?: BuilderContext | null; tsConfig?: string;