Skip to content

Commit ecf8d51

Browse files
committed
chore(css): css preprocessors support enhancement
1 parent 1945e85 commit ecf8d51

File tree

9 files changed

+144
-7
lines changed

9 files changed

+144
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body {
2+
background: rgba(0, 0, 0, 0.1);
3+
}

addon/ng2/blueprints/ng2/files/angular-cli.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
{
88
"main": "<%= sourceDir %>/main.ts",
99
"tsconfig": "<%= sourceDir %>/tsconfig.json",
10-
"mobile": <%= isMobile %>
10+
"mobile": <%= isMobile %>,
11+
"styles": {
12+
"src/style.css": { "output": "style.css", "autoImported": true }
13+
}
1114
}
1215
],
1316
"addons": [],

addon/ng2/models/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from './webpack-build-production';
44
export * from './webpack-build-development';
55
export * from './webpack-build-mobile';
66
export * from './webpack-build-utils';
7+
export * from './webpack-build-css';

addon/ng2/models/webpack-build-common.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { CliConfig } from './config';
77

88
export function getWebpackCommonConfig(projectRoot: string, sourceDir: string) {
99
return {
10+
name: 'main',
1011
devtool: 'inline-source-map',
1112
resolve: {
1213
extensions: ['', '.ts', '.js'],
@@ -73,7 +74,10 @@ export function getWebpackCommonConfig(projectRoot: string, sourceDir: string) {
7374
filename: 'inline.js',
7475
sourceMapFilename: 'inline.map'
7576
}),
76-
new CopyWebpackPlugin([{from: path.resolve(projectRoot, './public'), to: path.resolve(projectRoot, './dist')}])
77+
new CopyWebpackPlugin([{
78+
from: path.resolve(projectRoot, './public'),
79+
to: path.resolve(projectRoot, './dist')
80+
}])
7781
],
7882
node: {
7983
global: 'window',

addon/ng2/models/webpack-build-css.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as path from 'path';
2+
import * as ExtractTextPlugin from 'extract-text-webpack-plugin';
3+
import * as existsSync from 'exists-sync';
4+
import { InjectStylesWebpackPlugin } from '../utilities/inject-styles-webpack-plugin';
5+
import { CliConfig } from './config';
6+
7+
export function getWebpackCSSConfig(projectRoot: string, sourceDir: string) {
8+
const styles = CliConfig.fromProject().apps.map(app => app.styles);
9+
let entries = {};
10+
11+
styles.forEach(style => {
12+
for (let src in style) {
13+
if (existsSync(path.resolve(projectRoot, src))) {
14+
entries[style[src].output] = `./${src}`;
15+
}
16+
}
17+
});
18+
19+
return {
20+
name: 'styles',
21+
resolve: {
22+
root: path.resolve(projectRoot)
23+
},
24+
entry: entries,
25+
output: {
26+
path: path.resolve(projectRoot, './dist'),
27+
filename: '[name]'
28+
},
29+
module: {
30+
loaders: [
31+
{ test: /\.css$/i, loader: ExtractTextPlugin.extract(['css-loader']) },
32+
{ test: /\.sass$|\.scss$/i, loader: ExtractTextPlugin.extract(['css-loader', 'sass-loader']) },
33+
{ test: /\.less$/i, loader: ExtractTextPlugin.extract(['css-loader', 'less-loader']) },
34+
{ test: /\.styl$/i, loader: ExtractTextPlugin.extract(['css-loader', 'stylus-loader']) }
35+
]
36+
},
37+
plugins: [
38+
new ExtractTextPlugin('[name]'),
39+
new InjectStylesWebpackPlugin({
40+
template: path.resolve(projectRoot, sourceDir, 'index.html'),
41+
styles: styles,
42+
projectRoot: projectRoot
43+
});
44+
]
45+
}
46+
};

addon/ng2/models/webpack-config.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
getWebpackProdConfigPartial,
1010
getWebpackMobileConfigPartial,
1111
getWebpackMobileProdConfigPartial
12+
getWebpackCSSConfig
1213
} from './';
1314

1415
export class NgCliWebpackConfig {
@@ -22,15 +23,18 @@ export class NgCliWebpackConfig {
2223
private webpackMaterialE2EConfig: any;
2324
private webpackMobileConfigPartial: any;
2425
private webpackMobileProdConfigPartial: any;
26+
private webpackCSSConfig: any;
2527

2628
constructor(public ngCliProject: any, public target: string, public environment: string) {
2729
const sourceDir = CliConfig.fromProject().defaults.sourceDir;
30+
const publicDir = 'public';
2831

2932
const environmentPath = `./${sourceDir}/app/environments/environment.${environment}.ts`;
3033

3134
this.webpackBaseConfig = getWebpackCommonConfig(this.ngCliProject.root, sourceDir);
3235
this.webpackDevConfigPartial = getWebpackDevConfigPartial(this.ngCliProject.root, sourceDir);
3336
this.webpackProdConfigPartial = getWebpackProdConfigPartial(this.ngCliProject.root, sourceDir);
37+
this.webpackCSSConfig = getWebpackCSSConfig(this.ngCliProject.root, sourceDir);
3438

3539
if (CliConfig.fromProject().apps[0].mobile){
3640
this.webpackMobileConfigPartial = getWebpackMobileConfigPartial(this.ngCliProject.root, sourceDir);
@@ -40,7 +44,7 @@ export class NgCliWebpackConfig {
4044
}
4145

4246
this.generateConfig();
43-
this.config.plugins.unshift(new NgCliEnvironmentPlugin({
47+
this.config[0].plugins.unshift(new NgCliEnvironmentPlugin({
4448
path: path.resolve(this.ngCliProject.root, `./${sourceDir}/app/environments/`),
4549
src: 'environment.ts',
4650
dest: `environment.${this.environment}.ts`
@@ -50,10 +54,16 @@ export class NgCliWebpackConfig {
5054
generateConfig(): void {
5155
switch (this.target) {
5256
case "development":
53-
this.config = webpackMerge(this.webpackBaseConfig, this.webpackDevConfigPartial);
57+
this.config = [
58+
webpackMerge(this.webpackBaseConfig, this.webpackDevConfigPartial),
59+
this.webpackCSSConfig
60+
];
5461
break;
5562
case "production":
56-
this.config = webpackMerge(this.webpackBaseConfig, this.webpackProdConfigPartial);
63+
this.config = [
64+
webpackMerge(this.webpackBaseConfig, this.webpackProdConfigPartial),
65+
this.webpackCSSConfig
66+
];
5767
break;
5868
default:
5969
throw new Error("Invalid build target. Only 'development' and 'production' are available.");

addon/ng2/tasks/serve-webpack.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module.exports = Task.extend({
1818
var config: NgCliWebpackConfig = new NgCliWebpackConfig(this.project, commandOptions.target, commandOptions.environment).config;
1919
// This allows for live reload of page when changes are made to repo.
2020
// https://webpack.github.io/docs/webpack-dev-server.html#inline-mode
21-
config.entry.main.unshift(`webpack-dev-server/client?http://${commandOptions.host}:${commandOptions.port}/`);
21+
config[0].entry.main.unshift(`webpack-dev-server/client?http://${commandOptions.host}:${commandOptions.port}/`);
2222
webpackCompiler = webpack(config);
2323

2424
webpackCompiler.apply(new ProgressPlugin({
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
import * as parse5 from 'parse5';
4+
5+
export class InjectStylesWebpackPlugin {
6+
private interval;
7+
8+
constructor(options: any) {
9+
this.options = Object.assign({}, this.options, options);
10+
}
11+
12+
apply(compiler) {
13+
compiler.plugin('done', () => {
14+
this.importStyles();
15+
});
16+
}
17+
18+
importStyles() {
19+
let imports = this.getImports();
20+
let document = parse5.parse(this.readFileSync(this.options.template))
21+
let head = document.childNodes[1].childNodes[0];
22+
let links = head.childNodes.filter(childNode => childNode.nodeName === 'link');
23+
let hrefs = new Set(links.map(link => link.attrs.filter(attr => attr.name === 'href')[0].value));
24+
imports = imports.filter(i => !hrefs.has(i));
25+
26+
let adapter = parse5.treeAdapters.default;
27+
28+
imports.forEach(i => {
29+
let linkEl = adapter.createElement('link', null, [
30+
{ name: 'rel', value: 'stylesheet' },
31+
{ name: 'href', value: i }
32+
]);
33+
adapter.appendChild(head, linkEl);
34+
});
35+
36+
if (imports.length) {
37+
fs.writeFileSync(this.options.template, parse5.serialize(document), 'utf8');
38+
}
39+
}
40+
41+
getImports() {
42+
let imports = [];
43+
44+
this.options.styles.forEach(style => {
45+
for (let src in style) {
46+
if (this.existsSync(path.resolve(this.options.projectRoot, src))) {
47+
if (style[src].autoImported) {
48+
imports.push(style[src].output);
49+
}
50+
}
51+
}
52+
});
53+
54+
return imports;
55+
}
56+
57+
readFileSync(path: string) {
58+
return (this.existsSync(path)) ? fs.readFileSync(path, 'utf8') : false;
59+
}
60+
61+
existsSync(path: string) {
62+
try {
63+
fs.accessSync(path);
64+
return true;
65+
} catch (e) {
66+
return false;
67+
}
68+
}
69+
}

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"exit": "^0.1.2",
4949
"exports-loader": "^0.6.3",
5050
"expose-loader": "^0.7.1",
51+
"extract-text-webpack-plugin": "^2.0.0-beta.3",
5152
"file-loader": "^0.8.5",
5253
"fs-extra": "^0.30.0",
5354
"glob": "^7.0.3",
@@ -74,7 +75,7 @@
7475
"remap-istanbul": "^0.6.4",
7576
"resolve": "^1.1.7",
7677
"rimraf": "^2.5.3",
77-
"sass-loader": "^3.2.0",
78+
"sass-loader": "^3.2.3",
7879
"shelljs": "^0.7.0",
7980
"silent-error": "^1.0.0",
8081
"source-map-loader": "^0.1.5",

0 commit comments

Comments
 (0)