-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
PostHTMLTransformer.js
134 lines (115 loc) Β· 3.53 KB
/
PostHTMLTransformer.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
133
134
// @flow
import {Transformer} from '@parcel/plugin';
import path from 'path';
import posthtml from 'posthtml';
import {parser as parse} from 'posthtml-parser';
import {render} from 'posthtml-render';
import nullthrows from 'nullthrows';
import semver from 'semver';
import loadPlugins from './loadPlugins';
export default (new Transformer({
async loadConfig({config, options, logger}) {
if (!config.isSource) {
return;
}
let configFile = await config.getConfig(
[
'.posthtmlrc',
'.posthtmlrc.js',
'.posthtmlrc.cjs',
'.posthtmlrc.mjs',
'posthtml.config.js',
'posthtml.config.cjs',
'posthtml.config.mjs',
],
{
packageKey: 'posthtml',
},
);
if (configFile) {
let isJavascript = path.extname(configFile.filePath).endsWith('js');
if (isJavascript) {
// We have to invalidate on startup in case the config is non-deterministic,
// e.g. using unknown environment variables, reading from the filesystem, etc.
logger.warn({
message:
'WARNING: Using a JavaScript PostHTML config file means losing out on caching features of Parcel. Use a .posthtmlrc (JSON) file whenever possible.',
});
}
// Load plugins. This must be done before adding dev dependencies so we auto install.
let plugins = await loadPlugins(
configFile.contents.plugins,
config.searchPath,
options,
);
// Now add dev dependencies so we invalidate when they change.
let pluginArray = Array.isArray(configFile.contents.plugins)
? configFile.contents.plugins
: Object.keys(configFile.contents.plugins);
for (let p of pluginArray) {
if (typeof p === 'string') {
config.addDevDependency({
specifier: p,
resolveFrom: configFile.filePath,
});
}
}
configFile.contents.plugins = plugins;
// tells posthtml that we have already called parse
configFile.contents.skipParse = true;
delete configFile.contents.render;
return configFile.contents;
}
},
canReuseAST({ast}) {
return ast.type === 'posthtml' && semver.satisfies(ast.version, '^0.4.0');
},
async parse({asset, config}) {
// if we don't have a config it is posthtml is not configure, don't parse
if (!config) {
return;
}
return {
type: 'posthtml',
version: '0.4.1',
program: parse(await asset.getCode(), {
lowerCaseAttributeNames: true,
sourceLocations: true,
xmlMode: asset.type === 'xhtml',
}),
};
},
async transform({asset, config}) {
if (!config) {
return [asset];
}
let ast = nullthrows(await asset.getAST());
let res = await posthtml(config.plugins).process(ast.program, {
...config,
plugins: config.plugins,
});
if (res.messages) {
await Promise.all(
res.messages.map(({type, file: filePath}) => {
if (type === 'dependency') {
return asset.invalidateOnFileChange(filePath);
}
return Promise.resolve();
}),
);
}
asset.setAST({
type: 'posthtml',
version: '0.4.1',
program: JSON.parse(JSON.stringify(res.tree)), // posthtml adds functions to the AST that are not serializable
});
return [asset];
},
generate({ast, asset}) {
return {
content: render(ast.program, {
closingSingleTag: asset.type === 'xhtml' ? 'slash' : undefined,
}),
};
},
}): Transformer);