@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/types.js ADDED
@@ -0,0 +1,192 @@
1
+ // v2/lib/types.js — JSDoc type definitions for wcCompiler v2
2
+
3
+ /**
4
+ * @typedef {Object} ReactiveVar
5
+ * @property {string} name — Signal variable name (e.g., 'count')
6
+ * @property {string} value — Initial value expression (e.g., '0', '[1, 2, 3]')
7
+ */
8
+
9
+ /**
10
+ * @typedef {Object} ComputedDef
11
+ * @property {string} name — Computed variable name (e.g., 'doubled')
12
+ * @property {string} body — Computed expression body (e.g., 'count() * 2')
13
+ */
14
+
15
+ /**
16
+ * @typedef {Object} EffectDef
17
+ * @property {string} body — Effect function body
18
+ */
19
+
20
+ /**
21
+ * @typedef {Object} ConstantVar
22
+ * @property {string} name — Constant variable name (e.g., 'TAX_RATE')
23
+ * @property {string} value — Value expression (e.g., '0.21', "'hello'")
24
+ */
25
+
26
+ /**
27
+ * @typedef {Object} LifecycleHook
28
+ * @property {string} body — The callback body (JavaScript code)
29
+ */
30
+
31
+ /**
32
+ * @typedef {Object} MethodDef
33
+ * @property {string} name — Function name (e.g., 'increment')
34
+ * @property {string} params — Parameter list (e.g., '', 'a, b')
35
+ * @property {string} body — Function body
36
+ */
37
+
38
+ /**
39
+ * @typedef {Object} PropDef
40
+ * @property {string} name — camelCase prop name (e.g., 'itemCount')
41
+ * @property {string} default — Default value as source string (e.g., '0', "'Click'", 'undefined')
42
+ * @property {string} attrName — kebab-case attribute name (e.g., 'item-count')
43
+ */
44
+
45
+ /**
46
+ * @typedef {Object} Binding
47
+ * @property {string} varName — Internal name (e.g., '__b0')
48
+ * @property {string} name — Variable name from {{name}}
49
+ * @property {'signal'|'computed'|'method'|'prop'} type — Binding source type
50
+ * @property {string[]} path — DOM path from root (e.g., ['childNodes[0]', 'childNodes[1]'])
51
+ */
52
+
53
+ /**
54
+ * @typedef {Object} EventBinding
55
+ * @property {string} varName — Internal name (e.g., '__e0')
56
+ * @property {string} event — Event name (e.g., 'click')
57
+ * @property {string} handler — Handler function name (e.g., 'increment')
58
+ * @property {string[]} path — DOM path from root
59
+ */
60
+
61
+ /**
62
+ * @typedef {Object} ParseResult
63
+ * @property {string} tagName — Custom element tag (e.g., 'wcc-counter')
64
+ * @property {string} className — PascalCase class name (e.g., 'WccCounter')
65
+ * @property {string} template — Raw HTML template content
66
+ * @property {string} style — Raw CSS content (empty string if none)
67
+ * @property {ReactiveVar[]} signals — signal() declarations
68
+ * @property {ComputedDef[]} computeds — computed() declarations
69
+ * @property {EffectDef[]} effects — effect() declarations
70
+ * @property {ConstantVar[]} constantVars — Plain const declarations (non-reactive)
71
+ * @property {MethodDef[]} methods — function declarations
72
+ * @property {PropDef[]} propDefs — Prop definitions with names and defaults
73
+ * @property {string|null} propsObjectName — Variable name from `const X = defineProps(...)`
74
+ * @property {string[]} emits — Event names declared in defineEmits (empty array if no defineEmits)
75
+ * @property {string|null} emitsObjectName — Variable name from `const X = defineEmits(...)`
76
+ * @property {Binding[]} bindings — (populated by tree-walker)
77
+ * @property {EventBinding[]} events — (populated by tree-walker)
78
+ * @property {string|null} processedTemplate — (populated by tree-walker)
79
+ * @property {IfBlock[]} ifBlocks — Conditional blocks (empty array if none)
80
+ * @property {ShowBinding[]} showBindings — Show bindings (empty array if none)
81
+ * @property {ForBlock[]} forBlocks — For blocks (empty array if none)
82
+ * @property {LifecycleHook[]} onMountHooks — Mount lifecycle hooks (empty array if none)
83
+ * @property {LifecycleHook[]} onDestroyHooks — Destroy lifecycle hooks (empty array if none)
84
+ * @property {ModelBinding[]} modelBindings — Model bindings (empty array if none)
85
+ * @property {AttrBinding[]} attrBindings — Attribute bindings (empty array if none)
86
+ * @property {SlotBinding[]} slots — Slot bindings (empty array if no slots)
87
+ * @property {RefDeclaration[]} refs — templateRef declarations from script (empty array if none)
88
+ * @property {RefBinding[]} refBindings — ref attribute bindings from template (empty array if none)
89
+ */
90
+
91
+ /**
92
+ * @typedef {Object} ShowBinding
93
+ * @property {string} varName
94
+ * @property {string} expression
95
+ * @property {string[]} path
96
+ */
97
+
98
+ /**
99
+ * @typedef {Object} AttrBinding
100
+ * @property {string} varName
101
+ * @property {string} attr
102
+ * @property {string} expression
103
+ * @property {'attr'|'class'|'style'|'bool'} kind
104
+ * @property {string[]} path
105
+ */
106
+
107
+ /**
108
+ * @typedef {Object} IfBranch
109
+ * @property {'if'|'else-if'|'else'} type — Branch type
110
+ * @property {string|null} expression — JS expression (null for else)
111
+ * @property {string} templateHtml — Processed HTML (directive attr removed)
112
+ * @property {Binding[]} bindings — Text interpolation bindings
113
+ * @property {EventBinding[]} events — @event bindings
114
+ * @property {ShowBinding[]} showBindings — show bindings
115
+ * @property {AttrBinding[]} attrBindings — :attr / bind bindings
116
+ * @property {ModelBinding[]} modelBindings — model bindings
117
+ * @property {SlotBinding[]} slots — slot bindings
118
+ */
119
+
120
+ /**
121
+ * @typedef {Object} IfBlock
122
+ * @property {string} varName — Unique name: '__if0', '__if1', ...
123
+ * @property {string[]} anchorPath — DOM path to comment anchor from __root
124
+ * @property {IfBranch[]} branches — Array of branches in chain order
125
+ */
126
+
127
+ /**
128
+ * @typedef {Object} ForBlock
129
+ * @property {string} varName — Unique name: '__for0', '__for1', ...
130
+ * @property {string} itemVar — Iteration variable name (e.g., 'item')
131
+ * @property {string|null} indexVar — Index variable name or null
132
+ * @property {string} source — Source expression (e.g., 'items', '5')
133
+ * @property {string|null} keyExpr — :key expression or null
134
+ * @property {string} templateHtml — Processed item HTML (each/:key attrs removed)
135
+ * @property {string[]} anchorPath — DOM path to comment anchor from __root
136
+ * @property {Binding[]} bindings — Text interpolation bindings within item
137
+ * @property {EventBinding[]} events — @event bindings within item
138
+ * @property {ShowBinding[]} showBindings — show bindings within item
139
+ * @property {AttrBinding[]} attrBindings — :attr bindings within item
140
+ * @property {ModelBinding[]} modelBindings — model bindings within item
141
+ * @property {SlotBinding[]} slots — slot bindings within item
142
+ */
143
+
144
+ /**
145
+ * @typedef {Object} ModelBinding
146
+ * @property {string} varName — Internal name: '__model0', '__model1', ...
147
+ * @property {string} signal — Signal name referenced by model (e.g., 'name', 'count')
148
+ * @property {string} prop — DOM property to bind: 'value' or 'checked'
149
+ * @property {string} event — Event to listen for: 'input' or 'change'
150
+ * @property {boolean} coerce — true if value requires Number() coercion (input type="number")
151
+ * @property {string|null} radioValue — Value attribute for radio inputs, null for others
152
+ * @property {string[]} path — DOM path from root to the element
153
+ */
154
+
155
+ /**
156
+ * @typedef {Object} RefDeclaration
157
+ * @property {string} varName — Variable name from script (e.g., 'canvas')
158
+ * @property {string} refName — Ref name from templateRef argument (e.g., 'canvas')
159
+ */
160
+
161
+ /**
162
+ * @typedef {Object} RefBinding
163
+ * @property {string} refName — Ref name from ref attribute (e.g., 'canvas')
164
+ * @property {string[]} path — DOM path from root to the element (e.g., ['childNodes[0]'])
165
+ */
166
+
167
+ /**
168
+ * @typedef {Object} SlotProp
169
+ * @property {string} prop — Prop name (attribute name without ':'), e.g. 'item'
170
+ * @property {string} source — Source expression (attribute value), e.g. 'currentItem'
171
+ */
172
+
173
+ /**
174
+ * @typedef {Object} SlotBinding
175
+ * @property {string} varName — Internal name (e.g., '__s0')
176
+ * @property {string} name — Slot name (empty string for default slot)
177
+ * @property {string[]} path — DOM path from root to the replacement span
178
+ * @property {string} defaultContent — Fallback content from original <slot> element
179
+ * @property {SlotProp[]} slotProps — Array of :prop="expr" bindings on the slot
180
+ */
181
+
182
+ /**
183
+ * Set of HTML attributes that use property assignment instead of setAttribute.
184
+ * @type {Set<string>}
185
+ */
186
+ export const BOOLEAN_ATTRIBUTES = new Set([
187
+ 'disabled', 'checked', 'hidden', 'readonly', 'required',
188
+ 'selected', 'multiple', 'autofocus', 'autoplay', 'controls',
189
+ 'loop', 'muted', 'open', 'novalidate'
190
+ ]);
191
+
192
+ export {}
@@ -7,6 +7,12 @@ const state = {};
7
7
  const listeners = [];
