-
Notifications
You must be signed in to change notification settings - Fork 2
/
html5.ts
125 lines (110 loc) · 2.9 KB
/
html5.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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import {
Context,
Expression,
Invocation,
is_expression,
new_macro,
} from "./tsgen.ts";
import { body_, head, html, meta, link } from "./h.ts";
const statekey = Symbol();
interface Html5State {
html5_dependencies: Set<string>;
}
function html5_state(ctx: Context): Html5State {
const state = ctx.state.get(statekey);
if (state) {
return <Html5State> state;
} else {
ctx.state.set(statekey, {
html5_dependencies: new Set(),
});
return html5_state(ctx);
}
}
export function html5_dependency(dep: Expression): Expression {
const macro = new_macro(
undefined,
(fully_expanded, ctx) => {
html5_state(ctx).html5_dependencies.add(fully_expanded);
return "";
},
);
return new Invocation(macro, [dep]);
}
export function html5_dependency_css(path: Expression): Expression {
return html5_dependency([`<link rel="stylesheet" href="`, path, `">`]);
}
export function html5_dependency_js(path: Expression): Expression {
return html5_dependency([`<script type="module" src="`, path, `"></script>`]);
}
export function html5(
header: Expression,
body: Expression,
bodyClass: string,
): Expression {
let previous_scope: Set<string> | null = null;
const my_scope = new Set<string>();
const macro = new_macro(
(args, _) => {
let is_head_done = false;
let is_body_done = false;
return [
`<!doctype html>`,
html(
head(
meta({ charset: "utf-8" }),
notify(
(_) => is_head_done = true,
args[0],
),
new Invocation(
new_macro(
(_, ctx) => {
if (is_head_done && is_body_done) {
return Array.from(html5_state(ctx).html5_dependencies).join(
"",
);
} else {
return null;
}
},
),
[],
),
),
body_(
{ class: bodyClass },
notify(
(_) => is_body_done = true,
args[1],
),
),
),
];
},
undefined,
(ctx) => {
const state = html5_state(ctx);
previous_scope = state.html5_dependencies;
state.html5_dependencies = my_scope;
},
(ctx) => {
html5_state(ctx).html5_dependencies = <Set<string>> previous_scope;
},
);
return new Invocation(macro, [header, body]);
}
// Behaves just like the given macro, except it also calls the callback with the expanded content of the given macro when it has fully expanded.
export function notify(
cb: (expanded: string) => void,
wrapped: Expression,
): Expression {
const macro = new_macro(
(args, _) => args[0],
(fully_expanded, _) => {
cb(fully_expanded);
return fully_expanded;
},
);
return new Invocation(macro, [wrapped]);
}