@esportsplus/template 0.32.0 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/attributes.js +1 -2
- package/build/constants.d.ts +18 -3
- package/build/constants.js +31 -4
- package/build/html.d.ts +3 -3
- package/build/slot/array.d.ts +2 -2
- package/build/slot/array.js +5 -4
- package/build/slot/render.js +3 -2
- package/build/transformer/codegen.d.ts +2 -8
- package/build/transformer/codegen.js +87 -138
- package/build/transformer/index.d.ts +2 -3
- package/build/transformer/index.js +32 -31
- package/build/transformer/parser.d.ts +3 -2
- package/build/transformer/parser.js +4 -4
- package/build/transformer/plugins/tsc.js +8 -2
- package/build/transformer/plugins/vite.js +7 -9
- package/build/transformer/ts-parser.d.ts +1 -2
- package/build/transformer/ts-parser.js +25 -34
- package/build/transformer/type-analyzer.d.ts +4 -5
- package/build/transformer/type-analyzer.js +61 -71
- package/build/types.d.ts +1 -1
- package/package.json +7 -7
- package/src/attributes.ts +1 -4
- package/src/constants.ts +42 -6
- package/src/html.ts +3 -3
- package/src/slot/array.ts +9 -6
- package/src/slot/render.ts +5 -2
- package/src/transformer/codegen.ts +113 -175
- package/src/transformer/index.ts +43 -40
- package/src/transformer/parser.ts +10 -7
- package/src/transformer/plugins/tsc.ts +10 -2
- package/src/transformer/plugins/vite.ts +10 -14
- package/src/transformer/ts-parser.ts +31 -44
- package/src/transformer/type-analyzer.ts +75 -93
- package/src/types.ts +1 -1
- package/test/vite.config.ts +1 -1
- package/build/event/constants.d.ts +0 -3
- package/build/event/constants.js +0 -13
- package/src/event/constants.ts +0 -16
- package/storage/rewrite-analysis-2026-01-04.md +0 -439
package/build/attributes.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { effect } from '@esportsplus/reactivity';
|
|
2
2
|
import { isArray, isObject } from '@esportsplus/utilities';
|
|
3
|
-
import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, STATE_HYDRATING, STATE_NONE, STATE_WAITING } from './constants.js';
|
|
3
|
+
import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE } from './constants.js';
|
|
4
4
|
import { raf } from './utilities.js';
|
|
5
5
|
import q from '@esportsplus/queue';
|
|
6
6
|
import event from './event/index.js';
|
|
7
|
-
const STORE = Symbol();
|
|
8
7
|
let delimiters = {
|
|
9
8
|
class: ' ',
|
|
10
9
|
style: ';'
|
package/build/constants.d.ts
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
declare const ARRAY_SLOT: unique symbol;
|
|
2
2
|
declare const CLEANUP: unique symbol;
|
|
3
|
-
declare const
|
|
3
|
+
declare const COMPILER_ENTRYPOINT = "html";
|
|
4
|
+
declare const COMPILER_ENTRYPOINT_REACTIVITY = "reactive";
|
|
5
|
+
declare const COMPILER_NAMESPACE: string;
|
|
6
|
+
declare const enum COMPILER_TYPES {
|
|
7
|
+
ArraySlot = 0,
|
|
8
|
+
AttributeSlot = 1,
|
|
9
|
+
AttributeSpreadSlot = 2,
|
|
10
|
+
DocumentFragment = 3,
|
|
11
|
+
Effect = 4,
|
|
12
|
+
NodeSlot = 5,
|
|
13
|
+
Primitive = 6,
|
|
14
|
+
Static = 7,
|
|
15
|
+
Unknown = 8
|
|
16
|
+
}
|
|
17
|
+
declare const DIRECT_ATTACH_EVENTS: Set<string>;
|
|
18
|
+
declare const LIFECYCLE_EVENTS: Set<string>;
|
|
19
|
+
declare const PACKAGE = "@esportsplus/template";
|
|
4
20
|
declare const SLOT_HTML = "<!--$-->";
|
|
5
21
|
declare const STATE_HYDRATING = 0;
|
|
6
22
|
declare const STATE_NONE = 1;
|
|
7
23
|
declare const STATE_WAITING = 2;
|
|
8
24
|
declare const STORE: unique symbol;
|
|
9
|
-
export { ARRAY_SLOT, CLEANUP,
|
|
10
|
-
export * from './event/constants.js';
|
|
25
|
+
export { ARRAY_SLOT, CLEANUP, COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, PACKAGE, SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE, };
|
package/build/constants.js
CHANGED
|
@@ -1,11 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { uid } from '@esportsplus/typescript/transformer';
|
|
2
2
|
const ARRAY_SLOT = Symbol('template.array.slot');
|
|
3
3
|
const CLEANUP = Symbol('template.cleanup');
|
|
4
|
-
const
|
|
4
|
+
const COMPILER_ENTRYPOINT = 'html';
|
|
5
|
+
const COMPILER_ENTRYPOINT_REACTIVITY = 'reactive';
|
|
6
|
+
const COMPILER_NAMESPACE = uid('template');
|
|
7
|
+
var COMPILER_TYPES;
|
|
8
|
+
(function (COMPILER_TYPES) {
|
|
9
|
+
COMPILER_TYPES[COMPILER_TYPES["ArraySlot"] = 0] = "ArraySlot";
|
|
10
|
+
COMPILER_TYPES[COMPILER_TYPES["AttributeSlot"] = 1] = "AttributeSlot";
|
|
11
|
+
COMPILER_TYPES[COMPILER_TYPES["AttributeSpreadSlot"] = 2] = "AttributeSpreadSlot";
|
|
12
|
+
COMPILER_TYPES[COMPILER_TYPES["DocumentFragment"] = 3] = "DocumentFragment";
|
|
13
|
+
COMPILER_TYPES[COMPILER_TYPES["Effect"] = 4] = "Effect";
|
|
14
|
+
COMPILER_TYPES[COMPILER_TYPES["NodeSlot"] = 5] = "NodeSlot";
|
|
15
|
+
COMPILER_TYPES[COMPILER_TYPES["Primitive"] = 6] = "Primitive";
|
|
16
|
+
COMPILER_TYPES[COMPILER_TYPES["Static"] = 7] = "Static";
|
|
17
|
+
COMPILER_TYPES[COMPILER_TYPES["Unknown"] = 8] = "Unknown";
|
|
18
|
+
})(COMPILER_TYPES || (COMPILER_TYPES = {}));
|
|
19
|
+
;
|
|
20
|
+
const DIRECT_ATTACH_EVENTS = new Set([
|
|
21
|
+
'onblur',
|
|
22
|
+
'onerror',
|
|
23
|
+
'onfocus', 'onfocusin', 'onfocusout',
|
|
24
|
+
'onload',
|
|
25
|
+
'onplay', 'onpause', 'onended', 'ontimeupdate',
|
|
26
|
+
'onreset',
|
|
27
|
+
'onscroll', 'onsubmit'
|
|
28
|
+
]);
|
|
29
|
+
const LIFECYCLE_EVENTS = new Set([
|
|
30
|
+
'onconnect', 'ondisconnect', 'onrender', 'onresize', 'ontick'
|
|
31
|
+
]);
|
|
32
|
+
const PACKAGE = '@esportsplus/template';
|
|
5
33
|
const SLOT_HTML = '<!--$-->';
|
|
6
34
|
const STATE_HYDRATING = 0;
|
|
7
35
|
const STATE_NONE = 1;
|
|
8
36
|
const STATE_WAITING = 2;
|
|
9
37
|
const STORE = Symbol('template.store');
|
|
10
|
-
export { ARRAY_SLOT, CLEANUP,
|
|
11
|
-
export * from './event/constants.js';
|
|
38
|
+
export { ARRAY_SLOT, CLEANUP, COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_NAMESPACE, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, PACKAGE, SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE, };
|
package/build/html.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Reactive } from '@esportsplus/reactivity';
|
|
2
2
|
import { Attribute, Attributes, Renderable } from './types.js';
|
|
3
3
|
import { ArraySlot } from './slot/array.js';
|
|
4
|
-
type Values<T> = Attribute | Attributes<any> |
|
|
4
|
+
type Values<T> = ArraySlot<T extends unknown[] ? T : never> | Attribute | Attributes<any> | Renderable<T>;
|
|
5
5
|
declare const html: {
|
|
6
6
|
<T>(_literals: TemplateStringsArray, ..._values: (Values<T> | Values<T>[])[]): DocumentFragment;
|
|
7
|
-
reactive<T>(_arr:
|
|
7
|
+
reactive<T>(_arr: Reactive<T[]>, _template: (value: T) => DocumentFragment): ArraySlot<T[]>;
|
|
8
8
|
};
|
|
9
9
|
export default html;
|
package/build/slot/array.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Reactive } from '@esportsplus/reactivity';
|
|
2
2
|
import html from '../html.js';
|
|
3
3
|
declare class ArraySlot<T> {
|
|
4
4
|
private array;
|
|
@@ -9,7 +9,7 @@ declare class ArraySlot<T> {
|
|
|
9
9
|
private signal;
|
|
10
10
|
private template;
|
|
11
11
|
readonly fragment: DocumentFragment;
|
|
12
|
-
constructor(array:
|
|
12
|
+
constructor(array: Reactive<T[]>, template: ((value: Reactive<T[]>[number]) => ReturnType<typeof html>));
|
|
13
13
|
private anchor;
|
|
14
14
|
private clear;
|
|
15
15
|
private pop;
|
package/build/slot/array.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { read, root,
|
|
2
|
-
import { ARRAY_SLOT
|
|
3
|
-
import { clone, marker, raf } from '../utilities.js';
|
|
1
|
+
import { read, root, signal, write } from '@esportsplus/reactivity';
|
|
2
|
+
import { ARRAY_SLOT } from '../constants.js';
|
|
3
|
+
import { clone, fragment, marker, raf } from '../utilities.js';
|
|
4
4
|
import { ondisconnect, remove } from './cleanup.js';
|
|
5
|
+
const EMPTY_FRAGMENT = fragment('');
|
|
5
6
|
class ArraySlot {
|
|
6
7
|
array;
|
|
7
8
|
marker;
|
|
@@ -129,7 +130,7 @@ class ArraySlot {
|
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
});
|
|
132
|
-
|
|
133
|
+
write(this.signal, this.nodes.length);
|
|
133
134
|
});
|
|
134
135
|
}
|
|
135
136
|
shift() {
|
package/build/slot/render.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isArray } from '@esportsplus/utilities';
|
|
2
|
-
import { ARRAY_SLOT
|
|
3
|
-
import { clone, text } from '../utilities.js';
|
|
2
|
+
import { ARRAY_SLOT } from '../constants.js';
|
|
3
|
+
import { clone, fragment, text } from '../utilities.js';
|
|
4
|
+
const EMPTY_FRAGMENT = fragment('');
|
|
4
5
|
export default function render(anchor, value) {
|
|
5
6
|
if (value == null || value === false || value === '') {
|
|
6
7
|
return EMPTY_FRAGMENT;
|
|
@@ -5,14 +5,8 @@ type CodegenResult = {
|
|
|
5
5
|
code: string;
|
|
6
6
|
};
|
|
7
7
|
declare const addArraySlotImport: (code: string) => string;
|
|
8
|
-
declare const generateCode: (templates: TemplateInfo[], originalCode: string, sourceFile: ts.SourceFile) => CodegenResult;
|
|
8
|
+
declare const generateCode: (templates: TemplateInfo[], originalCode: string, sourceFile: ts.SourceFile, checker?: ts.TypeChecker) => CodegenResult;
|
|
9
9
|
declare const generateReactiveInlining: (calls: ReactiveCallInfo[], code: string, sourceFile: ts.SourceFile) => string;
|
|
10
|
-
declare const getNames: () => {
|
|
11
|
-
attr: string;
|
|
12
|
-
event: string;
|
|
13
|
-
slot: string;
|
|
14
|
-
};
|
|
15
10
|
declare const needsArraySlotImport: (sourceFile: ts.SourceFile) => boolean;
|
|
16
|
-
|
|
17
|
-
export { addArraySlotImport, generateCode, generateReactiveInlining, getNames, needsArraySlotImport, setTypeChecker };
|
|
11
|
+
export { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport };
|
|
18
12
|
export type { CodegenResult };
|
|
@@ -1,42 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { code as c, uid } from '@esportsplus/typescript/transformer';
|
|
2
2
|
import { analyzeExpression, generateAttributeBinding, generateSpreadBindings } from './type-analyzer.js';
|
|
3
3
|
import { ts } from '@esportsplus/typescript';
|
|
4
|
+
import { COMPILER_ENTRYPOINT, COMPILER_NAMESPACE, COMPILER_TYPES, PACKAGE } from '../constants.js';
|
|
4
5
|
import parser from './parser.js';
|
|
5
6
|
const ARROW_EMPTY_PARAMS = /\(\s*\)\s*=>\s*$/;
|
|
6
|
-
let
|
|
7
|
-
function collectNestedTemplateReplacements(node, exprStart,
|
|
7
|
+
let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
8
|
+
function collectNestedTemplateReplacements(ctx, node, exprStart, replacements) {
|
|
8
9
|
if (isNestedHtmlTemplate(node)) {
|
|
9
10
|
replacements.push({
|
|
10
11
|
end: node.end - exprStart,
|
|
11
|
-
newText: generateNestedTemplateCode(
|
|
12
|
+
newText: generateNestedTemplateCode(ctx, node),
|
|
12
13
|
start: node.getStart() - exprStart
|
|
13
14
|
});
|
|
14
15
|
}
|
|
15
16
|
else {
|
|
16
|
-
ts.forEachChild(node, child => collectNestedTemplateReplacements(child, exprStart,
|
|
17
|
+
ts.forEachChild(node, child => collectNestedTemplateReplacements(ctx, child, exprStart, replacements));
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
|
-
function
|
|
20
|
-
let specifiers = [];
|
|
21
|
-
if (needsArraySlot) {
|
|
22
|
-
specifiers.push(`ArraySlot as ${nameArraySlot}`);
|
|
23
|
-
}
|
|
24
|
-
if (needsEffectSlot) {
|
|
25
|
-
specifiers.push(`EffectSlot as ${nameEffectSlot}`);
|
|
26
|
-
}
|
|
27
|
-
if (needsAttr) {
|
|
28
|
-
specifiers.push(`attributes as ${nameAttr}`);
|
|
29
|
-
}
|
|
30
|
-
if (needsEvent) {
|
|
31
|
-
specifiers.push(`event as ${nameEvent}`);
|
|
32
|
-
}
|
|
33
|
-
if (needsSlot) {
|
|
34
|
-
specifiers.push(`slot as ${nameSlot}`);
|
|
35
|
-
}
|
|
36
|
-
specifiers.push(`template as ${nameTemplate}`);
|
|
37
|
-
return `import { ${specifiers.join(', ')} } from '@esportsplus/template';`;
|
|
38
|
-
}
|
|
39
|
-
function generateNestedTemplateCode(node, sourceFile) {
|
|
20
|
+
function generateNestedTemplateCode(ctx, node) {
|
|
40
21
|
let expressions = [], exprTexts = [], literals = [], template = node.template;
|
|
41
22
|
if (ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
42
23
|
literals.push(template.text);
|
|
@@ -47,42 +28,38 @@ function generateNestedTemplateCode(node, sourceFile) {
|
|
|
47
28
|
let expr = template.templateSpans[i].expression;
|
|
48
29
|
expressions.push(expr);
|
|
49
30
|
literals.push(template.templateSpans[i].literal.text);
|
|
50
|
-
exprTexts.push(rewriteExpression(
|
|
31
|
+
exprTexts.push(rewriteExpression(ctx, expr));
|
|
51
32
|
}
|
|
52
33
|
}
|
|
53
|
-
return generateTemplateCode(parser.parse(literals), exprTexts, expressions,
|
|
34
|
+
return generateTemplateCode(ctx, parser.parse(literals), exprTexts, expressions, false);
|
|
54
35
|
}
|
|
55
|
-
function generateNodeBinding(anchor, exprText, exprNode
|
|
36
|
+
function generateNodeBinding(ctx, anchor, exprText, exprNode) {
|
|
56
37
|
if (!exprNode) {
|
|
57
|
-
|
|
58
|
-
return `${nameSlot}(${anchor}, ${exprText});`;
|
|
38
|
+
return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
59
39
|
}
|
|
60
40
|
if (isNestedHtmlTemplate(exprNode)) {
|
|
61
|
-
return `${anchor}.parentNode.insertBefore(${generateNestedTemplateCode(
|
|
41
|
+
return `${anchor}.parentNode.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
|
|
62
42
|
}
|
|
63
|
-
let slotType = analyzeExpression(exprNode,
|
|
43
|
+
let slotType = analyzeExpression(exprNode, ctx.checker);
|
|
64
44
|
switch (slotType) {
|
|
65
|
-
case
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return `new ${nameArraySlot}(${anchor}, ${exprText});`;
|
|
71
|
-
case 'static':
|
|
45
|
+
case COMPILER_TYPES.Effect:
|
|
46
|
+
return `new ${COMPILER_NAMESPACE}.EffectSlot(${anchor}, ${exprText});`;
|
|
47
|
+
case COMPILER_TYPES.ArraySlot:
|
|
48
|
+
return `new ${COMPILER_NAMESPACE}.ArraySlot(${anchor}, ${exprText});`;
|
|
49
|
+
case COMPILER_TYPES.Static:
|
|
72
50
|
return `${anchor}.textContent = ${exprText};`;
|
|
73
|
-
case
|
|
51
|
+
case COMPILER_TYPES.DocumentFragment:
|
|
74
52
|
return `${anchor}.parentNode.insertBefore(${exprText}, ${anchor});`;
|
|
75
53
|
default:
|
|
76
|
-
|
|
77
|
-
return `${nameSlot}(${anchor}, ${exprText});`;
|
|
54
|
+
return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
78
55
|
}
|
|
79
56
|
}
|
|
80
|
-
function generateTemplateCode({ html, slots }, exprTexts, exprNodes,
|
|
57
|
+
function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArrowBody) {
|
|
81
58
|
if (!slots || slots.length === 0) {
|
|
82
|
-
return `${getOrCreateTemplateId(html)}()`;
|
|
59
|
+
return `${getOrCreateTemplateId(ctx, html)}()`;
|
|
83
60
|
}
|
|
84
61
|
let code = [], declarations = [], index = 0, nodes = new Map(), root = uid('root');
|
|
85
|
-
declarations.push(`${root} = ${getOrCreateTemplateId(html)}()`);
|
|
62
|
+
declarations.push(`${root} = ${getOrCreateTemplateId(ctx, html)}()`);
|
|
86
63
|
nodes.set('', root);
|
|
87
64
|
for (let i = 0, n = slots.length; i < n; i++) {
|
|
88
65
|
let path = slots[i].path;
|
|
@@ -111,26 +88,24 @@ function generateTemplateCode({ html, slots }, exprTexts, exprNodes, sourceFile,
|
|
|
111
88
|
let elementVar = slots[i].path.length === 0
|
|
112
89
|
? root
|
|
113
90
|
: (nodes.get(slots[i].path.join('.')) || root), slot = slots[i];
|
|
114
|
-
if (slot.type ===
|
|
91
|
+
if (slot.type === COMPILER_TYPES.AttributeSlot) {
|
|
115
92
|
for (let j = 0, m = slot.attributes.names.length; j < m; j++) {
|
|
116
93
|
let name = slot.attributes.names[j];
|
|
117
94
|
if (name === 'spread') {
|
|
118
|
-
let bindings = generateSpreadBindings(exprNodes[index], exprTexts[index] || 'undefined', elementVar, sourceFile,
|
|
95
|
+
let bindings = generateSpreadBindings(exprNodes[index], exprTexts[index] || 'undefined', elementVar, ctx.sourceFile, ctx.checker, COMPILER_NAMESPACE);
|
|
119
96
|
for (let k = 0, o = bindings.length; k < o; k++) {
|
|
120
|
-
trackBindingUsage(bindings[k]);
|
|
121
97
|
code.push(bindings[k]);
|
|
122
98
|
}
|
|
123
99
|
index++;
|
|
124
100
|
}
|
|
125
101
|
else {
|
|
126
|
-
let binding = generateAttributeBinding(elementVar, name, exprTexts[index++] || 'undefined', slot.attributes.statics[name] || '');
|
|
127
|
-
trackBindingUsage(binding);
|
|
102
|
+
let binding = generateAttributeBinding(elementVar, name, exprTexts[index++] || 'undefined', slot.attributes.statics[name] || '', COMPILER_NAMESPACE);
|
|
128
103
|
code.push(binding);
|
|
129
104
|
}
|
|
130
105
|
}
|
|
131
106
|
}
|
|
132
107
|
else {
|
|
133
|
-
code.push(generateNodeBinding(elementVar, exprTexts[index] || 'undefined', exprNodes[index]
|
|
108
|
+
code.push(generateNodeBinding(ctx, elementVar, exprTexts[index] || 'undefined', exprNodes[index]));
|
|
134
109
|
index++;
|
|
135
110
|
}
|
|
136
111
|
}
|
|
@@ -138,12 +113,12 @@ function generateTemplateCode({ html, slots }, exprTexts, exprNodes, sourceFile,
|
|
|
138
113
|
code.push(isArrowBody ? `}` : `})()`);
|
|
139
114
|
return code.join('\n');
|
|
140
115
|
}
|
|
141
|
-
function getOrCreateTemplateId(html) {
|
|
142
|
-
let id = htmlToTemplateId.get(html);
|
|
116
|
+
function getOrCreateTemplateId(ctx, html) {
|
|
117
|
+
let id = ctx.htmlToTemplateId.get(html);
|
|
143
118
|
if (!id) {
|
|
144
119
|
id = uid('tmpl');
|
|
145
|
-
hoistedFactories.set(id, html);
|
|
146
|
-
htmlToTemplateId.set(html, id);
|
|
120
|
+
ctx.hoistedFactories.set(id, html);
|
|
121
|
+
ctx.htmlToTemplateId.set(html, id);
|
|
147
122
|
}
|
|
148
123
|
return id;
|
|
149
124
|
}
|
|
@@ -165,98 +140,78 @@ function hasArraySlotImport(sourceFile) {
|
|
|
165
140
|
}
|
|
166
141
|
return false;
|
|
167
142
|
}
|
|
168
|
-
function
|
|
169
|
-
if (
|
|
170
|
-
ts.isIdentifier(node.expression) &&
|
|
171
|
-
node.expression.text === 'ArraySlot') {
|
|
143
|
+
function hasMatch(node, predicate) {
|
|
144
|
+
if (predicate(node)) {
|
|
172
145
|
return true;
|
|
173
146
|
}
|
|
174
147
|
let found = false;
|
|
175
148
|
ts.forEachChild(node, child => {
|
|
176
|
-
if (!found
|
|
177
|
-
found =
|
|
149
|
+
if (!found) {
|
|
150
|
+
found = hasMatch(child, predicate);
|
|
178
151
|
}
|
|
179
152
|
});
|
|
180
153
|
return found;
|
|
181
154
|
}
|
|
155
|
+
function hasArraySlotUsage(node) {
|
|
156
|
+
return hasMatch(node, n => ts.isNewExpression(n) &&
|
|
157
|
+
ts.isPropertyAccessExpression(n.expression) &&
|
|
158
|
+
n.expression.name.text === 'ArraySlot');
|
|
159
|
+
}
|
|
182
160
|
function hasNestedTemplates(node) {
|
|
183
|
-
|
|
184
|
-
return true;
|
|
185
|
-
}
|
|
186
|
-
let found = false;
|
|
187
|
-
ts.forEachChild(node, child => {
|
|
188
|
-
if (!found && hasNestedTemplates(child)) {
|
|
189
|
-
found = true;
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
return found;
|
|
161
|
+
return hasMatch(node, n => isNestedHtmlTemplate(n));
|
|
193
162
|
}
|
|
194
163
|
function isNestedHtmlTemplate(expr) {
|
|
195
|
-
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text ===
|
|
164
|
+
return ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT;
|
|
196
165
|
}
|
|
197
|
-
function isNestedTemplate(template,
|
|
198
|
-
for (let i = 0, n =
|
|
199
|
-
let
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
for (let j = 0, m = other.expressions.length; j < m; j++) {
|
|
204
|
-
let expr = other.expressions[j];
|
|
205
|
-
if (template.start >= expr.getStart() && template.end <= expr.end) {
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
166
|
+
function isNestedTemplate(template, exprRanges) {
|
|
167
|
+
for (let i = 0, n = exprRanges.length; i < n; i++) {
|
|
168
|
+
let range = exprRanges[i];
|
|
169
|
+
if (template.start >= range.start && template.end <= range.end) {
|
|
170
|
+
return true;
|
|
208
171
|
}
|
|
209
172
|
}
|
|
210
173
|
return false;
|
|
211
174
|
}
|
|
212
|
-
function rewriteExpression(
|
|
175
|
+
function rewriteExpression(ctx, expr) {
|
|
213
176
|
if (isNestedHtmlTemplate(expr)) {
|
|
214
|
-
return generateNestedTemplateCode(
|
|
177
|
+
return generateNestedTemplateCode(ctx, expr);
|
|
215
178
|
}
|
|
216
179
|
if (!hasNestedTemplates(expr)) {
|
|
217
|
-
return
|
|
180
|
+
return ctx.printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
|
|
218
181
|
}
|
|
219
182
|
let exprStart = expr.getStart(), replacements = [];
|
|
220
|
-
collectNestedTemplateReplacements(expr, exprStart,
|
|
221
|
-
return
|
|
222
|
-
}
|
|
223
|
-
function trackBindingUsage(binding) {
|
|
224
|
-
if (binding.startsWith(nameEvent + '.')) {
|
|
225
|
-
needsEvent = true;
|
|
226
|
-
}
|
|
227
|
-
else if (binding.startsWith(nameAttr + '.')) {
|
|
228
|
-
needsAttr = true;
|
|
229
|
-
}
|
|
183
|
+
collectNestedTemplateReplacements(ctx, expr, exprStart, replacements);
|
|
184
|
+
return c.replaceReverse(expr.getText(ctx.sourceFile), replacements);
|
|
230
185
|
}
|
|
231
186
|
const addArraySlotImport = (code) => {
|
|
232
|
-
return
|
|
187
|
+
return `import * as ${COMPILER_NAMESPACE} from '${PACKAGE}';\n` + code;
|
|
233
188
|
};
|
|
234
|
-
const generateCode = (templates, originalCode, sourceFile) => {
|
|
189
|
+
const generateCode = (templates, originalCode, sourceFile, checker) => {
|
|
235
190
|
if (templates.length === 0) {
|
|
236
191
|
return { changed: false, code: originalCode };
|
|
237
192
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
needsArraySlot = false;
|
|
247
|
-
needsAttr = false;
|
|
248
|
-
needsEffectSlot = false;
|
|
249
|
-
needsEvent = false;
|
|
250
|
-
needsSlot = false;
|
|
251
|
-
let rootTemplates = templates.filter(t => !isNestedTemplate(t, templates));
|
|
193
|
+
let exprRanges = [];
|
|
194
|
+
for (let i = 0, n = templates.length; i < n; i++) {
|
|
195
|
+
let exprs = templates[i].expressions;
|
|
196
|
+
for (let j = 0, m = exprs.length; j < m; j++) {
|
|
197
|
+
exprRanges.push({ end: exprs[j].end, start: exprs[j].getStart() });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
let rootTemplates = templates.filter(t => !isNestedTemplate(t, exprRanges));
|
|
252
201
|
if (rootTemplates.length === 0) {
|
|
253
202
|
return { changed: false, code: originalCode };
|
|
254
203
|
}
|
|
255
|
-
let
|
|
204
|
+
let ctx = {
|
|
205
|
+
checker,
|
|
206
|
+
hoistedFactories: new Map(),
|
|
207
|
+
htmlToTemplateId: new Map(),
|
|
208
|
+
printer,
|
|
209
|
+
sourceFile
|
|
210
|
+
}, replacements = [];
|
|
256
211
|
for (let i = 0, n = rootTemplates.length; i < n; i++) {
|
|
257
212
|
let exprTexts = [], template = rootTemplates[i];
|
|
258
213
|
for (let j = 0, m = template.expressions.length; j < m; j++) {
|
|
259
|
-
exprTexts.push(rewriteExpression(template.expressions[j]
|
|
214
|
+
exprTexts.push(rewriteExpression(ctx, template.expressions[j]));
|
|
260
215
|
}
|
|
261
216
|
let codeBefore = originalCode.slice(0, template.start), isArrowBody = codeBefore.trimEnd().endsWith('=>'), parsed = parser.parse(template.literals);
|
|
262
217
|
if (isArrowBody && (!parsed.slots || parsed.slots.length === 0)) {
|
|
@@ -264,7 +219,7 @@ const generateCode = (templates, originalCode, sourceFile) => {
|
|
|
264
219
|
if (arrowMatch) {
|
|
265
220
|
replacements.push({
|
|
266
221
|
end: template.end,
|
|
267
|
-
newText: getOrCreateTemplateId(parsed.html),
|
|
222
|
+
newText: getOrCreateTemplateId(ctx, parsed.html),
|
|
268
223
|
start: template.start - arrowMatch[0].length
|
|
269
224
|
});
|
|
270
225
|
continue;
|
|
@@ -272,17 +227,17 @@ const generateCode = (templates, originalCode, sourceFile) => {
|
|
|
272
227
|
}
|
|
273
228
|
replacements.push({
|
|
274
229
|
end: template.end,
|
|
275
|
-
newText: generateTemplateCode(parsed, exprTexts, template.expressions,
|
|
230
|
+
newText: generateTemplateCode(ctx, parsed, exprTexts, template.expressions, isArrowBody),
|
|
276
231
|
start: template.start
|
|
277
232
|
});
|
|
278
233
|
}
|
|
279
|
-
let changed = replacements.length > 0, code =
|
|
280
|
-
if (changed && hoistedFactories.size > 0) {
|
|
234
|
+
let changed = replacements.length > 0, code = c.replaceReverse(originalCode, replacements);
|
|
235
|
+
if (changed && ctx.hoistedFactories.size > 0) {
|
|
281
236
|
let factories = [];
|
|
282
|
-
for (let [id, html] of hoistedFactories) {
|
|
283
|
-
factories.push(`const ${id} = ${
|
|
237
|
+
for (let [id, html] of ctx.hoistedFactories) {
|
|
238
|
+
factories.push(`const ${id} = ${COMPILER_NAMESPACE}.template(\`${html}\`);`);
|
|
284
239
|
}
|
|
285
|
-
code =
|
|
240
|
+
code = `import * as ${COMPILER_NAMESPACE} from '${PACKAGE}';\n\n` + factories.join('\n') + '\n\n' + code;
|
|
286
241
|
}
|
|
287
242
|
return { changed, code };
|
|
288
243
|
};
|
|
@@ -290,27 +245,21 @@ const generateReactiveInlining = (calls, code, sourceFile) => {
|
|
|
290
245
|
if (calls.length === 0) {
|
|
291
246
|
return code;
|
|
292
247
|
}
|
|
293
|
-
let
|
|
294
|
-
for (let i = calls.length
|
|
248
|
+
let replacements = [];
|
|
249
|
+
for (let i = 0, n = calls.length; i < n; i++) {
|
|
295
250
|
let call = calls[i];
|
|
296
|
-
|
|
297
|
-
|
|
251
|
+
replacements.push({
|
|
252
|
+
end: call.end,
|
|
253
|
+
newText: `new ${COMPILER_NAMESPACE}.ArraySlot(
|
|
298
254
|
${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)},
|
|
299
255
|
${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)}
|
|
300
|
-
)
|
|
301
|
-
|
|
256
|
+
)`,
|
|
257
|
+
start: call.start
|
|
258
|
+
});
|
|
302
259
|
}
|
|
303
|
-
return
|
|
260
|
+
return c.replaceReverse(code, replacements);
|
|
304
261
|
};
|
|
305
|
-
const getNames = () => ({
|
|
306
|
-
attr: nameAttr,
|
|
307
|
-
event: nameEvent,
|
|
308
|
-
slot: nameSlot
|
|
309
|
-
});
|
|
310
262
|
const needsArraySlotImport = (sourceFile) => {
|
|
311
263
|
return hasArraySlotUsage(sourceFile) && !hasArraySlotImport(sourceFile);
|
|
312
264
|
};
|
|
313
|
-
|
|
314
|
-
currentChecker = checker;
|
|
315
|
-
};
|
|
316
|
-
export { addArraySlotImport, generateCode, generateReactiveInlining, getNames, needsArraySlotImport, setTypeChecker };
|
|
265
|
+
export { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { mightNeedTransform } from '@esportsplus/typescript/transformer';
|
|
2
1
|
import { ts } from '@esportsplus/typescript';
|
|
3
2
|
type TransformResult = {
|
|
4
3
|
changed: boolean;
|
|
@@ -8,5 +7,5 @@ type TransformResult = {
|
|
|
8
7
|
declare const PATTERNS: string[];
|
|
9
8
|
declare function createTransformer(program: ts.Program): ts.TransformerFactory<ts.SourceFile>;
|
|
10
9
|
declare const transform: (sourceFile: ts.SourceFile, program: ts.Program) => TransformResult;
|
|
11
|
-
|
|
12
|
-
export { createTransformer,
|
|
10
|
+
declare const transformCode: (code: string, sourceFile: ts.SourceFile, checker?: ts.TypeChecker) => TransformResult;
|
|
11
|
+
export { createTransformer, transform, transformCode, PATTERNS };
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport
|
|
1
|
+
import { code as c } from '@esportsplus/typescript/transformer';
|
|
2
|
+
import { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport } from './codegen.js';
|
|
3
|
+
import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY } from '../constants.js';
|
|
3
4
|
import { findHtmlTemplates, findReactiveCalls } from './ts-parser.js';
|
|
4
5
|
import { ts } from '@esportsplus/typescript';
|
|
5
|
-
const PATTERNS = [
|
|
6
|
+
const PATTERNS = [`${COMPILER_ENTRYPOINT}\``, `${COMPILER_ENTRYPOINT}.${COMPILER_ENTRYPOINT_REACTIVITY}`];
|
|
6
7
|
function createTransformer(program) {
|
|
7
8
|
let typeChecker = program.getTypeChecker();
|
|
8
9
|
return (_context) => {
|
|
9
10
|
return (sourceFile) => {
|
|
10
11
|
let code = sourceFile.getFullText();
|
|
11
|
-
if (!
|
|
12
|
+
if (!c.contains(code, { patterns: PATTERNS })) {
|
|
12
13
|
return sourceFile;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
let result = transformCode(code, sourceFile);
|
|
15
|
+
let result = transformCode(code, sourceFile, typeChecker);
|
|
16
16
|
if (!result.changed) {
|
|
17
17
|
return sourceFile;
|
|
18
18
|
}
|
|
@@ -20,43 +20,44 @@ function createTransformer(program) {
|
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
let
|
|
25
|
-
|
|
23
|
+
const transform = (sourceFile, program) => {
|
|
24
|
+
let code = sourceFile.getFullText();
|
|
25
|
+
if (!c.contains(code, { patterns: PATTERNS })) {
|
|
26
|
+
return { changed: false, code, sourceFile };
|
|
27
|
+
}
|
|
28
|
+
let checker, fileName = sourceFile.fileName, programSourceFile = program.getSourceFile(fileName)
|
|
29
|
+
|| program.getSourceFile(fileName.replace(/\\/g, '/'))
|
|
30
|
+
|| program.getSourceFile(fileName.replace(/\//g, '\\'));
|
|
31
|
+
if (programSourceFile) {
|
|
32
|
+
checker = program.getTypeChecker();
|
|
33
|
+
sourceFile = programSourceFile;
|
|
34
|
+
}
|
|
35
|
+
return transformCode(code, sourceFile, checker);
|
|
36
|
+
};
|
|
37
|
+
const transformCode = (code, sourceFile, checker) => {
|
|
38
|
+
let changed = false, codegenChanged = false, needsImport = false, result = code, reactiveCalls = findReactiveCalls(sourceFile);
|
|
26
39
|
if (reactiveCalls.length > 0) {
|
|
27
40
|
result = generateReactiveInlining(reactiveCalls, result, sourceFile);
|
|
28
41
|
changed = true;
|
|
29
42
|
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
33
|
-
}
|
|
34
|
-
setTypeChecker(undefined);
|
|
43
|
+
needsImport = needsArraySlotImport(sourceFile);
|
|
44
|
+
checker = undefined;
|
|
35
45
|
}
|
|
36
46
|
let templates = findHtmlTemplates(sourceFile);
|
|
37
47
|
if (templates.length > 0) {
|
|
38
|
-
let codegenResult = generateCode(templates, result, sourceFile);
|
|
48
|
+
let codegenResult = generateCode(templates, result, sourceFile, checker);
|
|
39
49
|
if (codegenResult.changed) {
|
|
40
50
|
changed = true;
|
|
51
|
+
codegenChanged = true;
|
|
41
52
|
result = codegenResult.code;
|
|
42
|
-
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
43
53
|
}
|
|
44
54
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const transform = (sourceFile, program) => {
|
|
48
|
-
let code = sourceFile.getFullText();
|
|
49
|
-
if (!mightNeedTransform(code, { patterns: PATTERNS })) {
|
|
50
|
-
return { changed: false, code, sourceFile };
|
|
55
|
+
if (needsImport && !codegenChanged) {
|
|
56
|
+
result = addArraySlotImport(result);
|
|
51
57
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
setTypeChecker(program.getTypeChecker());
|
|
55
|
-
sourceFile = programSourceFile;
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
setTypeChecker(undefined);
|
|
58
|
+
if (changed) {
|
|
59
|
+
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
59
60
|
}
|
|
60
|
-
return
|
|
61
|
+
return { changed, code: result, sourceFile };
|
|
61
62
|
};
|
|
62
|
-
export { createTransformer,
|
|
63
|
+
export { createTransformer, transform, transformCode, PATTERNS };
|