@esportsplus/template 0.32.0 → 0.32.2

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.
Files changed (44) hide show
  1. package/build/attributes.js +1 -2
  2. package/build/constants.d.ts +18 -3
  3. package/build/constants.js +31 -4
  4. package/build/html.d.ts +3 -3
  5. package/build/index.d.ts +3 -3
  6. package/build/index.js +3 -3
  7. package/build/slot/array.d.ts +2 -2
  8. package/build/slot/array.js +5 -4
  9. package/build/slot/render.js +3 -2
  10. package/build/transformer/codegen.d.ts +3 -9
  11. package/build/transformer/codegen.js +90 -147
  12. package/build/transformer/index.d.ts +1 -5
  13. package/build/transformer/index.js +30 -46
  14. package/build/transformer/parser.d.ts +3 -2
  15. package/build/transformer/parser.js +4 -4
  16. package/build/transformer/plugins/tsc.d.ts +2 -2
  17. package/build/transformer/plugins/tsc.js +3 -4
  18. package/build/transformer/plugins/vite.d.ts +11 -3
  19. package/build/transformer/plugins/vite.js +7 -37
  20. package/build/transformer/ts-parser.d.ts +1 -2
  21. package/build/transformer/ts-parser.js +28 -41
  22. package/build/transformer/type-analyzer.d.ts +4 -5
  23. package/build/transformer/type-analyzer.js +73 -118
  24. package/build/types.d.ts +1 -1
  25. package/package.json +7 -7
  26. package/src/attributes.ts +1 -4
  27. package/src/constants.ts +42 -6
  28. package/src/html.ts +3 -3
  29. package/src/index.ts +5 -3
  30. package/src/slot/array.ts +9 -6
  31. package/src/slot/render.ts +5 -2
  32. package/src/transformer/codegen.ts +119 -189
  33. package/src/transformer/index.ts +34 -54
  34. package/src/transformer/parser.ts +10 -7
  35. package/src/transformer/plugins/tsc.ts +3 -5
  36. package/src/transformer/plugins/vite.ts +7 -47
  37. package/src/transformer/ts-parser.ts +34 -54
  38. package/src/transformer/type-analyzer.ts +90 -158
  39. package/src/types.ts +1 -1
  40. package/test/vite.config.ts +1 -1
  41. package/build/event/constants.d.ts +0 -3
  42. package/build/event/constants.js +0 -13
  43. package/src/event/constants.ts +0 -16
  44. package/storage/rewrite-analysis-2026-01-04.md +0 -439
