Skip to content

Commit de7e25b

Browse files
committed
Merge pull request #4 from css-blocks/opticss
Opticss Integration
1 parent 91e8301 commit de7e25b

File tree

13 files changed

+4413
-140
lines changed

13 files changed

+4413
-140
lines changed

packages/webpack-plugin/package.json

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@css-blocks/webpack-plugin",
3-
"version": "0.12.2",
3+
"version": "0.15.0",
44
"description": "Webpack plugin for css-blocks.",
55
"main": "dist/src/index.js",
66
"scripts": {
@@ -27,7 +27,7 @@
2727
},
2828
"types": "dist/src",
2929
"files": [
30-
"dist",
30+
"dist/src",
3131
"*.md",
3232
"test/mocha.opts"
3333
],
@@ -40,6 +40,7 @@
4040
"@types/extract-text-webpack-plugin": "^2.1.0",
4141
"@types/glob": "^5.0.30",
4242
"@types/node": "^7.0.13",
43+
"@types/webpack": "^3.0.14",
4344
"@types/webpack-merge": "0.0.4",
4445
"@types/webpack-sources": "^0.1.2",
4546
"chai": "^3.5.0",
@@ -64,16 +65,17 @@
6465
"webpack-merge": "^4.1.0"
6566
},
6667
"dependencies": {
67-
"@types/webpack": "^3.0.14",
68+
"@opticss/element-analysis": "^0.2.0",
69+
"@opticss/template-api": "^0.2.0",
6870
"async": "^2.4.1",
6971
"convert-source-map": "^1.5.0",
70-
"css-blocks": "^0.12.1",
72+
"css-blocks": "^0.15.0",
7173
"debug": "^2.6.8",
7274
"install": "^0.10.1",
7375
"loader-utils": "^1.0.1",
76+
"opticss": "^0.2.0",
7477
"postcss": "^5.2.17",
7578
"source-map": "^0.5.6",
76-
"virtual-module-webpack-plugin": "^0.2.0",
7779
"webpack-sources": "^1.0.1"
7880
}
7981
}

packages/webpack-plugin/src/CssAssets.ts

+7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import * as fs from "fs";
55
import { Source, RawSource, SourceMapSource, ConcatSource } from "webpack-sources";
66
import { RawSourceMap } from "source-map";
77
import * as convertSourceMap from "convert-source-map";
8+
import * as debugGenerator from 'debug';
9+
10+
const debug = debugGenerator("css-blocks:webpack:assets");
811

