Skip to content

Commit

Permalink
feat(build): add lazy styles/scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
filipesilva committed Dec 6, 2016
1 parent e6364a9 commit fa60928
Show file tree
Hide file tree
Showing 20 changed files with 503 additions and 228 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"rimraf": "^2.5.3",
"rsvp": "^3.0.17",
"rxjs": "5.0.0-beta.12",
"sass-loader": "^3.2.0",
"sass-loader": "^4.0.1",
"script-loader": "^0.7.0",
"semver": "^5.1.0",
"silent-error": "^1.0.0",
Expand Down
46 changes: 39 additions & 7 deletions packages/angular-cli/lib/config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,17 @@
"default": "dist/"
},
"assets": {
"fixme": true,
"type": "array",
"items": {
"type": "string"
},
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"default": []
},
"index": {
Expand All @@ -62,15 +68,41 @@
"description": "Global styles to be included in the build.",
"type": "array",
"items": {
"type": "string"
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"input": {
"type": "string"
}
},
"additionalProperties": true
}
]
},
"additionalProperties": false
},
"scripts": {
"description": "Global scripts to be included in the build.",
"type": "array",
"items": {
"type": "string"
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"input": {
"type": "string"
}
},
"additionalProperties": true
}
]
},
"additionalProperties": false
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ function _parseJsonPath(path: string): string[] {
function _getSchemaNodeForPath<T>(rootMetaData: SchemaTreeNode<T>,
path: string): SchemaTreeNode<any> {
let fragments = _parseJsonPath(path);
// TODO: make this work with union (oneOf) schemas
return fragments.reduce((md: SchemaTreeNode<any>, current: string) => {
return md && md.children && md.children[current];
}, rootMetaData);
Expand Down
17 changes: 13 additions & 4 deletions packages/angular-cli/models/json-schema/schema-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,21 @@ export abstract class NonLeafSchemaTreeNode<T> extends SchemaTreeNode<T> {
protected _createChildProperty<T>(name: string, value: T, forward: SchemaTreeNode<T>,
schema: Schema, define = true): SchemaTreeNode<T> {

// TODO: fix this
if (schema['fixme'] && typeof value === 'string') {
value = <T>(<any>[ value ]);
let type: string;

if (!schema['oneOf']) {
type = schema['type'];
} else {
for (let testSchema of schema['oneOf']) {
if ((testSchema['type'] === 'array' && Array.isArray(value))
|| typeof value === testSchema['type']) {
type = testSchema['type'];
schema = testSchema;
break;
}
}
}

const type = schema['type'];
let Klass: any = null;

switch (type) {
Expand Down
133 changes: 74 additions & 59 deletions packages/angular-cli/models/webpack-build-common.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import * as webpack from 'webpack';
import * as path from 'path';
import {GlobCopyWebpackPlugin} from '../plugins/glob-copy-webpack-plugin';
import {packageChunkSort} from '../utilities/package-chunk-sort';
import {BaseHrefWebpackPlugin} from '@angular-cli/base-href-webpack';
import { GlobCopyWebpackPlugin } from '../plugins/glob-copy-webpack-plugin';
import { SuppressEntryChunksWebpackPlugin } from '../plugins/suppress-entry-chunks-webpack-plugin';
import { packageChunkSort } from '../utilities/package-chunk-sort';
import { BaseHrefWebpackPlugin } from '@angular-cli/base-href-webpack';
import { extraEntryParser, makeCssLoaders } from './webpack-build-utils';

const ProgressPlugin = require('webpack/lib/ProgressPlugin');
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const autoprefixer = require('autoprefixer');


export function getWebpackCommonConfig(
projectRoot: string,
environment: string,
Expand All @@ -23,21 +24,60 @@ export function getWebpackCommonConfig(
const appRoot = path.resolve(projectRoot, appConfig.root);
const appMain = path.resolve(appRoot, appConfig.main);
const nodeModules = path.resolve(projectRoot, 'node_modules');
const styles = appConfig.styles
? appConfig.styles.map((style: string) => path.resolve(appRoot, style))
: [];
const scripts = appConfig.scripts
? appConfig.scripts.map((script: string) => path.resolve(appRoot, script))
: [];
const extraPlugins: any[] = [];

let entry: { [key: string]: string[] } = {

let extraPlugins: any[] = [];
let extraRules: any[] = [];
let lazyChunks: string[] = [];

let entryPoints: { [key: string]: string[] } = {
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; }
// process global scripts
if (appConfig.scripts.length > 0) {
const globalScrips = extraEntryParser(appConfig.scripts, appRoot, 'scripts');

// add entry points and lazy chunks
globalScrips.forEach(script => {
if (script.lazy) { lazyChunks.push(script.entry); }
entryPoints[script.entry] = (entryPoints[script.entry] || []).concat(script.path);
});

// load global scripts using script-loader
extraRules.push({
include: globalScrips.map((script) => script.path), test: /\.js$/, loader: 'script-loader'
});
}

// process global styles
if (appConfig.styles.length === 0) {
// create css loaders for component css
extraRules.push(...makeCssLoaders());
} else {
const globalStyles = extraEntryParser(appConfig.styles, appRoot, 'styles');
let extractedCssEntryPoints: string[] = [];
// add entry points and lazy chunks
globalStyles.forEach(style => {
if (style.lazy) { lazyChunks.push(style.entry); }
if (!entryPoints[style.entry]) {
// since this entry point doesn't exist yet, it's going to only have
// extracted css and we can supress the entry point
extractedCssEntryPoints.push(style.entry);
entryPoints[style.entry] = (entryPoints[style.entry] || []).concat(style.path);
} else {
// existing entry point, just push the css in
entryPoints[style.entry].push(style.path);
}
});

// create css loaders for component css and for global css
extraRules.push(...makeCssLoaders(globalStyles.map((style) => style.path)));

if (extractedCssEntryPoints.length > 0) {
// don't emit the .js entry point for extracted styles
extraPlugins.push(new SuppressEntryChunksWebpackPlugin({ chunks: extractedCssEntryPoints }));
}
}

if (vendorChunk) {
extraPlugins.push(new webpack.optimize.CommonsChunkPlugin({
Expand All @@ -47,12 +87,7 @@ export function getWebpackCommonConfig(
}));
}

if (progress) {
extraPlugins.push(new ProgressPlugin({
profile: verbose,
colors: true
}));
}
if (progress) { extraPlugins.push(new ProgressPlugin({ profile: verbose, colors: true })); }

return {
devtool: sourcemap ? 'source-map' : false,
Expand All @@ -61,7 +96,7 @@ export function getWebpackCommonConfig(
modules: [nodeModules]
},
context: path.resolve(__dirname, './'),
entry: entry,
entry: entryPoints,
output: {
path: path.resolve(projectRoot, appConfig.outDir),
filename: '[name].bundle.js',
Expand All @@ -70,48 +105,22 @@ export function getWebpackCommonConfig(
},
module: {
rules: [
{
enforce: 'pre',
test: /\.js$/,
loader: 'source-map-loader',
exclude: [ nodeModules ]
},
// in main, load css as raw text
       {
exclude: styles,
test: /\.css$/,
loaders: ['raw-loader', 'postcss-loader']
}, {
exclude: styles,
test: /\.styl$/,
loaders: ['raw-loader', 'postcss-loader', 'stylus-loader'] },
       {
exclude: styles,
test: /\.less$/,
loaders: ['raw-loader', 'postcss-loader', 'less-loader']
}, {
exclude: styles,
test: /\.scss$|\.sass$/,
loaders: ['raw-loader', 'postcss-loader', 'sass-loader']
},


// load global scripts using script-loader
{ include: scripts, test: /\.js$/, loader: 'script-loader' },
{ enforce: 'pre', test: /\.js$/, loader: 'source-map-loader', exclude: [nodeModules] },

       { test: /\.json$/, loader: 'json-loader' },
       { test: /\.(jpg|png|gif)$/, loader: 'url-loader?limit=10000' },
       { test: /\.html$/, loader: 'raw-loader' },
{ test: /\.json$/, loader: 'json-loader' },
{ test: /\.(jpg|png|gif)$/, loader: 'url-loader?limit=10000' },
{ test: /\.html$/, loader: 'raw-loader' },

{ test: /\.(otf|ttf|woff|woff2)$/, loader: 'url-loader?limit=10000' },
{ test: /\.(eot|svg)$/, loader: 'file-loader' }
]
].concat(extraRules)
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(appRoot, appConfig.index),
filename: path.resolve(appConfig.outDir, appConfig.index),
chunksSortMode: packageChunkSort(['inline', 'styles', 'scripts', 'vendor', 'main'])
chunksSortMode: packageChunkSort(['inline', 'styles', 'scripts', 'vendor', 'main']),
excludeChunks: lazyChunks
}),
new BaseHrefWebpackPlugin({
baseHref: baseHref
Expand All @@ -130,12 +139,18 @@ export function getWebpackCommonConfig(
}),
new GlobCopyWebpackPlugin({
patterns: appConfig.assets,
globOptions: {cwd: appRoot, dot: true, ignore: '**/.gitkeep'}
globOptions: { cwd: appRoot, dot: true, ignore: '**/.gitkeep' }
}),
new webpack.LoaderOptionsPlugin({
test: /\.(css|scss|sass|less|styl)$/,
options: {
postcss: [ autoprefixer() ]
postcss: [autoprefixer()],
cssLoader: { sourceMap: sourcemap },
sassLoader: { sourceMap: sourcemap },
lessLoader: { sourceMap: sourcemap },
stylusLoader: { sourceMap: sourcemap },
// context needed as a workaround https://github.com/jtangelder/sass-loader/issues/285
context: path.resolve(__dirname, './')
},
})
].concat(extraPlugins),
Expand Down
31 changes: 5 additions & 26 deletions packages/angular-cli/models/webpack-build-development.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,18 @@
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

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 {
output: {
path: path.resolve(projectRoot, appConfig.outDir),
filename: '[name].bundle.js',
sourceMapFilename: '[name].bundle.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']
},
]
}
plugins: [
new ExtractTextPlugin({filename: '[name].bundle.css'})
]
};
};
Loading

0 comments on commit fa60928

Please sign in to comment.