@@ -1,62 +1,46 @@
1
- import { mightNeedTransform } from '@esportsplus/typescript/transformer';
2
- import { addArraySlotImport, generateCode, generateReactiveInlining, needsArraySlotImport, setTypeChecker } from './codegen.js';
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 = ['html`', 'html.reactive'];
6
- function createTransformer(program) {
7
- let typeChecker = program.getTypeChecker();
8
- return (_context) => {
9
- return (sourceFile) => {
10
- let code = sourceFile.getFullText();
11
- if (!mightNeedTransform(code, { patterns: PATTERNS })) {
12
- return sourceFile;
13
- }
14
- setTypeChecker(typeChecker);
15
- let result = transformCode(code, sourceFile);
16
- if (!result.changed) {
17
- return sourceFile;
18
- }
19
- return result.sourceFile;
20
- };
21
- };
22
- }
23
- function transformCode(code, sourceFile) {
24
- let changed = false, result = code;
25
- let reactiveCalls = findReactiveCalls(sourceFile);
6
+ const PATTERNS = [`${COMPILER_ENTRYPOINT}\``, `${COMPILER_ENTRYPOINT}.${COMPILER_ENTRYPOINT_REACTIVITY}`];
7
+ const REGEX_BACKSLASH = /\\/g;
8
+ const REGEX_FORWARD_SLASH = /\//g;
9
+ const transform = (sourceFile, program) => {
10
+ let code = sourceFile.getFullText();
11
+ if (!c.contains(code, { patterns: PATTERNS })) {
12
+ return { changed: false, code, sourceFile };
13
+ }
14
+ let checker, fileName = sourceFile.fileName, programSourceFile = program.getSourceFile(fileName)
15
+ || program.getSourceFile(fileName.replace(REGEX_BACKSLASH, '/'))
16
+ || program.getSourceFile(fileName.replace(REGEX_FORWARD_SLASH, '\\'));
17
+ if (programSourceFile) {
18
+ checker = program.getTypeChecker();
19
+ sourceFile = programSourceFile;
20
+ }
21
+ let changed = false, codegenChanged = false, needsImport = false, reactiveCalls = findReactiveCalls(sourceFile), result = code;
26
22
  if (reactiveCalls.length > 0) {
27
- result = generateReactiveInlining(reactiveCalls, result, sourceFile);
28
23
  changed = true;
24
+ checker = undefined;
25
+ result = generateReactiveInlining(reactiveCalls, result, sourceFile);
29
26
  sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
30
- if (needsArraySlotImport(sourceFile)) {
31
- result = addArraySlotImport(result);
32
- sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
33
- }
34
- setTypeChecker(undefined);
27
+ needsImport = needsArraySlotImport(sourceFile);
35
28
  }
36
29
  let templates = findHtmlTemplates(sourceFile);
37
30
  if (templates.length > 0) {
38
- let codegenResult = generateCode(templates, result, sourceFile);
31
+ let codegenResult = generateCode(templates, result, sourceFile, checker);
39
32
  if (codegenResult.changed) {
40
33
  changed = true;
34
+ codegenChanged = true;
41
35
  result = codegenResult.code;
42
- sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
43
36
  }
44
37
  }
45
- return { changed, code: result, sourceFile };
46
- }
47
- const transform = (sourceFile, program) => {
48
- let code = sourceFile.getFullText();
49
- if (!mightNeedTransform(code, { patterns: PATTERNS })) {
50
- return { changed: false, code, sourceFile };
38
+ if (needsImport && !codegenChanged) {
39
+ result = addArraySlotImport(result);
51
40
  }
52
- let programSourceFile = program.getSourceFile(sourceFile.fileName);
53
- if (programSourceFile) {
54
- setTypeChecker(program.getTypeChecker());
55
- sourceFile = programSourceFile;
56
- }
57
- else {
58
- setTypeChecker(undefined);
41
+ if (changed) {
42
+ sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
59
43
  }
60
- return transformCode(code, sourceFile);
44
+ return { changed, code: result, sourceFile };
61
45
  };
62
- export { createTransformer, mightNeedTransform, PATTERNS, transform };
46
+ export { transform };
@@ -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: "slot";
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: "attributes";
15
+ type: COMPILER_TYPES.AttributeSlot;
15
16
  })[] | null;
16
17
  };
17
18
  };
