From baeb761fe38efa1d1797f63afaf7ab4b8d10ae65 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Fri, 19 Aug 2016 02:19:12 +0100 Subject: [PATCH] chore(docs): add global lib docs --- README.md | 39 +++++++++++ .../ng2/blueprints/ng2/files/angular-cli.json | 5 +- addon/ng2/models/webpack-build-common.ts | 22 +++++-- lib/config/schema.json | 30 ++++----- package.json | 1 + tests/e2e/e2e_workflow.spec.js | 64 ++++++++++++++++--- 6 files changed, 128 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 9b16b22453f2..76901ea9ad31 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ The generated project has dependencies that require **Node 4 or greater**. * [Global styles](#global-styles) * [CSS preprocessor integration](#css-preprocessor-integration) * [3rd Party Library Installation](#3rd-party-library-installation) +* [Global Library Installation](#global-library-installation) * [Updating angular-cli](#updating-angular-cli) * [Known Issues](#known-issues) * [Development Hints for hacking on angular-cli](#development-hints-for-hacking-on-angular-cli) @@ -250,6 +251,8 @@ The `styles.css` file allows users to add global styles and supports If the project is created with the `--style=sass` option, this will be a `.sass` file instead, and the same applies to `scss/less/styl`. +You can add more global styles via the `apps[0].styles` property in `angular-cli.json`. + ### CSS Preprocessor integration Angular-CLI supports all major CSS preprocessors: @@ -296,6 +299,42 @@ npm install moment --save npm install @types/moment --save-dev ``` +### Global Library Installation + +Some javascript libraries need to be added to the global scope, and loaded as if +they were in a script tag. We can do this using the `apps[0].scripts` and +`apps[0].styles` properties of `angular-cli.json`. + +As an example, to use [Boostrap 4](http://v4-alpha.getbootstrap.com/) this is +what you need to do: + +First install Bootstrap from `npm`: + +```bash +npm install bootstrap@next +``` + +Then add the needed script files to to `apps[0].scripts`. + +``` +"scripts": [ + "../node_modules/jquery/dist/jquery.js", + "../node_modules/tether/dist/js/tether.js", + "../node_modules/bootstrap/dist/js/bootstrap.js" +], +``` + +Finally add the Bootstrap CSS to the `apps[0].styles` array: +``` +"styles": [ + "styles.css", + "../node_modules/bootstrap/dist/css/bootstrap.css" +], +``` + +Restart `ng serve` if you're running it, and Bootstrap 4 should be working on +your app. + ### Updating angular-cli To update `angular-cli` to a new version, you must update both the global package and your project's local package. diff --git a/addon/ng2/blueprints/ng2/files/angular-cli.json b/addon/ng2/blueprints/ng2/files/angular-cli.json index 4454880e6968..f908217c246b 100644 --- a/addon/ng2/blueprints/ng2/files/angular-cli.json +++ b/addon/ng2/blueprints/ng2/files/angular-cli.json @@ -14,7 +14,10 @@ "tsconfig": "tsconfig.json", "prefix": "<%= prefix %>", "mobile": <%= isMobile %>, - "styles": "styles.<%= styleExt %>", + "styles": [ + "styles.<%= styleExt %>" + ], + "scripts": [], "environments": { "source": "environments/environment.ts", "prod": "environments/environment.prod.ts", diff --git a/addon/ng2/models/webpack-build-common.ts b/addon/ng2/models/webpack-build-common.ts index 35cc21f82c79..591bf25dbf17 100644 --- a/addon/ng2/models/webpack-build-common.ts +++ b/addon/ng2/models/webpack-build-common.ts @@ -10,9 +10,18 @@ export function getWebpackCommonConfig(projectRoot: string, environment: string, const appRoot = path.resolve(projectRoot, appConfig.root); const appMain = path.resolve(appRoot, appConfig.main); - const styles = path.resolve(appRoot, appConfig.styles); + const styles = appConfig.styles.map(style => path.resolve(appRoot, style)); + const scripts = appConfig.scripts.map(script => path.resolve(appRoot, script)); const lazyModules = findLazyModules(appRoot); + let entry = { + main: [appMain] + }; + + // Only add styles/scripts if there's actually entries there + if (appConfig.styles.length > 0) entry.styles = styles; + if (appConfig.scripts.length > 0) entry.scripts = scripts; + return { devtool: 'source-map', resolve: { @@ -20,9 +29,7 @@ export function getWebpackCommonConfig(projectRoot: string, environment: string, root: appRoot }, context: path.resolve(__dirname, './'), - entry: { - main: [appMain, styles] - }, + entry: entry, output: { path: path.resolve(projectRoot, appConfig.outDir), filename: '[name].bundle.js' @@ -66,6 +73,9 @@ export function getWebpackCommonConfig(projectRoot: string, environment: string,        { include: styles, test: /\.less$/, loaders: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'] },        { include: styles, test: /\.scss$|\.sass$/, loaders: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] }, + // load global scripts using script-loader + { include: scripts, test: /\.js$/, loader: 'script-loader' }, +        { test: /\.json$/, loader: 'json-loader' },        { test: /\.(jpg|png)$/, loader: 'url-loader?limit=10000' },        { test: /\.html$/, loader: 'raw-loader' }, @@ -90,6 +100,10 @@ export function getWebpackCommonConfig(projectRoot: string, environment: string, .replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")), path.resolve(appRoot, appConfig.environments[environment]) ), + new webpack.optimize.CommonsChunkPlugin({ + // Optimizing ensures loading order in index.html + name: ['styles', 'scripts', 'main'].reverse(); + }), new webpack.optimize.CommonsChunkPlugin({ minChunks: Infinity, name: 'inline', diff --git a/lib/config/schema.json b/lib/config/schema.json index 168ad6a14e07..a971c033744a 100644 --- a/lib/config/schema.json +++ b/lib/config/schema.json @@ -49,27 +49,19 @@ "mobile": { "type": "boolean" }, - "additionalEntries": { - "description": "Additional files to be included in the build.", + "styles": { + "description": "Global styles to be included in the build.", "type": "array", "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "input": { - "type": "string" - }, - "output": { - "type": "string" - } - }, - "additionalProperties": false - } - ] + "type": "string" + }, + "additionalProperties": false + }, + "scripts": { + "description": "Global scripts to be included in the build.", + "type": "array", + "items": { + "type": "string" }, "additionalProperties": false }, diff --git a/package.json b/package.json index 13244ee47060..7ac6db5b15b9 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "rimraf": "^2.5.3", "rxjs": "^5.0.0-beta.11", "sass-loader": "^3.2.0", + "script-loader": "^0.7.0", "shelljs": "^0.7.0", "silent-error": "^1.0.0", "source-map-loader": "^0.1.5", diff --git a/tests/e2e/e2e_workflow.spec.js b/tests/e2e/e2e_workflow.spec.js index e72d63cb456e..a92ec8cc6501 100644 --- a/tests/e2e/e2e_workflow.spec.js +++ b/tests/e2e/e2e_workflow.spec.js @@ -486,7 +486,7 @@ describe('Basic end-to-end Workflow', function () { expect(indexHtml).to.include('main.bundle.js'); }); - it('styles.css is added to main bundle', function() { + it('styles.css is added to styles bundle', function() { this.timeout(420000); let stylesPath = path.join(process.cwd(), 'src', 'styles.css'); @@ -495,10 +495,10 @@ describe('Basic end-to-end Workflow', function () { sh.exec(`${ngBin} build`); - var mainBundlePath = path.join(process.cwd(), 'dist', 'main.bundle.js'); - var mainBundleContent = fs.readFileSync(mainBundlePath, { encoding: 'utf8' }); + var stylesBundlePath = path.join(process.cwd(), 'dist', 'styles.bundle.js'); + var stylesBundleContent = fs.readFileSync(stylesBundlePath, { encoding: 'utf8' }); - expect(mainBundleContent.includes(testStyle)).to.be.equal(true); + expect(stylesBundleContent.includes(testStyle)).to.be.equal(true); }); it('styles.css supports css imports', function() { @@ -508,16 +508,62 @@ describe('Basic end-to-end Workflow', function () { let testStyle = 'body { background-color: blue; }'; fs.writeFileSync(importedStylePath, testStyle, 'utf8'); - let stylesPath = path.join(process.cwd(), 'src', 'style.css'); - let importStyle = '@import \'./imported-style.css\';'; + let stylesPath = path.join(process.cwd(), 'src', 'styles.css'); + let importStyle = '@import \'./imported-styles.css\';'; fs.writeFileSync(stylesPath, importStyle, 'utf8'); sh.exec(`${ngBin} build`); - var mainBundlePath = path.join(process.cwd(), 'dist', 'main.bundle.js'); - var mainBundleContent = fs.readFileSync(mainBundlePath, { encoding: 'utf8' }); + var stylesBundlePath = path.join(process.cwd(), 'dist', 'styles.bundle.js'); + var stylesBundleContent = fs.readFileSync(stylesBundlePath, { encoding: 'utf8' }); + + expect(stylesBundleContent).to.include(testStyle); + }); + + it('build supports global styles and scripts', function() { + this.timeout(420000); + + sh.exec('npm install bootstrap@next', { silent: true }); + + const configFile = path.join(process.cwd(), 'angular-cli.json'); + let originalConfigContent = fs.readFileSync(configFile, { encoding: 'utf8' }); + let configContent = originalConfigContent.replace('"styles.css"', ` + "styles.css", + "../node_modules/bootstrap/dist/css/bootstrap.css" + `).replace('"scripts": [],',` + "scripts": [ + "../node_modules/jquery/dist/jquery.js", + "../node_modules/tether/dist/js/tether.js", + "../node_modules/bootstrap/dist/js/bootstrap.js" + ], + `); + + fs.writeFileSync(configFile, configContent, 'utf8'); + + sh.exec(`${ngBin} build`); - expect(mainBundleContent.includes(testStyle)).to.be.equal(true); + // checking for strings that are part of the included files + const stylesBundlePath = path.join(process.cwd(), 'dist', 'styles.bundle.js'); + const stylesBundleContent = fs.readFileSync(stylesBundlePath, { encoding: 'utf8' }); + expect(stylesBundleContent).to.include('* Bootstrap '); + + const scriptsBundlePath = path.join(process.cwd(), 'dist', 'scripts.bundle.js'); + const scriptsBundleContent = fs.readFileSync(scriptsBundlePath, { encoding: 'utf8' }); + expect(scriptsBundleContent).to.include('* jQuery JavaScript'); + expect(scriptsBundleContent).to.include('/*! tether '); + expect(scriptsBundleContent).to.include('* Bootstrap '); + + // check the scripts are loaded in the correct order + const indexPath = path.join(process.cwd(), 'dist', 'index.html'); + const indexContent = fs.readFileSync(indexPath, { encoding: 'utf8' }); + let scriptTags = '' + + '' + + '' + + '' + expect(indexContent).to.include(scriptTags); + + // restore config + fs.writeFileSync(configFile, originalConfigContent, 'utf8'); }); it('Serve and run e2e tests on dev environment', function () {