diff --git a/e2e/web/src/web.test.ts b/e2e/web/src/web.test.ts index de26d979ae5a5..6ab996395d2ee 100644 --- a/e2e/web/src/web.test.ts +++ b/e2e/web/src/web.test.ts @@ -4,6 +4,7 @@ import { createFile, newProject, readFile, + readJson, runCLI, runCLIAsync, uniq, @@ -172,3 +173,83 @@ describe('CLI - Environment Variables', () => { ); }); }); + +describe('Build Options', () => { + it('should inject/bundle external scripts and styles', () => { + newProject(); + + const appName = uniq('app'); + + runCLI(`generate @nrwl/web:app ${appName} --no-interactive`); + + const srcPath = `apps/${appName}/src`; + const fooCss = `${srcPath}/foo.css`; + const barCss = `${srcPath}/bar.css`; + const fooJs = `${srcPath}/foo.js`; + const barJs = `${srcPath}/bar.js`; + const fooCssContent = `/* ${uniq('foo')} */`; + const barCssContent = `/* ${uniq('bar')} */`; + const fooJsContent = `/* ${uniq('foo')} */`; + const barJsContent = `/* ${uniq('bar')} */`; + + createFile(fooCss); + createFile(barCss); + createFile(fooJs); + createFile(barJs); + + // createFile could not create a file with content + updateFile(fooCss, fooCssContent); + updateFile(barCss, barCssContent); + updateFile(fooJs, fooJsContent); + updateFile(barJs, barJsContent); + + const workspacePath = `workspace.json`; + const workspaceConfig = readJson(workspacePath); + const buildOptions = + workspaceConfig.projects[appName].targets.build.options; + + const barScriptsBundleName = 'bar-scripts'; + buildOptions.scripts = [ + { + input: fooJs, + inject: true, + }, + { + input: barJs, + inject: false, + bundleName: barScriptsBundleName, + }, + ]; + + const barStylesBundleName = 'bar-styles'; + buildOptions.styles = [ + { + input: fooCss, + inject: true, + }, + { + input: barCss, + inject: false, + bundleName: barStylesBundleName, + }, + ]; + + updateFile(workspacePath, JSON.stringify(workspaceConfig)); + + runCLI(`build ${appName}`); + + const distPath = `dist/apps/${appName}`; + const scripts = readFile(`${distPath}/scripts.js`); + const styles = readFile(`${distPath}/styles.js`); + const barScripts = readFile(`${distPath}/${barScriptsBundleName}.js`); + const barStyles = readFile(`${distPath}/${barStylesBundleName}.js`); + + expect(scripts).toContain(fooJsContent); + expect(scripts).not.toContain(barJsContent); + expect(barScripts).toContain(barJsContent); + + expect(styles).toContain(fooCssContent); + expect(styles).not.toContain(barCssContent); + expect(barStyles).toContain(barCssContent); + }); +}); diff --git a/packages/web/src/builders/build/build.impl.ts b/packages/web/src/builders/build/build.impl.ts index 8134d7c5b5039..655f59d58bf08 100644 --- a/packages/web/src/builders/build/build.impl.ts +++ b/packages/web/src/builders/build/build.impl.ts @@ -32,6 +32,7 @@ import { CrossOriginValue } from '../../utils/third-party/cli-files/utilities/in import { readTsConfig } from '@nrwl/workspace'; import { BuildBrowserFeatures } from '../../utils/third-party/utils/build-browser-features'; import { deleteOutputDir } from '../../utils/delete-output-dir'; +import { ExtraEntryPoint } from '../../utils/third-party/browser/schema'; export interface WebBuildBuilderOptions extends BuildBuilderOptions { index: string; @@ -45,8 +46,8 @@ export interface WebBuildBuilderOptions extends BuildBuilderOptions { polyfills?: string; es2015Polyfills?: string; - scripts: string[]; - styles: string[]; + scripts: ExtraEntryPoint[]; + styles: ExtraEntryPoint[]; vendorChunk?: boolean; commonChunk?: boolean; diff --git a/packages/web/src/builders/build/schema.json b/packages/web/src/builders/build/schema.json index c9b2e65ecb47f..1aa89d2d6848d 100644 --- a/packages/web/src/builders/build/schema.json +++ b/packages/web/src/builders/build/schema.json @@ -104,14 +104,14 @@ "type": "array", "description": "External Scripts which will be included before the main application entry", "items": { - "type": "string" + "$ref": "#/definitions/extraEntryPoint" } }, "styles": { "type": "array", "description": "External Styles which will be included with the application", "items": { - "type": "string" + "$ref": "#/definitions/extraEntryPoint" } }, "budgets": { @@ -325,6 +325,34 @@ }, "additionalProperties": false, "required": ["type"] + }, + "extraEntryPoint": { + "oneOf": [ + { + "type": "object", + "properties": { + "input": { + "type": "string", + "description": "The file to include." + }, + "bundleName": { + "type": "string", + "description": "The bundle name for this extra entry point." + }, + "inject": { + "type": "boolean", + "description": "If the bundle will be referenced in the HTML file.", + "default": true + } + }, + "additionalProperties": false, + "required": ["input"] + }, + { + "type": "string", + "description": "The file to include." + } + ] } } }