8
8
  const handlers = {};
9
9
 
10
+ /**
11
+ * Initialize the runtime with an initial state and bind :prop and @event attributes.
12
+ *
13
+ * @param {Record<string, any>} initialState
14
+ * @returns {void}
15
+ */
10
16
  export function init(initialState) {
11
17
  Object.assign(state, initialState);
12
18
  document.querySelectorAll('*').forEach(el => {
@@ -28,15 +34,35 @@ export function init(initialState) {
28
34
  });
29
35
  }
30
36
 
37
+ /**
38
+ * Set a state value and notify all listeners bound to that key.
39
+ *
40
+ * @param {string} key
41
+ * @param {any} value
42
+ * @returns {void}
43
+ */
31
44
  export function set(key, value) {
32
45
  state[key] = value;
33
46
  listeners.filter(l => l.key === key).forEach(l => l.update(state[key]));
34
47
  }
35
48
 
49
+ /**
50
+ * Get the current value for a state key.
51
+ *
52
+ * @param {string} key
53
+ * @returns {any}
54
+ */
36
55
  export function get(key) {
37
56
  return state[key];
38
57
  }
39
58
 
59
+ /**
60
+ * Register a named event handler.
61
+ *
62
+ * @param {string} name
63
+ * @param {Function} fn
64
+ * @returns {void}
65
+ */
40
66
  export function on(name, fn) {
41
67
  handlers[name] = fn;
42
68
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sprlab/wccompiler",
3
- "version": "0.0.2",
4
- "description": "Zero-runtime compiler that transforms .html files with Vue-like syntax into 100% native web components",
3
+ "version": "0.2.0",
4
+ "description": "Zero-runtime compiler that transforms .ts/.js component files into native web components with signals-based reactivity",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "wcc": "./bin/wcc.js"
@@ -10,33 +10,38 @@
10
10
  "bin/",
11
11
  "lib/*.js",
12
12
  "!lib/*.test.js",
13
+ "types/",
13
14
  "README.md"
14
15
  ],
16
+ "types": "./types/wcc.d.ts",
15
17
  "scripts": {
16
- "test": "vitest --run"
18
+ "test": "vitest --run",
19
+ "typecheck": "tsc --project jsconfig.json --noEmit"
17
20
  },
18
21
  "keywords": [
19
22
  "web-components",
20
23
  "compiler",
21
24
  "custom-elements",
22
25
  "zero-runtime",
23
- "native",
24
- "vue-like"
26
+ "signals",
27
+ "native"
25
28
  ],
26
29
  "license": "MIT",
27
- "repository": {
28
- "type": "git",
29
- "url": ""
30
+ "publishConfig": {
31
+ "access": "public"
30
32
  },
31
33
  "dependencies": {
34
+ "esbuild": "^0.27.0",
32
35
  "jsdom": "^29.0.2"
33
36
  },
34
37
  "devDependencies": {
38
+ "@types/jsdom": "^28.0.1",
35
39
  "fast-check": "^4.1.1",
40
+ "typescript": "^6.0.3",
36
41
  "vitest": "^3.2.1"
37
42
  },
38
43
  "volta": {
39
44
  "node": "24.0.0",
40
45
  "yarn": "4.12.0"
41
46
  }
42
- }
47
+ }
package/types/wcc.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ declare module 'wcc' {
2
+ interface Signal<T> {
3
+ (): T;
4
+ set(value: T): void;
5
+ }
6
+
7
+ export function signal<T>(value: T): Signal<T>;
8
+ export function computed<T>(fn: () => T): () => T;
9
+ export function effect(fn: () => void): void;
10
+ export function defineComponent(options: {
11
+ tag: string;
12
+ template: string;
13
+ styles?: string;
14
+ }): void;
15
+
16
+ export function defineProps<T extends Record<string, any>>(defaults?: Partial<T>): T;
17
+ export function defineProps(names: string[]): Record<string, any>;
18
+
19
+ export function defineEmits<T>(): T;
20
+ export function defineEmits(names: string[]): (name: string, detail?: any) => void;
21
+
22
+ export function templateRef(name: string): { value: HTMLElement | null };
23
+
24
+ export function onMount(fn: () => void): void;
25
+ export function onDestroy(fn: () => void): void;
26
+ export function templateBindings(bindings: Record<string, any>): void;
27
+ }
@@ -0,0 +1,46 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { readFileSync, existsSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
7
+ const dtsPath = resolve(__dirname, 'wcc.d.ts');
8
+
9
+ describe('type declarations (wcc.d.ts)', () => {
10
+ it('file exists', () => {
11
+ expect(existsSync(dtsPath)).toBe(true);
12
+ });
13
+
14
+ it('contains signal declaration', () => {
15
+ const content = readFileSync(dtsPath, 'utf-8');
16
+ expect(content).toContain('function signal<T>(value: T): Signal<T>');
17
+ });
18
+
19
+ it('contains computed declaration', () => {
20
+ const content = readFileSync(dtsPath, 'utf-8');
21
+ expect(content).toContain('function computed<T>(fn: () => T): () => T');
22
+ });
23
+
24
+ it('contains effect declaration', () => {
25
+ const content = readFileSync(dtsPath, 'utf-8');
26
+ expect(content).toContain('function effect(fn: () => void): void');
27
+ });
28
+
29
+ it('contains defineComponent declaration', () => {
30
+ const content = readFileSync(dtsPath, 'utf-8');
31
+ expect(content).toContain('function defineComponent');
32
+ expect(content).toContain('tag: string');
33
+ expect(content).toContain('template: string');
34
+ expect(content).toContain('styles?: string');
35
+ });
36
+
37
+ it('Signal<T> interface has call signature (): T', () => {
38
+ const content = readFileSync(dtsPath, 'utf-8');
39
+ expect(content).toContain('(): T');
40
+ });
41
+
42
+ it('Signal<T> interface has set(value: T): void', () => {
43
+ const content = readFileSync(dtsPath, 'utf-8');
44
+ expect(content).toContain('set(value: T): void');
45
+ });
46
+ });