Skip to content

Commit 07406b3

Browse files
Merge pull request #1023 from ijlee2/bugfix-1005
Optimized computations
2 parents f3c6105 + e5d7122 commit 07406b3

File tree

7 files changed

+258
-198
lines changed

7 files changed

+258
-198
lines changed

bin/build-augmentations.mjs

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,45 +13,62 @@ const ast = parse(source, {
1313
plugins: [['typescript', { dts: true }]],
1414
});
1515

16-
const elements = new Set(['SVGElement']);
16+
let htmlElements = new Set(['HTMLElement']);
17+
let svgElements = new Set(['SVGElement']);
18+
1719
traverse(ast, {
18-
TSInterfaceDeclaration: function (path) {
19-
if (path.node.id.name === 'HTMLElementTagNameMap') {
20-
const items = path.node.body.body;
21-
for (const item of items) {
22-
elements.add(item.typeAnnotation.typeAnnotation.typeName.name);
20+
TSInterfaceDeclaration(path) {
21+
switch (path.node.id.name) {
22+
case 'HTMLElementTagNameMap': {
23+
path.node.body.body.forEach(({ typeAnnotation }) => {
24+
htmlElements.add(typeAnnotation.typeAnnotation.typeName.name);
25+
});
26+
27+
break;
2328
}
24-
}
25-
if (path.node.id.name === 'SVGElementTagNameMap') {
26-
const items = path.node.body.body;
27-
for (const item of items) {
28-
elements.add(item.typeAnnotation.typeAnnotation.typeName.name);
29+
30+
case 'SVGElementTagNameMap': {
31+
path.node.body.body.forEach(({ typeAnnotation }) => {
32+
svgElements.add(typeAnnotation.typeAnnotation.typeName.name);
33+
});
34+
35+
break;
2936
}
3037
}
3138
},
3239
});
3340

34-
let registry = [];
35-
for (const element of elements) {
36-
registry.push(` '${element}': ${element};`);
37-
}
38-
39-
const prefix = `// generated by <Glint root>/bin/build-augmentations.mjs
40-
//
41-
// This serves to provide a UNIQUE mapping for HtmlElementType -> Html Element Name -> Html Element Attributes
42-
// (in combination with elements.d.ts)
43-
//
44-
// The TypeScript lib.dom.d.ts already has HTMLElementTagNameMap,
45-
// but it does not provide unique types for each element, and the technique
46-
// we use for looking up the type-string for each element does not work with the built in types.
47-
`;
41+
htmlElements = new Set([...htmlElements].sort());
42+
svgElements = new Set([...svgElements].sort());
43+
4844
const filePath = resolve(
4945
fileURLToPath(import.meta.url),
5046
'../../packages/template/-private/dsl/lib.dom.augmentation.d.ts',
5147
);
52-
let content = prefix;
53-
content += '\n';
54-
content += `export interface GlintElementRegistry {\n`;
55-
content += registry.join('\n') + '\n';
56-
content += `}\n`;
57-
writeFileSync(filePath, content);
48+
49+
writeFileSync(
50+
filePath,
51+
[
52+
`// Auto-generated by bin/build-augmentations.mjs`,
53+
`//`,
54+
`// This serves to provide a UNIQUE mapping for element type -> element name -> element attributes`,
55+
`// (in combination with elements.d.ts)`,
56+
`//`,
57+
`// The TypeScript lib.dom.d.ts already has HTMLElementTagNameMap,`,
58+
`// but it does not provide unique types for each element, and the technique`,
59+
`// we use for looking up the type-string for each element does not work with the built in types.`,
60+
``,
61+
`export type HTMLElementMap = {`,
62+
...Array.from(htmlElements).map((elementType) => {
63+
return ` '${elementType}': ${elementType};`;
64+
}),
65+
`};`,
66+
``,
67+
`export type SVGElementMap = {`,
68+
...Array.from(svgElements).map((elementType) => {
69+
return ` '${elementType}': ${elementType};`;
70+
}),
71+
`};`,
72+
``,
73+
].join('\n'),
74+
);

bin/build-elements.mjs

Lines changed: 124 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -49,23 +49,30 @@ const svgElementsMap = new Map([[GLOBAL_SVG_ATTRIBUTES_NAME, 'SVGElement']]);
4949
const mathmlElementsMap = new Map();
5050

5151
traverse(ast, {
52-
TSInterfaceDeclaration: function (path) {
53-
if (path.node.id.name === 'HTMLElementTagNameMap') {
54-
const items = path.node.body.body;
55-
for (const item of items) {
56-
htmlElementsMap.set(item.key.value, item.typeAnnotation.typeAnnotation.typeName.name);
52+
TSInterfaceDeclaration(path) {
53+
switch (path.node.id.name) {
54+
case 'HTMLElementTagNameMap': {
55+
path.node.body.body.forEach(({ key, typeAnnotation }) => {
56+
htmlElementsMap.set(key.value, typeAnnotation.typeAnnotation.typeName.name);
57+
});
58+
59+
break;
5760
}
58-
}
59-
if (path.node.id.name === 'SVGElementTagNameMap') {
60-
const items = path.node.body.body;
61-
for (const item of items) {
62-
svgElementsMap.set(item.key.value, item.typeAnnotation.typeAnnotation.typeName.name);
61+
62+
case 'MathMLElementTagNameMap': {
63+
path.node.body.body.forEach(({ key, typeAnnotation }) => {
64+
mathmlElementsMap.set(key.value, typeAnnotation.typeAnnotation.typeName.name);
65+
});
66+
67+
break;
6368
}
64-
}
65-
if (path.node.id.name === 'MathMLElementTagNameMap') {
66-
const items = path.node.body.body;
67-
for (const item of items) {
68-
mathmlElementsMap.set(item.key.value, item.typeAnnotation.typeAnnotation.typeName.name);
69+
70+
case 'SVGElementTagNameMap': {
71+
path.node.body.body.forEach(({ key, typeAnnotation }) => {
72+
svgElementsMap.set(key.value, typeAnnotation.typeAnnotation.typeName.name);
73+
});
74+
75+
break;
6976
}
7077
}
7178
},
@@ -81,22 +88,17 @@ function createAriaAttributesInterface() {
8188
}
8289

8390
function createHtmlElementsAttributesMap() {
84-
let htmlElementsContent = `
85-
import { AttrValue } from '../index';
91+
let htmlElementsContent = [
92+
`import { AttrValue } from '../index';`,
93+
``,
94+
`declare global {`,
95+
``,
96+
].join('\n');
8697

87-
declare global {
88-
`;
8998
const processed = new Set();
9099

91100
htmlElementsContent += createAriaAttributesInterface();
92101

93-
let mergedHtmlElements = `
94-
/**
95-
* @internal
96-
* @private - not for use outside of Glint
97-
*/
98-
interface GlintHtmlElementAttributesMap {\n`;
99-
100102
function emitAttributeInterface(type, keys, name) {
101103
if (!type || processed.has(type)) return;
102104
processed.add(type);
@@ -125,49 +127,69 @@ interface GlintHtmlElementAttributesMap {\n`;
125127
htmlElementsContent += '}\n';
126128
}
127129

128-
function addMapEntry(type) {
129-
const interfaceName =
130-
type === 'HTMLElement' ? GLOBAL_HTML_ATTRIBUTES_NAME : type + 'Attributes';
131-
mergedHtmlElements += ` ['${type}']: ${interfaceName};\n`;
132-
}
130+
let elementToAttributes = new Map();
133131

134-
Object.entries(htmlElementAttributes).forEach(([name, keys]) => {
135-
if (name === '*') {
136-
name = GLOBAL_HTML_ATTRIBUTES_NAME;
137-
htmlElementsMap.set(name, 'GlobalHTML');
132+
Object.entries(htmlElementAttributes).forEach(([elementName, keys]) => {
133+
if (elementName === '*') {
134+
elementName = GLOBAL_HTML_ATTRIBUTES_NAME;
135+
htmlElementsMap.set(elementName, 'GlobalHTML');
138136
}
139-
if (SKIP.has(name)) return;
140-
const type = htmlElementsMap.get(name);
141137

142-
if (!type) return;
138+
if (SKIP.has(elementName)) {
139+
return;
140+
}
143141

144-
emitAttributeInterface(type, keys, name);
142+
const elementType = htmlElementsMap.get(elementName);
143+
144+
if (!elementType) {
145+
return;
146+
}
147+
148+
emitAttributeInterface(elementType, keys, elementName);
145149

146150
// Not an element, but we use this prefix for attributes
147-
if (type === 'GlobalHTML') return;
151+
if (elementType === 'GlobalHTML') {
152+
return;
153+
}
148154

149-
addMapEntry(type);
155+
elementToAttributes.set(elementType, `${elementType}Attributes`);
150156
});
151-
emitAttributeInterface('HTMLElement', [], 'HTMLElement');
152-
addMapEntry('HTMLElement');
153157

154-
mergedHtmlElements += `}\n`;
158+
emitAttributeInterface('HTMLElement', [], 'HTMLElement');
159+
elementToAttributes.set('HTMLElement', GLOBAL_HTML_ATTRIBUTES_NAME);
160+
161+
// Manually add entries
162+
elementToAttributes.set('HTMLBodyElement', GLOBAL_HTML_ATTRIBUTES_NAME);
163+
elementToAttributes.set('HTMLDataListElement', GLOBAL_HTML_ATTRIBUTES_NAME);
164+
elementToAttributes.set('HTMLHtmlElement', GLOBAL_HTML_ATTRIBUTES_NAME);
165+
elementToAttributes.set('HTMLPictureElement', GLOBAL_HTML_ATTRIBUTES_NAME);
166+
elementToAttributes.set('HTMLSpanElement', GLOBAL_HTML_ATTRIBUTES_NAME);
167+
elementToAttributes.set('HTMLTitleElement', GLOBAL_HTML_ATTRIBUTES_NAME);
168+
169+
// Sort by element type
170+
elementToAttributes = new Map([...elementToAttributes].sort());
171+
172+
htmlElementsContent += [
173+
`/**`,
174+
` * @internal`,
175+
` * @private - not for use outside of Glint`,
176+
` */`,
177+
`interface GlintHtmlElementAttributesMap {`,
178+
...Array.from(elementToAttributes.entries()).map(([elementType, attributesType]) => {
179+
return ` ['${elementType}']: ${attributesType};`;
180+
}),
181+
`}`,
182+
``,
183+
].join('\n');
184+
185+
// Closing brace for `declare global`
186+
htmlElementsContent += '}\n';
155187

156-
htmlElementsContent += mergedHtmlElements + '}\n';
157188
return htmlElementsContent;
158189
}
159190

160191
function createSvgElementAttributesMap() {
161-
let svgElementsContent = `
162-
declare global {
163-
`;
164-
let mergedSvgElements = `
165-
166-
/**
167-
* @internal
168-
* @private - not for use outside of Glint
169-
*/
170-
interface GlintSvgElementAttributesMap {\n`;
192+
let svgElementsContent = [`declare global {`, ``].join('\n');
171193

172194
function emitAttributeInterface(type, keys, name) {
173195
const interfaceName = type + 'Attributes';
@@ -192,45 +214,70 @@ interface GlintSvgElementAttributesMap {\n`;
192214
svgElementsContent += ` ['${k}']: AttrValue;\n`;
193215
});
194216
}
217+
195218
svgElementsContent += `}\n`;
196219
}
197220

198-
function addMapEntry(type) {
199-
const interfaceName = type === 'SVGElement' ? GLOBAL_SVG_ATTRIBUTES_NAME : type + 'Attributes';
200-
201-
mergedSvgElements += ` ['${type}']: ${interfaceName};\n`;
202-
}
221+
let elementToAttributes = new Map();
203222

204-
Object.entries(svgElementAttributes).forEach(([name, keys]) => {
205-
if (name === '*') {
206-
name = GLOBAL_SVG_ATTRIBUTES_NAME;
207-
svgElementsMap.set(name, 'GlobalSVG');
223+
Object.entries(svgElementAttributes).forEach(([elementName, keys]) => {
224+
if (elementName === '*') {
225+
elementName = GLOBAL_SVG_ATTRIBUTES_NAME;
226+
svgElementsMap.set(elementName, 'GlobalSVG');
208227
}
209-
const type = svgElementsMap.get(name);
210228

211-
if (!type) return;
229+
const elementType = svgElementsMap.get(elementName);
212230

213-
emitAttributeInterface(type, keys, name);
231+
if (!elementType) {
232+
return;
233+
}
234+
235+
emitAttributeInterface(elementType, keys, elementName);
214236

215237
// Not an element, but we use this prefix for attributes
216-
if (type === 'GlobalSVG') return;
238+
if (elementType === 'GlobalSVG') {
239+
return;
240+
}
217241

218-
addMapEntry(type);
242+
elementToAttributes.set(elementType, `${elementType}Attributes`);
219243
});
244+
220245
emitAttributeInterface('SVGElement', [], 'SVGElement');
221-
addMapEntry('SVGElement');
246+
elementToAttributes.set('SVGElement', GLOBAL_SVG_ATTRIBUTES_NAME);
247+
248+
elementToAttributes = new Map([...elementToAttributes].sort());
249+
250+
svgElementsContent += [
251+
`/**`,
252+
` * @internal`,
253+
` * @private - not for use outside of Glint`,
254+
` */`,
255+
`interface GlintSvgElementAttributesMap {`,
256+
...Array.from(elementToAttributes.entries()).map(([elementType, attributesType]) => {
257+
return ` ['${elementType}']: ${attributesType};`;
258+
}),
259+
`}`,
260+
``,
261+
].join('\n');
262+
263+
// Closing brace for `declare global`
264+
svgElementsContent += '}\n';
222265

223-
mergedSvgElements += `}\n`;
224-
svgElementsContent += mergedSvgElements + '}\n';
225266
return svgElementsContent;
226267
}
227268

228-
const prefix = `//generated by scrips/build-elements.mjs
229-
// this server to provide the html attributes for each element
230-
`;
231269
const filePath = resolve(
232270
fileURLToPath(import.meta.url),
233271
'../../packages/template/-private/dsl/elements.d.ts',
234272
);
235-
const content = prefix + createHtmlElementsAttributesMap() + createSvgElementAttributesMap();
236-
writeFileSync(filePath, content);
273+
274+
writeFileSync(
275+
filePath,
276+
[
277+
'// Auto-generated by bin/build-elements.mjs',
278+
'// this server to provide the html attributes for each element',
279+
'',
280+
createHtmlElementsAttributesMap(),
281+
createSvgElementAttributesMap(),
282+
].join('\n'),
283+
);

0 commit comments

Comments
 (0)