1
- import Tapable = require( " tapable" ) ;
1
+ import Tapable = require( ' tapable' ) ;
2
2
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' ;
7
8
8
9
import {
9
10
MultiTemplateAnalyzer ,
10
- TemplateInfo ,
11
11
MetaTemplateAnalysis ,
12
12
Block ,
13
13
BlockCompiler ,
14
14
PluginOptions as CssBlocksOptions ,
15
15
PluginOptionsReader as CssBlocksOptionsReader ,
16
- // StyleMapping,
17
- MetaStyleMapping ,
16
+ StyleMapping ,
17
+ TemplateAnalysis ,
18
18
} 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 {
21
31
/// The name of the instance of the plugin. Defaults to outputCssFile.
22
32
name ?: string ;
23
33
/// The analzyer that decides what templates are analyzed and what blocks will be compiled.
24
- analyzer : MultiTemplateAnalyzer < Template > ;
34
+ analyzer : MultiTemplateAnalyzer ;
25
35
/// The output css file for all compiled CSS Blocks. Defaults to "css-blocks.css"
26
36
outputCssFile ?: string ;
27
37
/// Compilation options pass to css-blocks
28
- compilationOptions ?: CssBlocksOptions ;
38
+ compilationOptions ?: Partial < CssBlocksOptions > ;
39
+ /// Optimization options passed to opticss
40
+ optimization ?: OptiCSSOptions ;
29
41
}
30
42
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 ;
34
49
}
35
-
36
- export interface BlockCompilationComplete < Template extends TemplateInfo > {
50
+ export interface BlockCompilationComplete {
37
51
compilation : any ;
38
52
assetPath : string ;
39
- mapping : MetaStyleMapping < Template > ;
53
+ mapping : StyleMapping ;
54
+ optimizerActions : Actions ;
40
55
}
41
56
42
57
interface Assets {
43
58
[ key : string ] : Source ;
44
59
}
45
60
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
47
68
extends Tapable
48
69
implements WebpackPlugin
49
70
{
71
+ optimizationOptions : OptiCSSOptions ;
50
72
name : string ;
51
- analyzer : MultiTemplateAnalyzer < Template > ;
73
+ analyzer : MultiTemplateAnalyzer ;
52
74
projectDir : string ;
53
75
outputCssFile : string ;
54
76
compilationOptions : CssBlocksOptions ;
55
77
debug : ( message : string ) => void ;
56
78
57
- constructor ( options : CssBlocksWebpackOptions < Template > ) {
79
+ constructor ( options : CssBlocksWebpackOptions ) {
58
80
super ( ) ;
59
81
60
82
this . debug = debugGenerator ( "css-blocks:webpack" ) ;
@@ -63,6 +85,8 @@ export class CssBlocksPlugin<Template extends TemplateInfo>
63
85
this . name = options . name || this . outputCssFile ;
64
86
this . compilationOptions = options . compilationOptions || { } ;
65
87
this . projectDir = process . cwd ( ) ;
88
+ this . optimizationOptions =
89
+ Object . assign ( { } , DEFAULT_OPTIONS , options . optimization ) ;
66
90
}
67
91
68
92
apply ( compiler : WebpackCompiler ) {
@@ -77,7 +101,7 @@ export class CssBlocksPlugin<Template extends TemplateInfo>
77
101
this . analyzer . reset ( ) ;
78
102
79
103
// Try to run our analysis.
80
- let pendingResult : Promise < MetaStyleMapping < Template > > = this . analyzer . analyze ( )
104
+ let pendingResult : Promise < StyleMapping | void > = this . analyzer . analyze ( )
81
105
82
106
// If analysis fails, drain our BlockFactory, add error to compilation error list and propagate.
83
107
. catch ( ( err ) => {
@@ -90,23 +114,36 @@ export class CssBlocksPlugin<Template extends TemplateInfo>
90
114
91
115
// If analysis finished successfully, compile our blocks to output.
92
116
. then ( analysis => {
93
- return this . compileBlocks ( < MetaTemplateAnalysis < Template > > analysis , path . join ( outputPath , this . outputCssFile ) ) ;
117
+ return this . compileBlocks ( analysis , path . join ( outputPath , this . outputCssFile ) ) ;
94
118
} )
95
119
96
120
// Add the resulting css output to our build.
97
121
. then ( result => {
98
122
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 = {
101
137
compilation : compilation ,
102
138
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 ,
104
141
} ;
105
142
return completion ;
106
143
} )
107
144
108
145
// Notify the world when complete.
109
- . then < BlockCompilationComplete < Template > > ( ( completion ) => {
146
+ . then < BlockCompilationComplete > ( ( completion ) => {
110
147
this . trace ( `notifying of completion` ) ;
111
148
this . notifyComplete ( completion , cb ) ;
112
149
this . trace ( `notified of completion` ) ;
@@ -121,17 +158,15 @@ export class CssBlocksPlugin<Template extends TemplateInfo>
121
158
// If something bad happened, log the error and pretend like nothing happened
122
159
// by notifying deps of completion and returning an empty MetaStyleMapping
123
160
// so compilation can continue.
124
- . catch ( ( err ) => {
161
+ . catch ( ( error ) => {
125
162
this . trace ( `notifying of compilation failure` ) ;
126
- compilation . errors . push ( err ) ;
127
- let mapping = new MetaStyleMapping < Template > ( ) ;
163
+ compilation . errors . push ( error ) ;
128
164
this . notifyComplete ( {
129
- compilation : compilation ,
165
+ error,
166
+ compilation,
130
167
assetPath : this . outputCssFile ,
131
- mapping : mapping
132
168
} , cb ) ;
133
169
this . trace ( `notified of compilation failure` ) ;
134
- return mapping ;
135
170
} ) ;
136
171
137
172
compilation . plugin ( "normal-module-loader" , ( context : any , mod : any ) => {
@@ -158,46 +193,59 @@ export class CssBlocksPlugin<Template extends TemplateInfo>
158
193
159
194
} ) ;
160
195
}
161
- private compileBlocks ( analysis : MetaTemplateAnalysis < Template > , cssOutputName : string ) : CompilationResult < Template > {
196
+ private compileBlocks ( analysis : MetaTemplateAnalysis , cssOutputName : string ) : Promise < CompilationResult > {
162
197
let options : CssBlocksOptions = this . compilationOptions ;
163
198
let reader = new CssBlocksOptionsReader ( options ) ;
164
199
let blockCompiler = new BlockCompiler ( postcss , options ) ;
165
- let cssBundle = new ConcatSource ( ) ;
166
200
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 ) {
168
204
if ( block . root && block . identifier ) {
205
+ blocks . add ( block ) ;
169
206
this . trace ( `compiling ${ block . identifier } .` ) ;
170
- let originalSource = block . root . toString ( ) ;
171
207
let root = blockCompiler . compile ( block , block . root , analysis ) ;
172
208
let result = root . toResult ( { to : cssOutputName , map : { inline : false , annotation : false } } ) ;
173
209
// TODO: handle a sourcemap from compiling the block file via a preprocessor.
174
210
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
+ } ) ;
177
216
numBlocks ++ ;
178
217
}
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 ) ) ;
179
225
} ) ;
180
226
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
+ } ) ;
186
234
}
187
235
trace ( message : string ) {
188
236
message = message . replace ( this . projectDir + "/" , "" ) ;
189
237
this . debug ( `[${ this . name } ] ${ message } ` ) ;
190
238
}
191
- onPendingCompilation ( handler : ( pendingResult : Promise < MetaStyleMapping < Template > > ) => void ) {
239
+ onPendingCompilation ( handler : ( pendingResult : Promise < StyleMapping | void > ) => void ) {
192
240
this . plugin ( "block-compilation-pending" , handler ) ;
193
241
}
194
- notifyPendingCompilation ( pendingResult : Promise < MetaStyleMapping < Template > > ) {
242
+ notifyPendingCompilation ( pendingResult : Promise < StyleMapping | void > ) {
195
243
this . applyPlugins ( "block-compilation-pending" , pendingResult ) ;
196
244
}
197
- onComplete ( handler : ( result : BlockCompilationComplete < Template > , cb : ( err : Error ) => void ) => void ) {
245
+ onComplete ( handler : ( result : BlockCompilationComplete | BlockCompilationError , cb : ( err : Error ) => void ) => void ) {
198
246
this . plugin ( "block-compilation-complete" , handler ) ;
199
247
}
200
- notifyComplete ( result : BlockCompilationComplete < Template > , cb : ( err : Error ) => void ) {
248
+ notifyComplete ( result : BlockCompilationComplete | BlockCompilationError , cb : ( err : Error ) => void ) {
201
249
this . applyPluginsAsync ( "block-compilation-complete" , result , cb ) ;
202
250
}
203
251
}
0 commit comments