912
/**
1013
* Options for managing CSS assets without javascript imports.
@@ -117,6 +120,7 @@ export class CssAssets {
117120
// TODO: get the watcher to watch these files on disk
118121
// TODO: Use loaders to get these files into the assets -- which may help with the watching.
119122
compiler.plugin("emit", (compilation, cb) => {
123+
debug("emitting assets");
120124
let assetPaths = Object.keys(this.options.cssFiles);
121125
async.forEach(assetPaths, (assetPath, outputCallback) => {
122126
let asset = this.options.cssFiles[assetPath];
@@ -158,6 +162,7 @@ export class CssAssets {
158162
// strings of assets that should be in the asset.
159163
// TODO: maybe some glob or regex support
160164
compiler.plugin("emit", (compilation, cb) => {
165+
debug("concatenating assets");
161166
if (!this.options.concat) return;
162167
let concatFiles = Object.keys(this.options.concat);
163168
concatFiles.forEach((concatFile) => {
@@ -192,9 +197,11 @@ export class CssAssets {
192197
// true (false by default)
193198
compiler.plugin("emit", (compilation, cb) => {
194199
if (!this.options.emitSourceMaps) {
200+
debug("not adding sourcemaps");
195201
cb();
196202
return;
197203
}
204+
debug("adding sourcemaps");
198205
let assetPaths = Object.keys(compilation.assets).filter(p => /\.css$/.test(p));
199206
assetPaths.forEach(assetPath => {
200207
let asset = compilation.assets[assetPath];

packages/webpack-plugin/src/Plugin.ts

+96-48
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,82 @@
1-
import Tapable = require("tapable");
1+
import Tapable = require('tapable');
22
import { Plugin as WebpackPlugin, Compiler as WebpackCompiler } from "webpack";
3-
import * as debugGenerator from "debug";
4-
import * as postcss from "postcss";
5-
import * as path from "path";
6-
import { SourceMapSource, ConcatSource, Source } from 'webpack-sources';
3+
import * as debugGenerator from 'debug';
4+
import * as postcss from 'postcss';
5+
import * as path from 'path';
6+
import { SourceMapConsumer, SourceMapGenerator } from "source-map";
7+
import { SourceMapSource, Source, RawSource } from 'webpack-sources';
78

89
import {
910
MultiTemplateAnalyzer,
10-
TemplateInfo,
1111
MetaTemplateAnalysis,
1212
Block,
1313
BlockCompiler,
1414
PluginOptions as CssBlocksOptions,
1515
PluginOptionsReader as CssBlocksOptionsReader,
16-
// StyleMapping,
17-
MetaStyleMapping,
16+
StyleMapping,
17+
TemplateAnalysis,
1818
} from "css-blocks";
19-
20-
export interface CssBlocksWebpackOptions<Template extends TemplateInfo> {
19+
import {
20+
TemplateTypes
21+
} from "@opticss/template-api";
22+
import {
23+
Optimizer,
24+
OptiCSSOptions,
25+
DEFAULT_OPTIONS,
26+
OptimizationResult,
27+
Actions,
28+
} from "opticss";
29+
30+
export interface CssBlocksWebpackOptions {
2131
/// The name of the instance of the plugin. Defaults to outputCssFile.
2232
name?: string;
2333
/// The analzyer that decides what templates are analyzed and what blocks will be compiled.
24-
analyzer: MultiTemplateAnalyzer<Template>;
34+
analyzer: MultiTemplateAnalyzer;
2535
/// The output css file for all compiled CSS Blocks. Defaults to "css-blocks.css"
2636
outputCssFile?: string;
2737
/// Compilation options pass to css-blocks
28-
compilationOptions?: CssBlocksOptions;
38+
compilationOptions?: Partial<CssBlocksOptions>;
39+
/// Optimization options passed to opticss
40+
optimization?: OptiCSSOptions;
2941
}
3042

31-
interface CompilationResult<Template extends TemplateInfo> {
32-
css: ConcatSource;
33-
mapping: MetaStyleMapping<Template>;
43+
export interface BlockCompilationError {
44+
compilation: any;
45+
assetPath: string;
46+
error: Error;
47+
mapping?: StyleMapping;
48+
optimizerActions?: Actions;
3449
}
35-
36-
export interface BlockCompilationComplete<Template extends TemplateInfo> {
50+
export interface BlockCompilationComplete {
3751
compilation: any;
3852
assetPath: string;
39-
mapping: MetaStyleMapping<Template>;
53+
mapping: StyleMapping;
54+
optimizerActions: Actions;
4055
}
4156

4257
interface Assets {
4358
[key: string]: Source;
4459
}
4560

46-
export class CssBlocksPlugin<Template extends TemplateInfo>
61+
interface CompilationResult {
62+
optimizationResult: OptimizationResult;
63+
blocks: Set<Block>;
64+
analyses: Array<TemplateAnalysis<keyof TemplateTypes>>;
65+
}
66+
67+
export class CssBlocksPlugin
4768
extends Tapable
4869
implements WebpackPlugin
4970
{
71+
optimizationOptions: OptiCSSOptions;
5072
name: string;
51-
analyzer: MultiTemplateAnalyzer<Template>;
73+
analyzer: MultiTemplateAnalyzer;
5274
projectDir: string;
5375
outputCssFile: string;
5476
compilationOptions: CssBlocksOptions;
5577
debug: (message: string) => void;
5678

57-
constructor(options: CssBlocksWebpackOptions<Template>) {
79+
constructor(options: CssBlocksWebpackOptions) {
5880
super();
5981

6082
this.debug = debugGenerator("css-blocks:webpack");
@@ -63,6 +85,8 @@ export class CssBlocksPlugin<Template extends TemplateInfo>
6385
this.name = options.name || this.outputCssFile;
6486
this.compilationOptions = options.compilationOptions || {};
6587
this.projectDir = process.cwd();
88+
this.optimizationOptions =
89+
Object.assign({}, DEFAULT_OPTIONS, options.optimization);
6690
}
6791

6892
apply(compiler: WebpackCompiler) {
@@ -77,7 +101,7 @@ export class CssBlocksPlugin<Template extends TemplateInfo>
77101
this.analyzer.reset();
78102

79103
// Try to run our analysis.
80-
let pendingResult: Promise<MetaStyleMapping<Template>> = this.analyzer.analyze()
104+
let pendingResult: Promise<StyleMapping | void> = this.analyzer.analyze()
81105

82106
// If analysis fails, drain our BlockFactory, add error to compilation error list and propagate.
83107
.catch((err) => {
@@ -90,23 +114,36 @@ export class CssBlocksPlugin<Template extends TemplateInfo>
90114

91115
// If analysis finished successfully, compile our blocks to output.
92116
.then(analysis => {
93-
return this.compileBlocks(<MetaTemplateAnalysis<Template>>analysis, path.join(outputPath, this.outputCssFile));
117+
return this.compileBlocks(analysis, path.join(outputPath, this.outputCssFile));
94118
})
95119

96120
// Add the resulting css output to our build.
97121
.then(result => {
98122
this.trace(`setting css asset: ${this.outputCssFile}`);
99-
assets[this.outputCssFile] = result.css;
100-
let completion: BlockCompilationComplete<Template> = {
123+
let source: Source;
124+
if (result.optimizationResult.output.sourceMap) {
125+
let consumer = new SourceMapConsumer(result.optimizationResult.output.sourceMap);
126+
let map = SourceMapGenerator.fromSourceMap(consumer);
127+
source = new SourceMapSource(
128+
result.optimizationResult.output.content.toString(),
129+
"optimized css",
130+
map.toJSON());
131+
} else {
132+
source = new RawSource(result.optimizationResult.output.content.toString());
133+
}
134+
assets[`${this.outputCssFile}.log`] = new RawSource(result.optimizationResult.actions.performed.map(a => a.logString()).join("\n"));
135+
assets[this.outputCssFile] = source;
136+
let completion: BlockCompilationComplete = {
101137
compilation: compilation,
102138
assetPath: this.outputCssFile,
103-
mapping: result.mapping
139+
mapping: new StyleMapping(result.optimizationResult.styleMapping, result.blocks, new CssBlocksOptionsReader(this.compilationOptions), result.analyses),
140+
optimizerActions: result.optimizationResult.actions,
104141
};
105142
return completion;
106143
})
107144

108145
// Notify the world when complete.
109-
.then<BlockCompilationComplete<Template>>((completion) => {
146+
.then<BlockCompilationComplete>((completion) => {
110147
this.trace(`notifying of completion`);
111148
this.notifyComplete(completion, cb);
112149
this.trace(`notified of completion`);
@@ -121,17 +158,15 @@ export class CssBlocksPlugin<Template extends TemplateInfo>
121158
// If something bad happened, log the error and pretend like nothing happened
122159
// by notifying deps of completion and returning an empty MetaStyleMapping
123160
// so compilation can continue.
124-
.catch((err) => {
161+
.catch((error) => {
125162
this.trace(`notifying of compilation failure`);
126-
compilation.errors.push(err);
127-
let mapping = new MetaStyleMapping<Template>();
163+
compilation.errors.push(error);
128164
this.notifyComplete({
129-
compilation: compilation,
165+
error,
166+
compilation,
130167
assetPath: this.outputCssFile,
131-
mapping: mapping
132168
}, cb);
133169
this.trace(`notified of compilation failure`);
134-
return mapping;
135170
});
136171

137172
compilation.plugin("normal-module-loader", (context: any, mod: any) => {
@@ -158,46 +193,59 @@ export class CssBlocksPlugin<Template extends TemplateInfo>
158193

159194
});
160195
}
161-
private compileBlocks(analysis: MetaTemplateAnalysis<Template>, cssOutputName: string): CompilationResult<Template> {
196+
private compileBlocks(analysis: MetaTemplateAnalysis, cssOutputName: string): Promise<CompilationResult> {
162197
let options: CssBlocksOptions = this.compilationOptions;
163198
let reader = new CssBlocksOptionsReader(options);
164199
let blockCompiler = new BlockCompiler(postcss, options);
165-
let cssBundle = new ConcatSource();
166200
let numBlocks = 0;
167-
analysis.blockDependencies().forEach((block: Block) => {
201+
let optimizer = new Optimizer(this.optimizationOptions, analysis.optimizationOptions());
202+
let blocks = analysis.transitiveBlockDependencies();
203+
for (let block of blocks) {
168204
if (block.root && block.identifier) {
205+
blocks.add(block);
169206
this.trace(`compiling ${block.identifier}.`);
170-
let originalSource = block.root.toString();
171207
let root = blockCompiler.compile(block, block.root, analysis);
172208
let result = root.toResult({to: cssOutputName, map: { inline: false, annotation: false }});
173209
// TODO: handle a sourcemap from compiling the block file via a preprocessor.
174210
let filename = reader.importer.filesystemPath(block.identifier, reader) || reader.importer.debugIdentifier(block.identifier, reader);
175-
let source = new SourceMapSource(result.css, filename, result.map.toJSON(), originalSource);
176-
cssBundle.add(source);
211+
optimizer.addSource({
212+
content: result.css,
213+
filename,
214+
sourceMap: result.map.toJSON()
215+
});
177216
numBlocks++;
178217
}
218+
}
219+
let analyses = new Array<TemplateAnalysis<keyof TemplateTypes>>();
220+
analysis.eachAnalysis(a => {
221+
this.trace(`Adding analysis for ${a.template.identifier} to optimizer.`);
222+
this.trace(`Analysis for ${a.template.identifier} has ${a.elementCount()} elements.`);
223+
analyses.push(a);
224+
optimizer.addAnalysis(a.forOptimizer(reader));
179225
});
180226
this.trace(`compiled ${numBlocks} blocks.`);
181-
let metaMapping = MetaStyleMapping.fromMetaAnalysis(analysis, reader);
182-
return {
183-
css: cssBundle,
184-
mapping: metaMapping
185-
};
227+
return optimizer.optimize(cssOutputName).then(optimizationResult => {
228+
return {
229+
optimizationResult,
230+
blocks,
231+
analyses,
232+
};
233+
});
186234
}
187235
trace(message: string) {
188236
message = message.replace(this.projectDir + "/", "");
189237
this.debug(`[${this.name}] ${message}`);
190238
}
191-
onPendingCompilation(handler: (pendingResult: Promise<MetaStyleMapping<Template>>) => void) {
239+
onPendingCompilation(handler: (pendingResult: Promise<StyleMapping | void>) => void) {
192240
this.plugin("block-compilation-pending", handler);
193241
}
194-
notifyPendingCompilation(pendingResult: Promise<MetaStyleMapping<Template>>) {
242+
notifyPendingCompilation(pendingResult: Promise<StyleMapping | void>) {
195243
this.applyPlugins("block-compilation-pending", pendingResult);
196244
}
197-
onComplete(handler: (result: BlockCompilationComplete<Template>, cb: (err: Error) => void) => void) {
245+
onComplete(handler: (result: BlockCompilationComplete | BlockCompilationError, cb: (err: Error) => void) => void) {
198246
this.plugin("block-compilation-complete", handler);
199247
}
200-
notifyComplete(result: BlockCompilationComplete<Template>, cb: (err: Error) => void) {
248+
notifyComplete(result: BlockCompilationComplete | BlockCompilationError, cb: (err: Error) => void) {
201249
this.applyPluginsAsync("block-compilation-complete", result, cb);
202250
}
203251
}

packages/webpack-plugin/src/loader.ts

+21-23
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,24 @@ import cssBlocks from "css-blocks";
99
* @this {loader.LoaderContext}
1010
* @param {string} content
1111
*/
12-
const blockLoader: loader.Loader = function(content: string) {
13-
const callback = this.async();
14-
if (typeof callback !== "function") {
15-
throw new Error("synchronous compilation is not supported");
16-
}
17-
this.cacheable();
18-
let thisLoader = this.loaders[this.loaderIndex];
19-
let options;
20-
if (thisLoader.options) {
21-
options = thisLoader.options;
22-
} else {
23-
options = loaderUtils.getOptions(this);
24-
}
25-
let plugin = cssBlocks(postcss)(options);
26-
let result = postcss([plugin]).process(content, {from: this.resourcePath});
27-
result.then((result) => {
28-
callback(null, result.css);
29-
}, (error) => {
30-
callback(error);
31-
});
32-
};
33-
34-
export = blockLoader;
12+
export default function blockLoader(this: loader.LoaderContext, content: string) {
13+
const callback = this.async();
14+
if (typeof callback !== "function") {
15+
throw new Error("synchronous compilation is not supported");
16+
}
17+
this.cacheable();
18+
let thisLoader = this.loaders[this.loaderIndex];
19+
let options;
20+
if (thisLoader.options) {
21+
options = thisLoader.options;
22+
} else {
23+
options = loaderUtils.getOptions(this);
24+
}
25+
let plugin = cssBlocks(postcss)(options);
26+
let result = postcss([plugin]).process(content, { from: this.resourcePath });
27+
result.then((result) => {
28+
callback(null, result.css);
29+
}, (error) => {
30+
callback(error);
31+
});
32+
}

0 commit comments

Comments
 (0)