-
Notifications
You must be signed in to change notification settings - Fork 19
/
rewriter.js
132 lines (97 loc) · 4.08 KB
/
rewriter.js
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
"use strict";
const path = require("path");
const MagicString = require("magic-string");
const dedent = require("dedent");
const { DepGraph } = require("dependency-graph");
const formats = {
es : require("./formats/es.js"),
amd : require("./formats/amd.js"),
system : require("./formats/system.js"),
// Just an alias...
esm : require("./formats/es.js"),
};
const supported = new Set(Object.keys(formats).sort());
module.exports = (opts) => {
const options = Object.assign(Object.create(null), {
loader : false,
loadfn : false,
verbose : false,
}, opts);
if(!options.loadfn) {
throw new Error("options.loadfn must be configured");
}
// eslint-disable-next-line no-console, no-empty-function
const log = options.verbose ? console.log.bind(console, "[rewriter]") : () => {};
return {
name : "@modular-css/rollup-rewriter",
generateBundle({ format }, chunks) {
if(!supported.has(format)) {
// This throws, so execution stops here even though it doesn't look like it
this.error(`Unsupported format: ${format}. Supported formats are ${JSON.stringify([ ...supported.values() ])}`);
}
const entries = new Map();
const graph = new DepGraph({ circular : true });
Object.entries(chunks).forEach(([ entry, chunk ]) => {
const { isAsset, dynamicImports } = chunk;
if(isAsset) {
return;
}
// Guard against https://github.com/rollup/rollup/issues/2659
const imported = dynamicImports.filter(Boolean);
if(imported.length) {
entries.set(entry, imported);
}
graph.addNode(entry);
imported.forEach((file) => {
graph.addNode(file);
graph.addDependency(entry, file);
});
});
entries.forEach((deps, entry) => {
const { code } = chunks[entry];
const { regex, loader, load } = formats[format];
const search = regex(deps);
const str = new MagicString(code);
if(options.loader) {
const content = typeof options.loader === "function" ?
options.loader({ chunks, options }) :
options.loader;
loader({ content, str });
}
// Yay stateful regexes
search.lastIndex = 0;
let result = search.exec(code);
while(result) {
// Pull useful values out of the regex result
const [ statement, ident ] = result;
const { index } = result;
// TODO: is assuming .js safe here?
const file = path.extname(ident).length ? ident : `${ident}.js`;
// eslint-disable-next-line no-loop-func
const css = [
...graph.dependenciesOf(file),
...(file in chunks ? chunks[file].imports : []),
file,
].reduce((out, curr) => {
const { assets = [] } = chunks[curr];
assets.forEach((asset) => out.add(asset));
return out;
}, new Set());
if(css.size) {
const imports = [ ...css ].map((dep) =>
`${options.loadfn}("./${dep}")`
);
str.overwrite(
index,
index + statement.length,
dedent(load(options, imports.join(",\n"), statement))
);
}
result = search.exec(code);
}
log("Updating", entry);
chunks[entry].code = str.toString();
});
},
};
};