forked from sveltejs/svelte
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
108 lines (94 loc) · 2.96 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 { SourceMap } from 'magic-string';
import replaceAsync from '../utils/replaceAsync';
export interface PreprocessorGroup {
markup?: (options: {
content: string,
filename: string
}) => { code: string, map?: SourceMap | string, dependencies?: string[] };
style?: Preprocessor;
script?: Preprocessor;
}
export type Preprocessor = (options: {
content: string,
attributes: Record<string, string | boolean>,
filename?: string
}) => { code: string, map?: SourceMap | string, dependencies?: string[] };
interface Processed {
code: string;
map?: SourceMap | string;
dependencies?: string[];
}
function parseAttributeValue(value: string) {
return /^['"]/.test(value) ?
value.slice(1, -1) :
value;
}
function parseAttributes(str: string) {
const attrs = {};
str.split(/\s+/).filter(Boolean).forEach(attr => {
const [name, value] = attr.split('=');
attrs[name] = value ? parseAttributeValue(value) : true;
});
return attrs;
}
export default async function preprocess(
source: string,
preprocessor: PreprocessorGroup | PreprocessorGroup[],
options?: { filename?: string }
) {
const filename = (options && options.filename) || preprocessor.filename; // legacy
const dependencies = [];
const preprocessors = Array.isArray(preprocessor) ? preprocessor : [preprocessor];
const markup = preprocessors.map(p => p.markup).filter(Boolean);
const script = preprocessors.map(p => p.script).filter(Boolean);
const style = preprocessors.map(p => p.style).filter(Boolean);
for (const fn of markup) {
const processed: Processed = await fn({
content: source,
filename
});
if (processed && processed.dependencies) dependencies.push(...processed.dependencies);
source = processed ? processed.code : source;
}
for (const fn of script) {
source = await replaceAsync(
source,
/<script([^]*?)>([^]*?)<\/script>/gi,
async (match, attributes, content) => {
const processed: Processed = await fn({
content,
attributes: parseAttributes(attributes),
filename
});
if (processed && processed.dependencies) dependencies.push(...processed.dependencies);
return processed ? `<script${attributes}>${processed.code}</script>` : match;
}
);
}
for (const fn of style) {
source = await replaceAsync(
source,
/<style([^]*?)>([^]*?)<\/style>/gi,
async (match, attributes, content) => {
const processed: Processed = await fn({
content,
attributes: parseAttributes(attributes),
filename
});
if (processed && processed.dependencies) dependencies.push(...processed.dependencies);
return processed ? `<style${attributes}>${processed.code}</style>` : match;
}
);
}
return {
// TODO return separated output, in future version where svelte.compile supports it:
// style: { code: styleCode, map: styleMap },
// script { code: scriptCode, map: scriptMap },
// markup { code: markupCode, map: markupMap },
code: source,
dependencies: [...new Set(dependencies)],
toString() {
return source;
}
};
}