@esportsplus/template 0.31.7 → 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 -9
- package/build/transformer/codegen.js +87 -101
- package/build/transformer/index.d.ts +2 -3
- package/build/transformer/index.js +34 -32
- 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 +9 -14
- 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 -124
- package/src/transformer/index.ts +44 -49
- package/src/transformer/parser.ts +10 -7
- package/src/transformer/plugins/tsc.ts +10 -2
- package/src/transformer/plugins/vite.ts +12 -25
- 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,15 +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
|
-
declare const initNamespace: () => void;
|
|
16
10
|
declare const needsArraySlotImport: (sourceFile: ts.SourceFile) => boolean;
|
|
17
|
-
|
|
18
|
-
export { addArraySlotImport, generateCode, generateReactiveInlining, getNames, initNamespace, needsArraySlotImport, setTypeChecker };
|
|
11
|
+
export { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport };
|
|
19
12
|
export type { CodegenResult };
|
|
@@ -1,26 +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
|
-
|
|
7
|
-
|
|
8
|
-
function collectNestedTemplateReplacements(node, exprStart, sourceFile, replacements) {
|
|
7
|
+
let printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
8
|
+
function collectNestedTemplateReplacements(ctx, node, exprStart, replacements) {
|
|
9
9
|
if (isNestedHtmlTemplate(node)) {
|
|
10
10
|
replacements.push({
|
|
11
11
|
end: node.end - exprStart,
|
|
12
|
-
newText: generateNestedTemplateCode(
|
|
12
|
+
newText: generateNestedTemplateCode(ctx, node),
|
|
13
13
|
start: node.getStart() - exprStart
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
|
-
ts.forEachChild(node, child => collectNestedTemplateReplacements(child, exprStart,
|
|
17
|
+
ts.forEachChild(node, child => collectNestedTemplateReplacements(ctx, child, exprStart, replacements));
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
function
|
|
21
|
-
return `import * as ${ns} from '@esportsplus/template';`;
|
|
22
|
-
}
|
|
23
|
-
function generateNestedTemplateCode(node, sourceFile) {
|
|
20
|
+
function generateNestedTemplateCode(ctx, node) {
|
|
24
21
|
let expressions = [], exprTexts = [], literals = [], template = node.template;
|
|
25
22
|
if (ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
26
23
|
literals.push(template.text);
|
|
@@ -31,38 +28,38 @@ function generateNestedTemplateCode(node, sourceFile) {
|
|
|
31
28
|
let expr = template.templateSpans[i].expression;
|
|
32
29
|
expressions.push(expr);
|
|
33
30
|
literals.push(template.templateSpans[i].literal.text);
|
|
34
|
-
exprTexts.push(rewriteExpression(
|
|
31
|
+
exprTexts.push(rewriteExpression(ctx, expr));
|
|
35
32
|
}
|
|
36
33
|
}
|
|
37
|
-
return generateTemplateCode(parser.parse(literals), exprTexts, expressions,
|
|
34
|
+
return generateTemplateCode(ctx, parser.parse(literals), exprTexts, expressions, false);
|
|
38
35
|
}
|
|
39
|
-
function generateNodeBinding(anchor, exprText, exprNode
|
|
36
|
+
function generateNodeBinding(ctx, anchor, exprText, exprNode) {
|
|
40
37
|
if (!exprNode) {
|
|
41
|
-
return `${
|
|
38
|
+
return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
42
39
|
}
|
|
43
40
|
if (isNestedHtmlTemplate(exprNode)) {
|
|
44
|
-
return `${anchor}.parentNode.insertBefore(${generateNestedTemplateCode(
|
|
41
|
+
return `${anchor}.parentNode.insertBefore(${generateNestedTemplateCode(ctx, exprNode)}, ${anchor});`;
|
|
45
42
|
}
|
|
46
|
-
let slotType = analyzeExpression(exprNode,
|
|
43
|
+
let slotType = analyzeExpression(exprNode, ctx.checker);
|
|
47
44
|
switch (slotType) {
|
|
48
|
-
case
|
|
49
|
-
return `new ${
|
|
50
|
-
case
|
|
51
|
-
return `new ${
|
|
52
|
-
case
|
|
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:
|
|
53
50
|
return `${anchor}.textContent = ${exprText};`;
|
|
54
|
-
case
|
|
51
|
+
case COMPILER_TYPES.DocumentFragment:
|
|
55
52
|
return `${anchor}.parentNode.insertBefore(${exprText}, ${anchor});`;
|
|
56
53
|
default:
|
|
57
|
-
return `${
|
|
54
|
+
return `${COMPILER_NAMESPACE}.slot(${anchor}, ${exprText});`;
|
|
58
55
|
}
|
|
59
56
|
}
|
|
60
|
-
function generateTemplateCode({ html, slots }, exprTexts, exprNodes,
|
|
57
|
+
function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, isArrowBody) {
|
|
61
58
|
if (!slots || slots.length === 0) {
|
|
62
|
-
return `${getOrCreateTemplateId(html)}()`;
|
|
59
|
+
return `${getOrCreateTemplateId(ctx, html)}()`;
|
|
63
60
|
}
|
|
64
61
|
let code = [], declarations = [], index = 0, nodes = new Map(), root = uid('root');
|
|
65
|
-
declarations.push(`${root} = ${getOrCreateTemplateId(html)}()`);
|
|
62
|
+
declarations.push(`${root} = ${getOrCreateTemplateId(ctx, html)}()`);
|
|
66
63
|
nodes.set('', root);
|
|
67
64
|
for (let i = 0, n = slots.length; i < n; i++) {
|
|
68
65
|
let path = slots[i].path;
|
|
@@ -91,24 +88,24 @@ function generateTemplateCode({ html, slots }, exprTexts, exprNodes, sourceFile,
|
|
|
91
88
|
let elementVar = slots[i].path.length === 0
|
|
92
89
|
? root
|
|
93
90
|
: (nodes.get(slots[i].path.join('.')) || root), slot = slots[i];
|
|
94
|
-
if (slot.type ===
|
|
91
|
+
if (slot.type === COMPILER_TYPES.AttributeSlot) {
|
|
95
92
|
for (let j = 0, m = slot.attributes.names.length; j < m; j++) {
|
|
96
93
|
let name = slot.attributes.names[j];
|
|
97
94
|
if (name === 'spread') {
|
|
98
|
-
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);
|
|
99
96
|
for (let k = 0, o = bindings.length; k < o; k++) {
|
|
100
97
|
code.push(bindings[k]);
|
|
101
98
|
}
|
|
102
99
|
index++;
|
|
103
100
|
}
|
|
104
101
|
else {
|
|
105
|
-
let binding = generateAttributeBinding(elementVar, name, exprTexts[index++] || 'undefined', slot.attributes.statics[name] || '');
|
|
102
|
+
let binding = generateAttributeBinding(elementVar, name, exprTexts[index++] || 'undefined', slot.attributes.statics[name] || '', COMPILER_NAMESPACE);
|
|
106
103
|
code.push(binding);
|
|
107
104
|
}
|
|
108
105
|
}
|
|
109
106
|
}
|
|
110
107
|
else {
|
|
111
|
-
code.push(generateNodeBinding(elementVar, exprTexts[index] || 'undefined', exprNodes[index]
|
|
108
|
+
code.push(generateNodeBinding(ctx, elementVar, exprTexts[index] || 'undefined', exprNodes[index]));
|
|
112
109
|
index++;
|
|
113
110
|
}
|
|
114
111
|
}
|
|
@@ -116,12 +113,12 @@ function generateTemplateCode({ html, slots }, exprTexts, exprNodes, sourceFile,
|
|
|
116
113
|
code.push(isArrowBody ? `}` : `})()`);
|
|
117
114
|
return code.join('\n');
|
|
118
115
|
}
|
|
119
|
-
function getOrCreateTemplateId(html) {
|
|
120
|
-
let id = htmlToTemplateId.get(html);
|
|
116
|
+
function getOrCreateTemplateId(ctx, html) {
|
|
117
|
+
let id = ctx.htmlToTemplateId.get(html);
|
|
121
118
|
if (!id) {
|
|
122
119
|
id = uid('tmpl');
|
|
123
|
-
hoistedFactories.set(id, html);
|
|
124
|
-
htmlToTemplateId.set(html, id);
|
|
120
|
+
ctx.hoistedFactories.set(id, html);
|
|
121
|
+
ctx.htmlToTemplateId.set(html, id);
|
|
125
122
|
}
|
|
126
123
|
return id;
|
|
127
124
|
}
|
|
@@ -143,77 +140,78 @@ function hasArraySlotImport(sourceFile) {
|
|
|
143
140
|
}
|
|
144
141
|
return false;
|
|
145
142
|
}
|
|
146
|
-
function
|
|
147
|
-
if (
|
|
148
|
-
ts.isIdentifier(node.expression) &&
|
|
149
|
-
node.expression.text === 'ArraySlot') {
|
|
143
|
+
function hasMatch(node, predicate) {
|
|
144
|
+
if (predicate(node)) {
|
|
150
145
|
return true;
|
|
151
146
|
}
|
|
152
147
|
let found = false;
|
|
153
148
|
ts.forEachChild(node, child => {
|
|
154
|
-
if (!found
|
|
155
|
-
found =
|
|
149
|
+
if (!found) {
|
|
150
|
+
found = hasMatch(child, predicate);
|
|
156
151
|
}
|
|
157
152
|
});
|
|
158
153
|
return found;
|
|
159
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
|
+
}
|
|
160
160
|
function hasNestedTemplates(node) {
|
|
161
|
-
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
let found = false;
|
|
165
|
-
ts.forEachChild(node, child => {
|
|
166
|
-
if (!found && hasNestedTemplates(child)) {
|
|
167
|
-
found = true;
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
return found;
|
|
161
|
+
return hasMatch(node, n => isNestedHtmlTemplate(n));
|
|
171
162
|
}
|
|
172
163
|
function isNestedHtmlTemplate(expr) {
|
|
173
|
-
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;
|
|
174
165
|
}
|
|
175
|
-
function isNestedTemplate(template,
|
|
176
|
-
for (let i = 0, n =
|
|
177
|
-
let
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
for (let j = 0, m = other.expressions.length; j < m; j++) {
|
|
182
|
-
let expr = other.expressions[j];
|
|
183
|
-
if (template.start >= expr.getStart() && template.end <= expr.end) {
|
|
184
|
-
return true;
|
|
185
|
-
}
|
|
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;
|
|
186
171
|
}
|
|
187
172
|
}
|
|
188
173
|
return false;
|
|
189
174
|
}
|
|
190
|
-
function rewriteExpression(
|
|
175
|
+
function rewriteExpression(ctx, expr) {
|
|
191
176
|
if (isNestedHtmlTemplate(expr)) {
|
|
192
|
-
return generateNestedTemplateCode(
|
|
177
|
+
return generateNestedTemplateCode(ctx, expr);
|
|
193
178
|
}
|
|
194
179
|
if (!hasNestedTemplates(expr)) {
|
|
195
|
-
return
|
|
180
|
+
return ctx.printer.printNode(ts.EmitHint.Expression, expr, ctx.sourceFile);
|
|
196
181
|
}
|
|
197
182
|
let exprStart = expr.getStart(), replacements = [];
|
|
198
|
-
collectNestedTemplateReplacements(expr, exprStart,
|
|
199
|
-
return
|
|
183
|
+
collectNestedTemplateReplacements(ctx, expr, exprStart, replacements);
|
|
184
|
+
return c.replaceReverse(expr.getText(ctx.sourceFile), replacements);
|
|
200
185
|
}
|
|
201
186
|
const addArraySlotImport = (code) => {
|
|
202
|
-
return `import * as ${
|
|
187
|
+
return `import * as ${COMPILER_NAMESPACE} from '${PACKAGE}';\n` + code;
|
|
203
188
|
};
|
|
204
|
-
const generateCode = (templates, originalCode, sourceFile) => {
|
|
189
|
+
const generateCode = (templates, originalCode, sourceFile, checker) => {
|
|
205
190
|
if (templates.length === 0) {
|
|
206
191
|
return { changed: false, code: originalCode };
|
|
207
192
|
}
|
|
208
|
-
let
|
|
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));
|
|
209
201
|
if (rootTemplates.length === 0) {
|
|
210
202
|
return { changed: false, code: originalCode };
|
|
211
203
|
}
|
|
212
|
-
let
|
|
204
|
+
let ctx = {
|
|
205
|
+
checker,
|
|
206
|
+
hoistedFactories: new Map(),
|
|
207
|
+
htmlToTemplateId: new Map(),
|
|
208
|
+
printer,
|
|
209
|
+
sourceFile
|
|
210
|
+
}, replacements = [];
|
|
213
211
|
for (let i = 0, n = rootTemplates.length; i < n; i++) {
|
|
214
212
|
let exprTexts = [], template = rootTemplates[i];
|
|
215
213
|
for (let j = 0, m = template.expressions.length; j < m; j++) {
|
|
216
|
-
exprTexts.push(rewriteExpression(template.expressions[j]
|
|
214
|
+
exprTexts.push(rewriteExpression(ctx, template.expressions[j]));
|
|
217
215
|
}
|
|
218
216
|
let codeBefore = originalCode.slice(0, template.start), isArrowBody = codeBefore.trimEnd().endsWith('=>'), parsed = parser.parse(template.literals);
|
|
219
217
|
if (isArrowBody && (!parsed.slots || parsed.slots.length === 0)) {
|
|
@@ -221,7 +219,7 @@ const generateCode = (templates, originalCode, sourceFile) => {
|
|
|
221
219
|
if (arrowMatch) {
|
|
222
220
|
replacements.push({
|
|
223
221
|
end: template.end,
|
|
224
|
-
newText: getOrCreateTemplateId(parsed.html),
|
|
222
|
+
newText: getOrCreateTemplateId(ctx, parsed.html),
|
|
225
223
|
start: template.start - arrowMatch[0].length
|
|
226
224
|
});
|
|
227
225
|
continue;
|
|
@@ -229,18 +227,17 @@ const generateCode = (templates, originalCode, sourceFile) => {
|
|
|
229
227
|
}
|
|
230
228
|
replacements.push({
|
|
231
229
|
end: template.end,
|
|
232
|
-
newText: generateTemplateCode(parsed, exprTexts, template.expressions,
|
|
230
|
+
newText: generateTemplateCode(ctx, parsed, exprTexts, template.expressions, isArrowBody),
|
|
233
231
|
start: template.start
|
|
234
232
|
});
|
|
235
233
|
}
|
|
236
|
-
let changed = replacements.length > 0, code =
|
|
237
|
-
if (changed && hoistedFactories.size > 0) {
|
|
234
|
+
let changed = replacements.length > 0, code = c.replaceReverse(originalCode, replacements);
|
|
235
|
+
if (changed && ctx.hoistedFactories.size > 0) {
|
|
238
236
|
let factories = [];
|
|
239
|
-
for (let [id, html] of hoistedFactories) {
|
|
240
|
-
factories.push(`const ${id} = ${
|
|
237
|
+
for (let [id, html] of ctx.hoistedFactories) {
|
|
238
|
+
factories.push(`const ${id} = ${COMPILER_NAMESPACE}.template(\`${html}\`);`);
|
|
241
239
|
}
|
|
242
|
-
code =
|
|
243
|
-
code = generateImports() + '\n\n' + factories.join('\n') + '\n\n' + code;
|
|
240
|
+
code = `import * as ${COMPILER_NAMESPACE} from '${PACKAGE}';\n\n` + factories.join('\n') + '\n\n' + code;
|
|
244
241
|
}
|
|
245
242
|
return { changed, code };
|
|
246
243
|
};
|
|
@@ -248,32 +245,21 @@ const generateReactiveInlining = (calls, code, sourceFile) => {
|
|
|
248
245
|
if (calls.length === 0) {
|
|
249
246
|
return code;
|
|
250
247
|
}
|
|
251
|
-
let
|
|
252
|
-
for (let i = calls.length
|
|
248
|
+
let replacements = [];
|
|
249
|
+
for (let i = 0, n = calls.length; i < n; i++) {
|
|
253
250
|
let call = calls[i];
|
|
254
|
-
|
|
255
|
-
|
|
251
|
+
replacements.push({
|
|
252
|
+
end: call.end,
|
|
253
|
+
newText: `new ${COMPILER_NAMESPACE}.ArraySlot(
|
|
256
254
|
${printer.printNode(ts.EmitHint.Expression, call.arrayArg, sourceFile)},
|
|
257
255
|
${printer.printNode(ts.EmitHint.Expression, call.callbackArg, sourceFile)}
|
|
258
|
-
)
|
|
259
|
-
|
|
256
|
+
)`,
|
|
257
|
+
start: call.start
|
|
258
|
+
});
|
|
260
259
|
}
|
|
261
|
-
return
|
|
262
|
-
};
|
|
263
|
-
const getNames = () => ({
|
|
264
|
-
attr: `${ns}.attributes`,
|
|
265
|
-
event: `${ns}.event`,
|
|
266
|
-
slot: `${ns}.slot`
|
|
267
|
-
});
|
|
268
|
-
const initNamespace = () => {
|
|
269
|
-
hoistedFactories.clear();
|
|
270
|
-
htmlToTemplateId.clear();
|
|
271
|
-
ns = uid('t');
|
|
260
|
+
return c.replaceReverse(code, replacements);
|
|
272
261
|
};
|
|
273
262
|
const needsArraySlotImport = (sourceFile) => {
|
|
274
263
|
return hasArraySlotUsage(sourceFile) && !hasArraySlotImport(sourceFile);
|
|
275
264
|
};
|
|
276
|
-
|
|
277
|
-
currentChecker = checker;
|
|
278
|
-
};
|
|
279
|
-
export { addArraySlotImport, generateCode, generateReactiveInlining, getNames, initNamespace, 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,19 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { addArraySlotImport, generateCode, generateReactiveInlining,
|
|
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
|
-
let
|
|
8
|
+
let typeChecker = program.getTypeChecker();
|
|
8
9
|
return (_context) => {
|
|
9
10
|
return (sourceFile) => {
|
|
10
|
-
let code =
|
|
11
|
-
if (!
|
|
11
|
+
let code = sourceFile.getFullText();
|
|
12
|
+
if (!c.contains(code, { patterns: PATTERNS })) {
|
|
12
13
|
return sourceFile;
|
|
13
14
|
}
|
|
14
|
-
let
|
|
15
|
-
setTypeChecker(typeChecker);
|
|
16
|
-
let result = transformCode(code, reparsed);
|
|
15
|
+
let result = transformCode(code, sourceFile, typeChecker);
|
|
17
16
|
if (!result.changed) {
|
|
18
17
|
return sourceFile;
|
|
19
18
|
}
|
|
@@ -21,41 +20,44 @@ function createTransformer(program) {
|
|
|
21
20
|
};
|
|
22
21
|
};
|
|
23
22
|
}
|
|
24
|
-
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
|
|
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);
|
|
28
39
|
if (reactiveCalls.length > 0) {
|
|
29
40
|
result = generateReactiveInlining(reactiveCalls, result, sourceFile);
|
|
30
|
-
result = addArraySlotImport(result);
|
|
31
41
|
changed = true;
|
|
32
42
|
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
33
|
-
|
|
43
|
+
needsImport = needsArraySlotImport(sourceFile);
|
|
44
|
+
checker = undefined;
|
|
34
45
|
}
|
|
35
46
|
let templates = findHtmlTemplates(sourceFile);
|
|
36
47
|
if (templates.length > 0) {
|
|
37
|
-
let codegenResult = generateCode(templates, result, sourceFile);
|
|
48
|
+
let codegenResult = generateCode(templates, result, sourceFile, checker);
|
|
38
49
|
if (codegenResult.changed) {
|
|
39
50
|
changed = true;
|
|
51
|
+
codegenChanged = true;
|
|
40
52
|
result = codegenResult.code;
|
|
41
|
-
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
42
53
|
}
|
|
43
54
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const transform = (sourceFile, program) => {
|
|
47
|
-
let printer = ts.createPrinter(), code = printer.printFile(sourceFile);
|
|
48
|
-
if (!mightNeedTransform(code, { patterns: PATTERNS })) {
|
|
49
|
-
return { changed: false, code, sourceFile };
|
|
50
|
-
}
|
|
51
|
-
let reparsed = ts.createSourceFile(sourceFile.fileName, code, sourceFile.languageVersion, true);
|
|
52
|
-
let programSourceFile = program.getSourceFile(sourceFile.fileName);
|
|
53
|
-
if (programSourceFile) {
|
|
54
|
-
setTypeChecker(program.getTypeChecker());
|
|
55
|
+
if (needsImport && !codegenChanged) {
|
|
56
|
+
result = addArraySlotImport(result);
|
|
55
57
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
if (changed) {
|
|
59
|
+
sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
58
60
|
}
|
|
59
|
-
return
|
|
61
|
+
return { changed, code: result, sourceFile };
|
|
60
62
|
};
|
|
61
|
-
export { createTransformer,
|
|
63
|
+
export { createTransformer, transform, transformCode, PATTERNS };
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { COMPILER_TYPES } from '../constants.js';
|
|
1
2
|
type NodePath = ('firstChild' | 'firstElementChild' | 'nextElementSibling' | 'nextSibling')[];
|
|
2
3
|
declare const _default: {
|
|
3
4
|
parse: (literals: string[]) => {
|
|
4
5
|
html: string;
|
|
5
6
|
slots: ({
|
|
6
7
|
path: NodePath;
|
|
7
|
-
type:
|
|
8
|
+
type: COMPILER_TYPES.NodeSlot;
|
|
8
9
|
} | {
|
|
9
10
|
attributes: {
|
|
10
11
|
names: string[];
|
|
11
12
|
statics: Record<string, string>;
|
|
12
13
|
};
|
|
13
14
|
path: NodePath;
|
|
14
|
-
type:
|
|
15
|
+
type: COMPILER_TYPES.AttributeSlot;
|
|
15
16
|
})[] | null;
|
|
16
17
|
};
|
|
17
18
|
};
|