1
1
import { Analyzer , BlockCompiler , StyleMapping } from "@css-blocks/core" ;
2
2
import { TemplateTypes } from "@opticss/template-api" ;
3
3
import * as debugGenerator from "debug" ;
4
- import * as fs from "fs-extra " ;
4
+ import * as fs from "fs" ;
5
5
import * as FSTree from "fs-tree-diff" ;
6
6
import * as glob from "glob" ;
7
+ import * as minimatch from "minimatch" ;
7
8
import { OptiCSSOptions , Optimizer , postcss } from "opticss" ;
8
9
import * as path from "path" ;
9
10
import * as walkSync from "walk-sync" ;
10
11
11
12
import { Transport } from "./Transport" ;
12
- import { BroccoliPlugin , symlinkOrCopy } from "./utils" ;
13
+ import { BroccoliPlugin } from "./utils" ;
13
14
14
15
const debug = debugGenerator ( "css-blocks:broccoli" ) ;
15
16
@@ -32,11 +33,9 @@ export class CSSBlocksAnalyze extends BroccoliPlugin {
32
33
private analyzer : Analyzer < keyof TemplateTypes > ;
33
34
private entries : string [ ] ;
34
35
private output : string ;
35
- private root : string ;
36
36
private transport : Transport ;
37
37
private optimizationOptions : Partial < OptiCSSOptions > ;
38
38
private previousInput : FSTree = new FSTree ( ) ;
39
- private previousOutput : FSTree = new FSTree ( ) ;
40
39
41
40
/**
42
41
* Initialize this new instance with the app tree, transport, and analysis options.
@@ -55,48 +54,52 @@ export class CSSBlocksAnalyze extends BroccoliPlugin {
55
54
this . output = options . output || "css-blocks.css" ;
56
55
this . optimizationOptions = options . optimization || { } ;
57
56
this . analyzer = options . analyzer ;
58
- this . root = options . root || process . cwd ( ) ;
59
57
this . transport . css = this . transport . css ? this . transport . css : "" ;
60
58
}
61
59
62
60
/**
63
61
* Re-run the broccoli build over supplied inputs.
64
62
*/
65
63
async build ( ) {
66
- let input = this . inputPaths [ 0 ] ;
64
+ // We currently rely on the fact that the input path is a source directory
65
+ // and not a temp directory from a previous build step.
66
+ // When the input directory is a temp directory, the block guid
67
+ // becomes unstable across brocolli build processes which can cause
68
+ // persistent cache incoherence within the templates.
69
+ let input = path . resolve ( this . inputPaths [ 0 ] ) ;
67
70
let output = this . outputPath ;
68
71
let options = this . analyzer . cssBlocksOptions ;
69
72
let blockCompiler = new BlockCompiler ( postcss , options ) ;
70
73
let optimizer = new Optimizer ( this . optimizationOptions , this . analyzer . optimizationOptions ) ;
71
74
75
+ let isBlockFile = minimatch . makeRe ( "**/*.block.*" ) ;
76
+
72
77
// Test if anything has changed since last time. If not, skip all analysis work.
73
78
let newFsTree = FSTree . fromEntries ( walkSync . entries ( input ) ) ;
74
79
let diff = this . previousInput . calculatePatch ( newFsTree ) ;
80
+ this . previousInput = newFsTree ;
75
81
if ( ! diff . length ) { return ; }
76
82
77
- // Save the current state of our output dir for future diffs.
78
- this . previousOutput = FSTree . fromEntries ( walkSync . entries ( output ) ) ;
79
- FSTree . applyPatch ( input , output , this . previousOutput . calculatePatch ( newFsTree ) ) ;
80
- this . previousInput = newFsTree ;
83
+ // Get all the operations for files that aren't related to CSS Block files
84
+ // So they can be performed on the output directory.
85
+ let nonBlockFileChanges = new Array < FSTree . Operation > ( ) ;
86
+ for ( let op of diff ) {
87
+ let entry = op [ 2 ] ;
88
+ if ( entry && entry . relativePath . match ( isBlockFile ) ) {
89
+ continue ;
90
+ }
91
+ nonBlockFileChanges . push ( op ) ;
92
+ }
93
+
94
+ FSTree . applyPatch ( input , output , nonBlockFileChanges ) ;
81
95
82
96
// When no entry points are passed, we treat *every* template as an entry point.
83
- this . entries = this . entries . length ? this . entries : glob . sync ( "**/*.hbs" , { cwd : output } ) ;
84
-
85
- // The glimmer-analyzer package tries to require() package.json
86
- // in the root of the directory it is passed. We pass it our broccoli
87
- // tree, so it needs to contain package.json too.
88
- // TODO: Ideally this is configurable in glimmer-analyzer. We can
89
- // contribute that option back to the project. However,
90
- // other template integrations may want this available too...
91
- let pjsonLink = path . join ( output , "package.json" ) ;
92
- if ( ! fs . existsSync ( pjsonLink ) ) {
93
- symlinkOrCopy ( path . join ( this . root , "package.json" ) , pjsonLink ) ;
94
- }
97
+ this . entries = this . entries . length ? this . entries : glob . sync ( "**/*.hbs" , { cwd : input } ) ;
95
98
96
99
// Oh hey look, we're analyzing.
97
100
this . analyzer . reset ( ) ;
98
101
this . transport . reset ( ) ;
99
- await this . analyzer . analyze ( output , this . entries ) ;
102
+ await this . analyzer . analyze ( input , this . entries ) ;
100
103
101
104
// Compile all Blocks and add them as sources to the Optimizer.
102
105
// TODO: handle a sourcemap from compiling the block file via a preprocessor.
@@ -108,10 +111,14 @@ export class CSSBlocksAnalyze extends BroccoliPlugin {
108
111
let filesystemPath = options . importer . filesystemPath ( block . identifier , options ) ;
109
112
let filename = filesystemPath || options . importer . debugIdentifier ( block . identifier , options ) ;
110
113
111
- // If this Block has a representation on disk, remove it from our output tree.
112
- if ( filesystemPath && ! ! ~ filesystemPath . indexOf ( output ) ) {
113
- debug ( `Removing block file ${ filesystemPath } from output.` ) ;
114
- if ( fs . existsSync ( filesystemPath ) ) { fs . removeSync ( filesystemPath ) ; }
114
+ if ( filesystemPath && filesystemPath . startsWith ( input ) ) {
115
+ let relativePath = path . relative ( input , filesystemPath ) ;
116
+ let outputPath = path . resolve ( output , relativePath ) ;
117
+ try {
118
+ fs . unlinkSync ( outputPath ) ;
119
+ } catch ( e ) {
120
+ // ignore
121
+ }
115
122
}
116
123
117
124
// Add the compiled Block file to the optimizer.
@@ -123,9 +130,6 @@ export class CSSBlocksAnalyze extends BroccoliPlugin {
123
130
}
124
131
}
125
132
126
- // Save the current state of our output dir for future diffs.
127
- this . previousOutput = FSTree . fromEntries ( walkSync . entries ( output ) ) ;
128
-
129
133
// Add each Analysis to the Optimizer.
130
134
this . analyzer . eachAnalysis ( ( a ) => optimizer . addAnalysis ( a . forOptimizer ( options ) ) ) ;
131
135
0 commit comments