diff --git a/app/angular/src/server/__mocks-ng-workspace__/with-angularBrowserTarget/angular.json b/app/angular/src/server/__mocks-ng-workspace__/with-angularBrowserTarget/angular.json index 06a1590a183..0bd10ad584f 100644 --- a/app/angular/src/server/__mocks-ng-workspace__/with-angularBrowserTarget/angular.json +++ b/app/angular/src/server/__mocks-ng-workspace__/with-angularBrowserTarget/angular.json @@ -12,7 +12,7 @@ } } }, - "target-project": { + "no-confs-project": { "root": "", "architect": { "target-build": { @@ -22,6 +22,38 @@ } } } + }, + "no-target-conf-project": { + "root": "", + "architect": { + "target-build": { + "options": { + "tsConfig": "src/tsconfig.app.json", + "assets": [] + }, + "configurations": { + "other-conf": { + "styles": ["src/styles.css"] + } + } + } + } + }, + "target-project": { + "root": "", + "architect": { + "target-build": { + "options": { + "tsConfig": "src/tsconfig.app.json", + "assets": [] + }, + "configurations": { + "target-conf": { + "styles": ["src/styles.css"] + } + } + } + } } }, "defaultProject": "foo-project" diff --git a/app/angular/src/server/__mocks-ng-workspace__/with-angularBrowserTarget/src/styles.css b/app/angular/src/server/__mocks-ng-workspace__/with-angularBrowserTarget/src/styles.css new file mode 100644 index 00000000000..25357ee7cc9 --- /dev/null +++ b/app/angular/src/server/__mocks-ng-workspace__/with-angularBrowserTarget/src/styles.css @@ -0,0 +1,2 @@ +.class { +} diff --git a/app/angular/src/server/angular-devkit-build-webpack.ts b/app/angular/src/server/angular-devkit-build-webpack.ts index bd176b118ff..6f2e26f0529 100644 --- a/app/angular/src/server/angular-devkit-build-webpack.ts +++ b/app/angular/src/server/angular-devkit-build-webpack.ts @@ -66,9 +66,22 @@ const importAngularCliReadTsconfigUtil = (): typeof import('@angular-devkit/buil const buildWebpackConfigOptions = async ( dirToSearch: string, project: workspaces.ProjectDefinition, - target: workspaces.TargetDefinition + target: workspaces.TargetDefinition, + confName?: string ): Promise => { - const { options: projectBuildOptions = {} } = target; + let conf: Record = {}; + + if (confName) { + if (!target.configurations) { + throw new Error('Missing "configurations" section in project target'); + } + if (!target.configurations[confName]) { + throw new Error(`Missing required configuration in project target. Check "${confName}"`); + } + conf = target.configurations[confName]; + } + + const projectBuildOptions = { ...target.options, ...conf }; const requiredOptions = ['tsConfig']; if (!requiredOptions.every((key) => !!projectBuildOptions[key])) { @@ -159,11 +172,17 @@ export type AngularCliWebpackConfig = { export async function extractAngularCliWebpackConfig( dirToSearch: string, project: workspaces.ProjectDefinition, - target: workspaces.TargetDefinition + target: workspaces.TargetDefinition, + confName?: string ): Promise { const { getCommonConfig, getStylesConfig } = importAngularCliWebpackConfigGenerator(); - const webpackConfigOptions = await buildWebpackConfigOptions(dirToSearch, project, target); + const webpackConfigOptions = await buildWebpackConfigOptions( + dirToSearch, + project, + target, + confName + ); const cliCommonConfig = getCommonConfig(webpackConfigOptions); const cliStyleConfig = getStylesConfig(webpackConfigOptions); diff --git a/app/angular/src/server/framework-preset-angular-cli.test.ts b/app/angular/src/server/framework-preset-angular-cli.test.ts index 6fbf8498b37..6dad8384fe3 100644 --- a/app/angular/src/server/framework-preset-angular-cli.test.ts +++ b/app/angular/src/server/framework-preset-angular-cli.test.ts @@ -613,6 +613,71 @@ describe('framework-preset-angular-cli', () => { }); }); + describe('with angularBrowserTarget option with configuration', () => { + beforeEach(() => { + initMockWorkspace('with-angularBrowserTarget'); + }); + describe('when angular.json have the target without "configurations" section', () => { + beforeEach(() => { + options = { angularBrowserTarget: 'no-confs-project:target-build:target-conf' } as Options; + }); + it('throws error', async () => { + await expect(() => webpackFinal(newWebpackConfiguration(), options)).rejects.toThrowError( + 'Missing "configurations" section in project target' + ); + expect(logger.error).toHaveBeenCalledWith(`=> Could not get angular cli webpack config`); + }); + }); + describe('when angular.json have the target without required configuration', () => { + beforeEach(() => { + options = { + angularBrowserTarget: 'no-target-conf-project:target-build:target-conf', + } as Options; + }); + it('throws error', async () => { + await expect(() => webpackFinal(newWebpackConfiguration(), options)).rejects.toThrowError( + 'Missing required configuration in project target. Check "target-conf"' + ); + expect(logger.error).toHaveBeenCalledWith(`=> Could not get angular cli webpack config`); + }); + }); + describe('when angular.json have the target with required configuration', () => { + beforeEach(() => { + options = { angularBrowserTarget: 'target-project:target-build:target-conf' } as Options; + }); + it('should log', async () => { + const baseWebpackConfig = newWebpackConfiguration(); + await webpackFinal(baseWebpackConfig, options); + + expect(logger.info).toHaveBeenCalledTimes(3); + expect(logger.info).toHaveBeenNthCalledWith(1, '=> Loading angular-cli config'); + expect(logger.info).toHaveBeenNthCalledWith( + 2, + '=> Using angular project "target-project:target-build:target-conf" for configuring Storybook' + ); + expect(logger.info).toHaveBeenNthCalledWith(3, '=> Using angular-cli webpack config'); + }); + it('should extends webpack base config', async () => { + const baseWebpackConfig = newWebpackConfiguration(); + const webpackFinalConfig = await webpackFinal(baseWebpackConfig, options); + + expect(webpackFinalConfig).toEqual({ + ...baseWebpackConfig, + entry: [...(baseWebpackConfig.entry as any[]), `${workspaceRoot}/src/styles.css`], + module: { ...baseWebpackConfig.module, rules: expect.anything() }, + plugins: expect.anything(), + resolve: { + ...baseWebpackConfig.resolve, + modules: expect.arrayContaining(baseWebpackConfig.resolve.modules), + // the base resolve.plugins are not kept 🤷‍♂️ + plugins: expect.not.arrayContaining(baseWebpackConfig.resolve.plugins), + }, + resolveLoader: expect.anything(), + }); + }); + }); + }); + describe('with only tsConfig option', () => { beforeEach(() => { initMockWorkspace('without-projects-entry'); diff --git a/app/angular/src/server/framework-preset-angular-cli.ts b/app/angular/src/server/framework-preset-angular-cli.ts index ffc90c3c9a2..5f76ff6c118 100644 --- a/app/angular/src/server/framework-preset-angular-cli.ts +++ b/app/angular/src/server/framework-preset-angular-cli.ts @@ -46,6 +46,7 @@ export async function webpackFinal(baseConfig: webpack.Configuration, options: O // Find angular project target let project: workspaces.ProjectDefinition; let target: workspaces.TargetDefinition; + let confName: string; try { // Default behavior when `angularBrowserTarget` are not explicitly defined to null if (options.angularBrowserTarget !== null) { @@ -64,9 +65,12 @@ export async function webpackFinal(baseConfig: webpack.Configuration, options: O ); project = fondProject.project; target = fondProject.target; + confName = browserTarget.configuration; logger.info( - `=> Using angular project "${browserTarget.project}:${browserTarget.target}" for configuring Storybook` + `=> Using angular project "${browserTarget.project}:${browserTarget.target}${ + confName ? `:${confName}` : '' + }" for configuring Storybook` ); } // Start storybook when only tsConfig is provided. @@ -85,7 +89,12 @@ export async function webpackFinal(baseConfig: webpack.Configuration, options: O // Use angular-cli to get some webpack config let angularCliWebpackConfig: AngularCliWebpackConfig; try { - angularCliWebpackConfig = await extractAngularCliWebpackConfig(dirToSearch, project, target); + angularCliWebpackConfig = await extractAngularCliWebpackConfig( + dirToSearch, + project, + target, + confName + ); logger.info(`=> Using angular-cli webpack config`); } catch (error) { logger.error(`=> Could not get angular cli webpack config`);