@@ -1,4 +1,4 @@
1
- const SLOT_HTML = '<!--$-->';
1
+ import { COMPILER_TYPES, PACKAGE, SLOT_HTML } from '../constants.js';
2
2
  const ATTRIBUTE_DELIMITERS = {
3
3
  class: ' ',
4
4
  style: ';'
@@ -125,9 +125,9 @@ const parse = (literals) => {
125
125
  if (attr) {
126
126
  let attrs = attributes[attr];
127
127
  if (!attrs) {
128
- throw new Error(`@esportsplus/template: attribute metadata could not be found for '${attr}'`);
128
+ throw new Error(`${PACKAGE}: attribute metadata could not be found for '${attr}'`);
129
129
  }
130
- slots.push({ attributes: attrs, path, type: 'attributes' });
130
+ slots.push({ attributes: attrs, path, type: COMPILER_TYPES.AttributeSlot });
131
131
  for (let i = 0, n = attrs.names.length; i < n; i++) {
132
132
  buffer += parsed[slot++];
133
133
  }
@@ -139,7 +139,7 @@ const parse = (literals) => {
139
139
  }
140
140
  else if (type === NODE_SLOT) {
141
141
  buffer += parsed[slot++] + SLOT_HTML;
142
- slots.push({ path: methods(parent.children, parent.path, 'firstChild', 'nextSibling'), type: 'slot' });
142
+ slots.push({ path: methods(parent.children, parent.path, 'firstChild', 'nextSibling'), type: COMPILER_TYPES.NodeSlot });
143
143
  }
144
144
  if (n === slot) {
145
145
  buffer += parsed[slot];
@@ -1,3 +1,3 @@
1
- import { ts } from '@esportsplus/typescript';
2
- declare const _default: (program: ts.Program) => ts.TransformerFactory<ts.SourceFile>;
1
+ import { plugin } from '@esportsplus/typescript/transformer';
2
+ declare const _default: ReturnType<typeof plugin.tsc>;
3
3
  export default _default;
@@ -1,4 +1,3 @@
1
- import { createTransformer } from '../index.js';
2
- export default (program) => {
3
- return createTransformer(program);
4
- };
1
+ import { plugin } from '@esportsplus/typescript/transformer';
2
+ import { transform } from '../index.js';
3
+ export default plugin.tsc(transform);
@@ -1,5 +1,13 @@
1
- import type { Plugin } from 'vite';
2
- declare const _default: (options?: {
1
+ declare const _default: ({ root }?: {
3
2
  root?: string;
4
- }) => Plugin;
3
+ }) => {
4
+ configResolved(config: import("vite").ResolvedConfig): void;
5
+ enforce: string;
6
+ name: string;
7
+ transform(code: string, id: string): {
8
+ code: string;
9
+ map: null;
10
+ } | null;
11
+ watchChange(id: string): void;
12
+ };
5
13
  export default _default;
@@ -1,37 +1,7 @@
1
- import { mightNeedTransform, PATTERNS, transform } from '../index.js';
2
- import { program, TRANSFORM_PATTERN } from '@esportsplus/typescript/transformer';
3
- import { ts } from '@esportsplus/typescript';
4
- export default (options) => {
5
- let root;
6
- return {
7
- enforce: 'pre',
8
- name: '@esportsplus/template/plugin-vite',
9
- configResolved(config) {
10
- root = options?.root ?? config.root;
11
- },
12
- transform(code, id) {
13
- if (!TRANSFORM_PATTERN.test(id) || id.includes('node_modules')) {
14
- return null;
15
- }
16
- if (!mightNeedTransform(code, { patterns: PATTERNS })) {
17
- return null;
18
- }
19
- try {
20
- let sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true), result = transform(sourceFile, program.get(root));
21
- if (!result.changed) {
22
- return null;
23
- }
24
- return { code: result.code, map: null };
25
- }
26
- catch (error) {
27
- console.error(`@esportsplus/template: Error transforming ${id}:`, error);
28
- return null;
29
- }
30
- },
31
- watchChange(id) {
32
- if (TRANSFORM_PATTERN.test(id)) {
33
- program.delete(root);
34
- }
35
- }
36
- };
37
- };
1
+ import { plugin } from '@esportsplus/typescript/transformer';
2
+ import { PACKAGE } from '../../constants.js';
3
+ import { transform } from '../index.js';
4
+ export default plugin.vite({
5
+ name: PACKAGE,
6
+ transform
7
+ });
@@ -16,6 +16,5 @@ type TemplateInfo = {
16
16
  };
17
17
  declare const findHtmlTemplates: (sourceFile: ts.SourceFile) => TemplateInfo[];
18
18
  declare const findReactiveCalls: (sourceFile: ts.SourceFile) => ReactiveCallInfo[];
19
- declare const getTemplateExpressions: (info: TemplateInfo, sourceFile: ts.SourceFile) => string[];
20
- export { findHtmlTemplates, findReactiveCalls, getTemplateExpressions };
19
+ export { findHtmlTemplates, findReactiveCalls };
21
20
  export type { ReactiveCallInfo, TemplateInfo };
@@ -1,38 +1,11 @@
1
+ import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY } from '../constants.js';
1
2
  import { ts } from '@esportsplus/typescript';
2
- function extractTemplateInfo(node, depth) {
3
- let expressions = [], literals = [], template = node.template;
4
- if (ts.isNoSubstitutionTemplateLiteral(template)) {
5
- literals.push(template.text);
6
- }
7
- else if (ts.isTemplateExpression(template)) {
8
- literals.push(template.head.text);
9
- for (let i = 0, n = template.templateSpans.length; i < n; i++) {
10
- let span = template.templateSpans[i];
11
- expressions.push(span.expression);
12
- literals.push(span.literal.text);
13
- }
14
- }
15
- return {
16
- depth,
17
- end: node.end,
18
- expressions,
19
- literals,
20
- node,
21
- start: node.getStart()
22
- };
23
- }
24
- function isFunctionNode(node) {
25
- return (ts.isArrowFunction(node) ||
26
- ts.isFunctionDeclaration(node) ||
27
- ts.isFunctionExpression(node) ||
28
- ts.isMethodDeclaration(node));
29
- }
30
3
  function visitReactiveCalls(node, calls) {
31
4
  if (ts.isCallExpression(node) &&
32
5
  ts.isPropertyAccessExpression(node.expression) &&
33
6
  ts.isIdentifier(node.expression.expression) &&
34
- node.expression.expression.text === 'html' &&
35
- node.expression.name.text === 'reactive' &&
7
+ node.expression.expression.text === COMPILER_ENTRYPOINT &&
8
+ node.expression.name.text === COMPILER_ENTRYPOINT_REACTIVITY &&
36
9
  node.arguments.length === 2) {
37
10
  calls.push({
38
11
  arrayArg: node.arguments[0],
@@ -45,9 +18,30 @@ function visitReactiveCalls(node, calls) {
45
18
  ts.forEachChild(node, child => visitReactiveCalls(child, calls));
46
19
  }
47
20
  function visitTemplates(node, depth, templates) {
48
- let nextDepth = isFunctionNode(node) ? depth + 1 : depth;
49
- if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && node.tag.text === 'html') {
50
- templates.push(extractTemplateInfo(node, depth));
21
+ let nextDepth = (ts.isArrowFunction(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isMethodDeclaration(node))
22
+ ? depth + 1
23
+ : depth;
24
+ if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && node.tag.text === COMPILER_ENTRYPOINT) {
25
+ let expressions = [], literals = [], template = node.template;
26
+ if (ts.isNoSubstitutionTemplateLiteral(template)) {
27
+ literals.push(template.text);
28
+ }
29
+ else if (ts.isTemplateExpression(template)) {
30
+ literals.push(template.head.text);
31
+ for (let i = 0, n = template.templateSpans.length; i < n; i++) {
32
+ let span = template.templateSpans[i];
33
+ expressions.push(span.expression);
34
+ literals.push(span.literal.text);
35
+ }
36
+ }
37
+ templates.push({
38
+ depth,
39
+ end: node.end,
40
+ expressions,
41
+ literals,
42
+ node,
43
+ start: node.getStart()
44
+ });
51
45
  }
52
46
  ts.forEachChild(node, child => visitTemplates(child, nextDepth, templates));
53
47
  }
@@ -62,11 +56,4 @@ const findReactiveCalls = (sourceFile) => {
62
56
  visitReactiveCalls(sourceFile, calls);
63
57
  return calls;
64
58
  };
65
- const getTemplateExpressions = (info, sourceFile) => {
66
- let exprs = [];
67
- for (let i = 0, n = info.expressions.length; i < n; i++) {
68
- exprs.push(info.expressions[i].getText(sourceFile));
69
- }
70
- return exprs;
71
- };
72
- export { findHtmlTemplates, findReactiveCalls, getTemplateExpressions };
59
+ export { findHtmlTemplates, findReactiveCalls };
@@ -1,7 +1,6 @@
1
+ import { COMPILER_TYPES } from '../constants.js';
1
2
  import { ts } from '@esportsplus/typescript';
2
- type SlotType = 'array-slot' | 'document-fragment' | 'effect' | 'node' | 'primitive' | 'static' | 'unknown';
3
- declare const analyzeExpression: (expr: ts.Expression, checker?: ts.TypeChecker) => SlotType;
4
- declare const generateAttributeBinding: (elementVar: string, name: string, expr: string, staticValue: string) => string;
5
- declare const generateSpreadBindings: (expr: ts.Expression, exprCode: string, elementVar: string, sourceFile: ts.SourceFile, checker?: ts.TypeChecker) => string[];
3
+ declare const analyzeExpression: (expr: ts.Expression, checker?: ts.TypeChecker) => COMPILER_TYPES;
4
+ declare const generateAttributeBinding: (elementVar: string, name: string, expr: string, staticValue: string, ns: string) => string;
5
+ declare const generateSpreadBindings: (expr: ts.Expression, exprCode: string, elementVar: string, checker: ts.TypeChecker | undefined, ns: string) => string[];
6
6
  export { analyzeExpression, generateAttributeBinding, generateSpreadBindings };
7
- export type { SlotType };
@@ -1,5 +1,4 @@
1
- import { getNames } from './codegen.js';
2
- import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../event/constants.js';
1
+ import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REACTIVITY, COMPILER_TYPES, DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS } from '../constants.js';
3
2
  import { ts } from '@esportsplus/typescript';
4
3
  function analyzeSpread(expr, checker) {
5
4
  while (ts.isParenthesizedExpression(expr)) {
@@ -25,143 +24,84 @@ function analyzeSpread(expr, checker) {
25
24
  }
26
25
  if (checker && (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr))) {
27
26
  try {
28
- let keys = extractTypePropertyKeys(checker.getTypeAtLocation(expr));
27
+ let keys = [], props = checker.getTypeAtLocation(expr).getProperties();
28
+ for (let i = 0, n = props.length; i < n; i++) {
29
+ let name = props[i].getName();
30
+ if (name.startsWith('__') || name.startsWith('[')) {
31
+ continue;
32
+ }
33
+ keys.push(name);
34
+ }
29
35
  if (keys.length > 0) {
30
36
  return { canUnpack: true, keys };
31
37
  }
32
38
  }
33
- catch {
34
- }
39
+ catch { }
35
40
  }
36
41
  return { canUnpack: false, keys: [] };
37
42
  }
38
- function extractTypePropertyKeys(type) {
39
- let keys = [], props = type.getProperties();
40
- for (let i = 0, n = props.length; i < n; i++) {
41
- let name = props[i].getName();
42
- if (name.startsWith('__') || name.startsWith('[')) {
43
- continue;
44
- }
45
- keys.push(name);
46
- }
47
- return keys;
48
- }
49
- function getObjectPropertyValue(expr, key, sourceFile) {
50
- for (let i = 0, n = expr.properties.length; i < n; i++) {
51
- let prop = expr.properties[i];
52
- if (ts.isPropertyAssignment(prop)) {
53
- let name = ts.isIdentifier(prop.name)
54
- ? prop.name.text
55
- : ts.isStringLiteral(prop.name) ? prop.name.text : null;
56
- if (name === key) {
57
- return prop.initializer.getText(sourceFile);
58
- }
59
- }
60
- else if (ts.isShorthandPropertyAssignment(prop) && prop.name.text === key) {
61
- return prop.name.text;
62
- }
63
- }
64
- return null;
65
- }
66
- function inferSlotType(expr, ctx) {
43
+ function inferCOMPILER_TYPES(expr, checker) {
67
44
  while (ts.isParenthesizedExpression(expr)) {
68
45
  expr = expr.expression;
69
46
  }
70
47
  if (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr)) {
71
- return 'effect';
48
+ return COMPILER_TYPES.Effect;
72
49
  }
73
50
  if (ts.isCallExpression(expr) &&
74
51
  ts.isPropertyAccessExpression(expr.expression) &&
75
52
  ts.isIdentifier(expr.expression.expression) &&
76
- expr.expression.expression.text === 'html' &&
77
- expr.expression.name.text === 'reactive') {
78
- return 'array-slot';
53
+ expr.expression.expression.text === COMPILER_ENTRYPOINT &&
54
+ expr.expression.name.text === COMPILER_ENTRYPOINT_REACTIVITY) {
55
+ return COMPILER_TYPES.ArraySlot;
79
56
  }
80
- if (ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === 'html') {
81
- return 'document-fragment';
57
+ if (ts.isTaggedTemplateExpression(expr) && ts.isIdentifier(expr.tag) && expr.tag.text === COMPILER_ENTRYPOINT) {
58
+ return COMPILER_TYPES.DocumentFragment;
82
59
  }
83
60
  if (ts.isArrayLiteralExpression(expr)) {
84
- return 'array-slot';
85
- }
86
- if (ts.isStringLiteral(expr) || ts.isNoSubstitutionTemplateLiteral(expr)) {
87
- return 'static';
88
- }
89
- if (ts.isNumericLiteral(expr)) {
90
- return 'static';
61
+ return COMPILER_TYPES.ArraySlot;
91
62
  }
92
- if (expr.kind === ts.SyntaxKind.TrueKeyword || expr.kind === ts.SyntaxKind.FalseKeyword) {
93
- return 'static';
94
- }
95
- if (expr.kind === ts.SyntaxKind.NullKeyword || expr.kind === ts.SyntaxKind.UndefinedKeyword) {
96
- return 'static';
63
+ if (ts.isNumericLiteral(expr) ||
64
+ ts.isStringLiteral(expr) ||
65
+ ts.isNoSubstitutionTemplateLiteral(expr) ||
66
+ expr.kind === ts.SyntaxKind.TrueKeyword ||
67
+ expr.kind === ts.SyntaxKind.FalseKeyword ||
68
+ expr.kind === ts.SyntaxKind.NullKeyword ||
69
+ expr.kind === ts.SyntaxKind.UndefinedKeyword) {
70
+ return COMPILER_TYPES.Static;
97
71
  }
98
72
  if (ts.isTemplateExpression(expr)) {
99
- return 'primitive';
73
+ return COMPILER_TYPES.Primitive;
100
74
  }
101
75
  if (ts.isConditionalExpression(expr)) {
102
- let whenFalse = inferSlotType(expr.whenFalse, ctx), whenTrue = inferSlotType(expr.whenTrue, ctx);
76
+ let whenFalse = inferCOMPILER_TYPES(expr.whenFalse, checker), whenTrue = inferCOMPILER_TYPES(expr.whenTrue, checker);
103
77
  if (whenTrue === whenFalse) {
104
78
  return whenTrue;
105
79
  }
106
- if (whenTrue === 'effect' || whenFalse === 'effect') {
107
- return 'effect';
80
+ if (whenTrue === COMPILER_TYPES.Effect || whenFalse === COMPILER_TYPES.Effect) {
81
+ return COMPILER_TYPES.Effect;
108
82
  }
109
- return 'unknown';
110
- }
111
- if (ctx?.checker) {
112
- let checker = ctx.checker;
113
- if (ts.isIdentifier(expr)) {
114
- try {
115
- let type = checker.getTypeAtLocation(expr);
116
- if (isTypeFunction(type, checker)) {
117
- return 'effect';
118
- }
119
- if (isTypeArray(type, checker)) {
120
- return 'array-slot';
121
- }
122
- }
123
- catch {
124
- }
125
- }
126
- if (ts.isPropertyAccessExpression(expr)) {
127
- try {
128
- let type = checker.getTypeAtLocation(expr);
129
- if (isTypeFunction(type, checker)) {
130
- return 'effect';
131
- }
132
- if (isTypeArray(type, checker)) {
133
- return 'array-slot';
134
- }
83
+ return COMPILER_TYPES.Unknown;
84
+ }
85
+ if (checker && (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr) || ts.isCallExpression(expr))) {
86
+ try {
87
+ let type = checker.getTypeAtLocation(expr);
88
+ if (isTypeFunction(type, checker)) {
89
+ return COMPILER_TYPES.Effect;
135
90
  }
136
- catch {
91
+ if (isTypeArray(type, checker)) {
92
+ return COMPILER_TYPES.ArraySlot;
137
93
  }
138
94
  }
139
- if (ts.isCallExpression(expr)) {
140
- try {
141
- let type = checker.getTypeAtLocation(expr);
142
- if (isTypeFunction(type, checker)) {
143
- return 'effect';
144
- }
145
- if (isTypeArray(type, checker)) {
146
- return 'array-slot';
147
- }
148
- }
149
- catch {
150
- }
95
+ catch {
151
96
  }
152
97
  }
153
- return 'unknown';
98
+ return COMPILER_TYPES.Unknown;
154
99
  }
155
100
  function isTypeArray(type, checker) {
156
- let typeStr = checker.typeToString(type);
157
- if (typeStr.endsWith('[]') || typeStr.startsWith('Array<') || typeStr.startsWith('ReactiveArray<')) {
158
- return true;
159
- }
160
- let symbol = type.getSymbol();
161
- if (symbol && (symbol.getName() === 'Array' || symbol.getName() === 'ReactiveArray')) {
101
+ if (checker.isArrayType(type)) {
162
102
  return true;
163
103
  }
164
- return false;
104
+ return type.getSymbol()?.getName() === 'ReactiveArray';
165
105
  }
166
106
  function isTypeFunction(type, checker) {
167
107
  if (type.getCallSignatures().length > 0) {
@@ -177,52 +117,67 @@ function isTypeFunction(type, checker) {
177
117
  return false;
178
118
  }
179
119
  const analyzeExpression = (expr, checker) => {
180
- return inferSlotType(expr, checker ? { checker } : undefined);
120
+ return inferCOMPILER_TYPES(expr, checker);
181
121
  };
182
- const generateAttributeBinding = (elementVar, name, expr, staticValue) => {
183
- let n = getNames();
122
+ const generateAttributeBinding = (elementVar, name, expr, staticValue, ns) => {
184
123
  if (name.startsWith('on') && name.length > 2) {
185
124
  let event = name.slice(2).toLowerCase(), key = name.toLowerCase();
186
125
  if (LIFECYCLE_EVENTS.has(key)) {
187
- return `${n.event}.${key}(${elementVar}, ${expr});`;
126
+ return `${ns}.event.${key}(${elementVar}, ${expr});`;
188
127
  }
189
128
  if (DIRECT_ATTACH_EVENTS.has(key)) {
190
- return `${n.event}.direct(${elementVar}, '${event}', ${expr});`;
129
+ return `${ns}.event.direct(${elementVar}, '${event}', ${expr});`;
191
130
  }
192
- return `${n.event}.delegate(${elementVar}, '${event}', ${expr});`;
131
+ return `${ns}.event.delegate(${elementVar}, '${event}', ${expr});`;
193
132
  }
194
133
  if (name === 'class') {
195
- return `${n.attr}.setClass(${elementVar}, '${staticValue}', ${expr});`;
134
+ return `${ns}.attributes.setClass(${elementVar}, '${staticValue}', ${expr});`;
196
135
  }
197
136
  if (name === 'spread') {
198
- return `${n.attr}.spread(${elementVar}, ${expr});`;
137
+ return `${ns}.attributes.spread(${elementVar}, ${expr});`;
199
138
  }
200
139
  if (name === 'style') {
201
- return `${n.attr}.setStyle(${elementVar}, '${staticValue}', ${expr});`;
140
+ return `${ns}.attributes.setStyle(${elementVar}, '${staticValue}', ${expr});`;
202
141
  }
203
- return `${n.attr}.setProperty(${elementVar}, '${name}', ${expr});`;
142
+ return `${ns}.attributes.setProperty(${elementVar}, '${name}', ${expr});`;
204
143
  };
205
- const generateSpreadBindings = (expr, exprCode, elementVar, sourceFile, checker) => {
144
+ const generateSpreadBindings = (expr, exprCode, elementVar, checker, ns) => {
206
145
  while (ts.isParenthesizedExpression(expr)) {
207
146
  expr = expr.expression;
208
147
  }
209
148
  let analysis = analyzeSpread(expr, checker);
210
149
  if (!analysis.canUnpack) {
211
- return [`${getNames().attr}.spread(${elementVar}, ${exprCode});`];
150
+ return [`${ns}.attributes.spread(${elementVar}, ${exprCode});`];
212
151
  }
213
152
  let lines = [];
214
153
  if (ts.isObjectLiteralExpression(expr)) {
215
154
  for (let i = 0, n = analysis.keys.length; i < n; i++) {
216
- let key = analysis.keys[i], value = getObjectPropertyValue(expr, key, sourceFile);
155
+ let key = analysis.keys[i], value = null;
156
+ for (let j = 0, m = expr.properties.length; j < m; j++) {
157
+ let prop = expr.properties[j];
158
+ if (ts.isPropertyAssignment(prop)) {
159
+ let text = ts.isIdentifier(prop.name)
160
+ ? prop.name.text
161
+ : ts.isStringLiteral(prop.name) ? prop.name.text : null;
162
+ if (text === key) {
163
+ value = prop.initializer.getText();
164
+ break;
165
+ }
166
+ }
167
+ else if (ts.isShorthandPropertyAssignment(prop) && prop.name.text === key) {
168
+ value = prop.name.text;
169
+ break;
170
+ }
171
+ }
217
172
  if (value !== null) {
218
- lines.push(generateAttributeBinding(elementVar, key, value, ''));
173
+ lines.push(generateAttributeBinding(elementVar, key, value, '', ns));
219
174
  }
220
175
  }
221
176
  }
222
177
  else {
223
178
  for (let i = 0, n = analysis.keys.length; i < n; i++) {
224
179
  let key = analysis.keys[i];
225
- lines.push(generateAttributeBinding(elementVar, key, `${exprCode}.${key}`, ''));
180
+ lines.push(generateAttributeBinding(elementVar, key, `${exprCode}.${key}`, '', ns));
226
181
  }
227
182
  }
228
183
  return lines;
package/build/types.d.ts CHANGED
@@ -17,7 +17,7 @@ type Attributes<T extends HTMLElement = Element> = {
17
17
  type Effect<T> = () => T extends [] ? Renderable<T>[] : Renderable<T>;
18
18
  type Element = HTMLElement & Attributes<any>;
19
19
  type Primitive = bigint | boolean | null | number | string | undefined;
20
- type Renderable<T> = DocumentFragment | ArraySlot<T> | Effect<T> | Node | NodeList | Primitive | Renderable<T>[];
20
+ type Renderable<T> = ArraySlot<T> | DocumentFragment | Effect<T> | Node | NodeList | Primitive | Renderable<T>[];
21
21
  type SlotGroup = {
22
22
  head: Element;
23
23
  tail: Element;