@sprlab/wccompiler 0.0.2 → 0.2.0

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/lib/printer.js CHANGED
@@ -1,104 +1,118 @@
1
1
  /**
2
- * Pretty Printer — reconstructs .html source from a ParseResult IR.
2
+ * Pretty Printer — serializes a ParseResult IR back to valid .js source format.
3
3
  *
4
- * Produces a valid source file with <template>, <script>, and <style> blocks
5
- * that preserves the order and semantics of all IR elements.
4
+ * Used for round-trip testing: parse prettyPrint parse should yield
5
+ * an equivalent IR.
6
6
  */
7
7
 
8
- /**
9
- * Reconstruct the <template> block from the IR.
10
- * Uses the original template string (before tree-walking) since it
11
- * already contains {{var}} bindings, @event attributes, and <slot> elements.
12
- *
13
- * @param {import('./parser.js').ParseResult} ir
14
- * @returns {string}
15
- */
16
- function reconstructTemplate(ir) {
17
- return ir.template;
18
- }
8
+ /** @import { ParseResult } from './types.js' */
19
9
 
20
10
  /**
21
- * Reconstruct the <script> block from the IR's extracted constructs.
11
+ * Pretty-print a ParseResult IR back to component source format.
22
12
  *
23
- * @param {import('./parser.js').ParseResult} ir
24
- * @returns {string}
13
+ * @param {ParseResult} ir — The intermediate representation
14
+ * @returns {string} Reconstructed source code
25
15
  */
