/
index.ts
108 lines (87 loc) · 2.68 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import assert from 'assert';
import { join, basename } from 'path';
import { sort } from '@compiled/css';
import { hash } from '@compiled/utils';
import { Optimizer } from '@parcel/plugin';
import posthtml from 'posthtml';
import { insertAt } from 'posthtml-insert-at';
import type { ParcelOptimizerOpts } from './types';
const configFiles = [
'.compiledcssrc',
'.compiledcssrc.json',
'compiledcss.js',
'compiledcss.config.js',
];
export default new Optimizer<ParcelOptimizerOpts, unknown>({
async loadConfig({ config, options }) {
const conf = await config.getConfigFrom(join(options.projectRoot, 'index'), configFiles, {
packageKey: '@compiled/parcel-optimizer',
});
const contents = {
inlineCss: false,
};
if (conf) {
config.invalidateOnStartup();
Object.assign(contents, conf.contents);
}
return contents;
},
async optimize({ contents, map, bundle, bundleGraph, options, config }) {
const { outputFS } = options;
const styleRules = new Set<string>();
// Traverse the descendants of HTML bundle
// Extract the stylesRules from assets
bundleGraph.traverseBundles((childBundle) => {
childBundle.traverseAssets((asset) => {
const rules = asset.meta.styleRules;
if (rules == null) {
return;
}
assert(rules instanceof Array);
for (const rule of rules) {
styleRules.add(rule as string);
}
});
}, bundle);
if (styleRules.size === 0) return { contents, map };
const stylesheet = sort(Array.from(styleRules).join(''));
let newContents = '';
if (config.inlineCss) {
newContents = (
await posthtml()
.use(
insertAt({
selector: 'head',
append: '<style>' + stylesheet + '</style>',
behavior: 'inside',
})
)
.process(contents.toString())
).html;
} else {
const { distDir } = bundle.target;
if (!outputFS.existsSync(distDir)) {
await outputFS.mkdirp(distDir);
}
const cssFileName = basename(bundle.displayName, '.html') + '.' + hash(stylesheet) + '.css';
await outputFS.writeFile(
join(distDir, cssFileName),
stylesheet,
undefined // for TypeScript
);
newContents = (
await posthtml()
.use(
insertAt({
selector: 'head',
append:
'<link href="' + bundle.target.publicUrl + cssFileName + '" rel="stylesheet" />',
behavior: 'inside',
})
)
.process(contents.toString())
).html;
}
return { contents: newContents, map };
},
});