From a6415cc1d5f13567487713291cde429c119f6387 Mon Sep 17 00:00:00 2001 From: Jimit Ndiaye Date: Thu, 13 Oct 2016 11:08:08 +0100 Subject: [PATCH] feat(build): use static files for css Fix #2148 Fix #2020 Fix #2826 Close #2646 --- package.json | 1 + .../models/webpack-build-common.ts | 18 --------- .../models/webpack-build-development.ts | 27 +++++++++++++ .../models/webpack-build-production.ts | 29 ++++++++++++++ packages/angular-cli/package.json | 1 + tests/e2e/tests/build/prod-build.ts | 1 + tests/e2e/tests/build/styles/css.ts | 38 ++++++++++++++++++- 7 files changed, 95 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 2b7e390c35fa..a25c71729a40 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "exit": "^0.1.2", "exports-loader": "^0.6.3", "expose-loader": "^0.7.1", + "extract-text-webpack-plugin": "^2.0.0-beta.4", "file-loader": "^0.8.5", "fs-extra": "^0.30.0", "fs.realpath": "^1.0.0", diff --git a/packages/angular-cli/models/webpack-build-common.ts b/packages/angular-cli/models/webpack-build-common.ts index 1dddf6d63582..45d51422b4e6 100644 --- a/packages/angular-cli/models/webpack-build-common.ts +++ b/packages/angular-cli/models/webpack-build-common.ts @@ -71,24 +71,6 @@ export function getWebpackCommonConfig( loaders: ['raw-loader', 'postcss-loader', 'sass-loader'] }, - // outside of main, load it via style-loader -        { - include: styles, - test: /\.css$/, - loaders: ['style-loader', 'css-loader', 'postcss-loader'] - }, { - include: styles, - test: /\.styl$/, - loaders: ['style-loader', 'css-loader', 'postcss-loader', 'stylus-loader'] - }, { - 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' }, diff --git a/packages/angular-cli/models/webpack-build-development.ts b/packages/angular-cli/models/webpack-build-development.ts index f1bfd7ff6b24..c36760fab3bc 100644 --- a/packages/angular-cli/models/webpack-build-development.ts +++ b/packages/angular-cli/models/webpack-build-development.ts @@ -1,6 +1,11 @@ const path = require('path'); export const getWebpackDevConfigPartial = function(projectRoot: string, appConfig: any) { + const appRoot = path.resolve(projectRoot, appConfig.root); + const styles = appConfig.styles + ? appConfig.styles.map((style: string) => path.resolve(appRoot, style)) + : []; + const cssLoaders = ['style-loader', 'css-loader?sourcemap', 'postcss-loader']; return { devtool: 'source-map', output: { @@ -8,6 +13,28 @@ export const getWebpackDevConfigPartial = function(projectRoot: string, appConfi filename: '[name].bundle.js', sourceMapFilename: '[name].map', chunkFilename: '[id].chunk.js' + }, + module: { + rules: [ + // outside of main, load it via style-loader for development builds +        { + include: styles, + test: /\.css$/, + loaders: cssLoaders + }, { + include: styles, + test: /\.styl$/, + loaders: [...cssLoaders, 'stylus-loader?sourcemap'] + }, { + include: styles, + test: /\.less$/, + loaders: [...cssLoaders, 'less-loader?sourcemap'] + }, { + include: styles, + test: /\.scss$|\.sass$/, + loaders: [...cssLoaders, 'sass-loader?sourcemap'] + }, + ] } }; }; diff --git a/packages/angular-cli/models/webpack-build-production.ts b/packages/angular-cli/models/webpack-build-production.ts index 3ff408ad9a07..23a015d3c7a2 100644 --- a/packages/angular-cli/models/webpack-build-production.ts +++ b/packages/angular-cli/models/webpack-build-production.ts @@ -2,6 +2,7 @@ import * as path from 'path'; const WebpackMd5Hash = require('webpack-md5-hash'); const CompressionPlugin = require('compression-webpack-plugin'); import * as webpack from 'webpack'; +const ExtractTextPlugin = require('extract-text-webpack-plugin'); declare module 'webpack' { export interface LoaderOptionsPlugin {} @@ -14,6 +15,11 @@ declare module 'webpack' { } export const getWebpackProdConfigPartial = function(projectRoot: string, appConfig: any) { + const appRoot = path.resolve(projectRoot, appConfig.root); + const styles = appConfig.styles + ? appConfig.styles.map((style: string) => path.resolve(appRoot, style)) + : []; + const cssLoaders = ['css-loader?sourcemap&minimize', 'postcss-loader']; return { devtool: 'source-map', output: { @@ -22,7 +28,30 @@ export const getWebpackProdConfigPartial = function(projectRoot: string, appConf sourceMapFilename: '[name].[chunkhash].bundle.map', chunkFilename: '[id].[chunkhash].chunk.js' }, + module: { + rules: [ + // outside of main, load it via extract-text-plugin for production builds +        { + include: styles, + test: /\.css$/, + loaders: ExtractTextPlugin.extract(cssLoaders) + }, { + include: styles, + test: /\.styl$/, + loaders: ExtractTextPlugin.extract([...cssLoaders, 'stylus-loader?sourcemap']) + }, { + include: styles, + test: /\.less$/, + loaders: ExtractTextPlugin.extract([...cssLoaders, 'less-loader?sourcemap']) + }, { + include: styles, + test: /\.scss$|\.sass$/, + loaders: ExtractTextPlugin.extract([...cssLoaders, 'sass-loader?sourcemap']) + }, + ] + }, plugins: [ + new ExtractTextPlugin('[name].[contenthash].bundle.css'), new WebpackMd5Hash(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') diff --git a/packages/angular-cli/package.json b/packages/angular-cli/package.json index ec0688fd3532..fb2a868312c7 100644 --- a/packages/angular-cli/package.json +++ b/packages/angular-cli/package.json @@ -49,6 +49,7 @@ "exit": "^0.1.2", "exports-loader": "^0.6.3", "expose-loader": "^0.7.1", + "extract-text-webpack-plugin": "^2.0.0-beta.4", "file-loader": "^0.8.5", "fs-extra": "^0.30.0", "fs.realpath": "^1.0.0", diff --git a/tests/e2e/tests/build/prod-build.ts b/tests/e2e/tests/build/prod-build.ts index fe0699af4356..f4d798b70485 100644 --- a/tests/e2e/tests/build/prod-build.ts +++ b/tests/e2e/tests/build/prod-build.ts @@ -34,6 +34,7 @@ export default function() { .then(() => expectFileToExist(join(process.cwd(), 'dist'))) // Check for cache busting hash script src .then(() => expectFileToMatch('dist/index.html', /main\.[0-9a-f]{20}\.bundle\.js/)) + .then(() => expectFileToMatch('dist/index.html', /styles\.[0-9a-f]{32}\.bundle\.css/)) // Check that the process didn't change local files. .then(() => expectGitToBeClean()) diff --git a/tests/e2e/tests/build/styles/css.ts b/tests/e2e/tests/build/styles/css.ts index aa081ef1f45c..7fbe8ed8982f 100644 --- a/tests/e2e/tests/build/styles/css.ts +++ b/tests/e2e/tests/build/styles/css.ts @@ -1,19 +1,53 @@ +import * as glob from 'glob'; + import {writeMultipleFiles, expectFileToMatch} from '../../../utils/fs'; import {ng} from '../../../utils/process'; +import {updateJsonFile} from '../../../utils/project'; export default function() { return writeMultipleFiles({ 'src/styles.css': ` @import './imported-styles.css'; - + body { background-color: blue; } `, 'src/imported-styles.css': ` p { background-color: red; } + `, + 'src/styles.less': ` + .outer { + .inner { + background: #fff; + } + } + `, + 'src/styles.scss': ` + .upper { + .lower { + background: #def; + } + } ` }) + .then(() => updateJsonFile('angular-cli.json', configJson => { + const app = configJson['apps'][0]; + app['styles'].push('styles.less'); + app['styles'].push('styles.scss'); + })) .then(() => ng('build')) .then(() => expectFileToMatch('dist/styles.bundle.js', 'body { background-color: blue; }')) - .then(() => expectFileToMatch('dist/styles.bundle.js', 'p { background-color: red; }')); + .then(() => expectFileToMatch('dist/styles.bundle.js', 'p { background-color: red; }')) + .then(() => expectFileToMatch('dist/styles.bundle.js', /.outer.*.inner.*background:\s*#[fF]+/)) + .then(() => expectFileToMatch('dist/styles.bundle.js', /.upper.*.lower.*background.*#def/)) + + .then(() => ng('build', '--prod')) + .then(() => new Promise(() => + glob.sync('dist/styles.*.bundle.css').find(file => !!file))) + .then((styles) => + expectFileToMatch(styles, 'body { background-color: blue; }') + .then(() => expectFileToMatch(styles, 'p { background-color: red; }') + .then(() => expectFileToMatch(styles, /.outer.*.inner.*background:\s*#[fF]+/)) + .then(() => expectFileToMatch(styles, /.upper.*.lower.*background.*#def/))) + ); }