26
- function reconstructScript(ir) {
27
- const lines = [];
16
+ export function prettyPrint(ir) {
17
+ const sections = [];
28
18
 
29
- // defineProps
30
- if (ir.props.length > 0) {
31
- const propsList = ir.props.map(p => `'${p}'`).join(', ');
32
- lines.push(` defineProps([${propsList}])`);
33
- lines.push('');
34
- }
19
+ // 1. Import statement — include only macros actually used
20
+ const macros = ['defineComponent'];
21
+ if ((ir.propDefs || []).length > 0) macros.push('defineProps');
22
+ if ((ir.emits || []).length > 0) macros.push('defineEmits');
23
+ if (ir.signals.length > 0) macros.push('signal');
24
+ if (ir.computeds.length > 0) macros.push('computed');
25
+ if (ir.effects.length > 0) macros.push('effect');
26
+ if ((ir.onMountHooks || []).length > 0) macros.push('onMount');
27
+ if ((ir.onDestroyHooks || []).length > 0) macros.push('onDestroy');
28
+ sections.push(`import { ${macros.join(', ')} } from 'wcc'`);
35
29
 
36
- // Reactive variables
37
- for (const v of ir.reactiveVars) {
38
- lines.push(` const ${v.name} = ${v.value}`);
30
+ // 2. defineComponent call
31
+ const defParts = [];
32
+ defParts.push(` tag: '${ir.tagName}',`);
33
+ defParts.push(` template: './${ir.tagName}.html',`);
34
+ if (ir.style !== '') {
35
+ defParts.push(` styles: './${ir.tagName}.css',`);
39
36
  }
40
- if (ir.reactiveVars.length > 0) lines.push('');
37
+ sections.push(`export default defineComponent({\n${defParts.join('\n')}\n})`);
41
38
 
42
- // Computed properties
43
- for (const c of ir.computeds) {
44
- lines.push(` const ${c.name} = computed(() => ${c.body})`);
39
+ // 3. defineProps (if present)
40
+ if ((ir.propDefs || []).length > 0) {
41
+ const propsObjName = ir.propsObjectName || 'props';
42
+ const propEntries = ir.propDefs.map(p => `${p.name}: ${p.default}`);
43
+ sections.push(`const ${propsObjName} = defineProps({ ${propEntries.join(', ')} })`);
45
44
  }
46
- if (ir.computeds.length > 0) lines.push('');
47
45
 
48
- // Watchers
49
- for (const w of ir.watchers) {
50
- lines.push(` watch('${w.target}', (${w.newParam}, ${w.oldParam}) => {`);
51
- // Indent the body lines
52
- for (const bodyLine of w.body.split('\n')) {
53
- lines.push(` ${bodyLine}`);
54
- }
55
- lines.push(' })');
46
+ // 3b. defineEmits (if present)
47
+ if ((ir.emits || []).length > 0) {
48
+ const emitsObjName = ir.emitsObjectName || 'emit';
49
+ const emitEntries = ir.emits.map(e => `'${e}'`).join(', ');
50
+ sections.push(`const ${emitsObjName} = defineEmits([${emitEntries}])`);
56
51
  }
57
- if (ir.watchers.length > 0) lines.push('');
58
52
 
59
- // Functions
60
- for (const m of ir.methods) {
61
- lines.push(` function ${m.name}(${m.params}) {`);
62
- for (const bodyLine of m.body.split('\n')) {
63
- lines.push(` ${bodyLine}`);
64
- }
65
- lines.push(' }');
53
+ // 4. Signal declarations
54
+ if (ir.signals.length > 0) {
55
+ const signalLines = ir.signals.map(
56
+ s => `const ${s.name} = signal(${s.value})`
57
+ );
58
+ sections.push(signalLines.join('\n'));
66
59
  }
67
60
 
68
- return lines.join('\n');
69
- }
61
+ // 5. Computed declarations
62
+ if (ir.computeds.length > 0) {
63
+ const computedLines = ir.computeds.map(
64
+ c => `const ${c.name} = computed(() => ${c.body})`
65
+ );
66
+ sections.push(computedLines.join('\n'));
67
+ }
70
68
 
71
- /**
72
- * Pretty-print a ParseResult IR back to .html source format.
73
- *
74
- * @param {import('./parser.js').ParseResult} ir - The intermediate representation
75
- * @returns {string} Reconstructed .html source
76
- */
77
- export function prettyPrint(ir) {
78
- const parts = [];
69
+ // 6. Effect declarations
70
+ if (ir.effects.length > 0) {
71
+ const effectBlocks = ir.effects.map(e => {
72
+ const indentedBody = e.body
73
+ .split('\n')
74
+ .map(line => ` ${line}`)
75
+ .join('\n');
76
+ return `effect(() => {\n${indentedBody}\n})`;
77
+ });
78
+ sections.push(effectBlocks.join('\n\n'));
79
+ }
79
80
 
80
- // <template> block
81
- parts.push('<template>');
82
- parts.push(reconstructTemplate(ir));
83
- parts.push('</template>');
81
+ // 7. Function declarations
82
+ if (ir.methods.length > 0) {
83
+ const fnBlocks = ir.methods.map(m => {
84
+ const indentedBody = m.body
85
+ .split('\n')
86
+ .map(line => ` ${line}`)
87
+ .join('\n');
88
+ return `function ${m.name}(${m.params}) {\n${indentedBody}\n}`;
89
+ });
90
+ sections.push(fnBlocks.join('\n\n'));
91
+ }
84
92
 
85
- // <style> block (if present)
86
- if (ir.style) {
87
- parts.push('');
88
- parts.push('<style>');
89
- parts.push(ir.style);
90
- parts.push('</style>');
93
+ // 8. Lifecycle hooks — onMount
94
+ if ((ir.onMountHooks || []).length > 0) {
95
+ const mountBlocks = ir.onMountHooks.map(h => {
96
+ const indentedBody = h.body
97
+ .split('\n')
98
+ .map(line => ` ${line}`)
99
+ .join('\n');
100
+ return `onMount(() => {\n${indentedBody}\n})`;
101
+ });
102
+ sections.push(mountBlocks.join('\n\n'));
91
103
  }
92
104
 
93
- // <script> block (if there's any script content)
94
- const scriptContent = reconstructScript(ir);
95
- if (scriptContent.trim()) {
96
- parts.push('');
97
- parts.push('<script>');
98
- parts.push(scriptContent);
99
- parts.push('</script>');
105
+ // 9. Lifecycle hooks onDestroy
106
+ if ((ir.onDestroyHooks || []).length > 0) {
107
+ const destroyBlocks = ir.onDestroyHooks.map(h => {
108
+ const indentedBody = h.body
109
+ .split('\n')
110
+ .map(line => ` ${line}`)
111
+ .join('\n');
112
+ return `onDestroy(() => {\n${indentedBody}\n})`;
113
+ });
114
+ sections.push(destroyBlocks.join('\n\n'));
100
115
  }
101
116
 
102
- parts.push('');
103
- return parts.join('\n');
117
+ return sections.join('\n\n') + '\n';
104
118
  }
@@ -10,6 +10,7 @@
10
10
  *
11
11
  * Dependency tracking uses a global stack (__currentEffect).
12
12
  */
13
+ /** @type {string} */
13
14
  export const reactiveRuntime = `
14
15
  let __currentEffect = null;
15
16