@sprlab/wccompiler 0.4.2 → 0.5.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/bin/wcc.js +13 -3
- package/lib/codegen.js +8 -3
- package/lib/compiler.js +5 -5
- package/lib/reactive-runtime.js +5 -8
- package/lib/tree-walker.js +3 -3
- package/package.json +3 -3
package/bin/wcc.js
CHANGED
|
@@ -15,16 +15,28 @@ async function build(config, cwd) {
|
|
|
15
15
|
|
|
16
16
|
if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });
|
|
17
17
|
|
|
18
|
+
// Generate shared reactive runtime
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = dirname(__filename);
|
|
21
|
+
const { reactiveRuntime } = await import('../lib/reactive-runtime.js');
|
|
22
|
+
const signalsContent = reactiveRuntime.trim().replace(/^/gm, '') + '\nexport { __signal, __computed, __effect, __batch };\n';
|
|
23
|
+
const signalsDest = join(outputDir, '__wcc-signals.js');
|
|
24
|
+
writeFileSync(signalsDest, signalsContent);
|
|
25
|
+
|
|
18
26
|
// Discover source files
|
|
19
27
|
const files = discoverFiles(inputDir);
|
|
20
28
|
let errors = 0;
|
|
21
29
|
|
|
22
30
|
for (const file of files) {
|
|
23
31
|
try {
|
|
24
|
-
|
|
32
|
+
// Calculate relative path from component output to __wcc-signals.js
|
|
25
33
|
const relPath = relative(inputDir, file);
|
|
26
34
|
const outPath = resolve(outputDir, relPath.replace(/\.wcc$/, '.js'));
|
|
27
35
|
const outDir = dirname(outPath);
|
|
36
|
+
const runtimeRelPath = relative(outDir, signalsDest).replace(/\\/g, '/');
|
|
37
|
+
const runtimeImportPath = runtimeRelPath.startsWith('.') ? runtimeRelPath : './' + runtimeRelPath;
|
|
38
|
+
|
|
39
|
+
const output = await compile(file, { runtimeImportPath });
|
|
28
40
|
if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
|
|
29
41
|
writeFileSync(outPath, output);
|
|
30
42
|
} catch (err) {
|
|
@@ -34,8 +46,6 @@ async function build(config, cwd) {
|
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
// Copy wcc-runtime.js to output directory
|
|
37
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
38
|
-
const __dirname = dirname(__filename);
|
|
39
49
|
const runtimeSrc = resolve(__dirname, '../lib/wcc-runtime.js');
|
|
40
50
|
const runtimeDest = join(outputDir, 'wcc-runtime.js');
|
|
41
51
|
copyFileSync(runtimeSrc, runtimeDest);
|
package/lib/codegen.js
CHANGED
|
@@ -485,9 +485,10 @@ function generateItemSetup(lines, forBlock, itemVar, indexVar, propNames, signal
|
|
|
485
485
|
* Generate a fully self-contained JS component from a ParseResult.
|
|
486
486
|
*
|
|
487
487
|
* @param {ParseResult} parseResult — Complete IR with bindings/events
|
|
488
|
+
* @param {{ runtimeImportPath?: string }} [options] — Optional generation options
|
|
488
489
|
* @returns {string} JavaScript source code
|
|
489
490
|
*/
|
|
490
|
-
export function generateComponent(parseResult) {
|
|
491
|
+
export function generateComponent(parseResult, options = {}) {
|
|
491
492
|
const {
|
|
492
493
|
tagName,
|
|
493
494
|
className,
|
|
@@ -529,8 +530,12 @@ export function generateComponent(parseResult) {
|
|
|
529
530
|
|
|
530
531
|
const lines = [];
|
|
531
532
|
|
|
532
|
-
// ── 1.
|
|
533
|
-
|
|
533
|
+
// ── 1. Reactive runtime (shared import or inline) ──
|
|
534
|
+
if (options.runtimeImportPath) {
|
|
535
|
+
lines.push(`import { __signal, __computed, __effect, __batch } from '${options.runtimeImportPath}';`);
|
|
536
|
+
} else {
|
|
537
|
+
lines.push(reactiveRuntime.trim());
|
|
538
|
+
}
|
|
534
539
|
lines.push('');
|
|
535
540
|
|
|
536
541
|
// ── 1b. Child component imports ──
|
package/lib/compiler.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Takes a .wcc file path and produces a self-contained JavaScript web component string.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { parseHTML } from 'linkedom';
|
|
10
10
|
import { readFileSync, existsSync } from 'node:fs';
|
|
11
11
|
import { dirname, extname, basename, resolve } from 'node:path';
|
|
12
12
|
import { walkTree, processIfChains, processForBlocks, recomputeAnchorPath, detectRefs } from './tree-walker.js';
|
|
@@ -223,9 +223,9 @@ async function compileSFC(filePath, config) {
|
|
|
223
223
|
exposeNames,
|
|
224
224
|
};
|
|
225
225
|
|
|
226
|
-
// 16. Process template through
|
|
227
|
-
const
|
|
228
|
-
const rootEl =
|
|
226
|
+
// 16. Process template through linkedom → tree-walker → codegen
|
|
227
|
+
const { document } = parseHTML(`<div id="__root">${template}</div>`);
|
|
228
|
+
const rootEl = document.getElementById('__root');
|
|
229
229
|
|
|
230
230
|
const signalNames = new Set(signals.map(s => s.name));
|
|
231
231
|
const computedNames = new Set(computeds.map(c => c.name));
|
|
@@ -323,7 +323,7 @@ async function compileSFC(filePath, config) {
|
|
|
323
323
|
parseResult.processedTemplate = rootEl.innerHTML;
|
|
324
324
|
|
|
325
325
|
// 20. Generate component
|
|
326
|
-
return generateComponent(parseResult);
|
|
326
|
+
return generateComponent(parseResult, config);
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
/**
|
package/lib/reactive-runtime.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
export const reactiveRuntime = `
|
|
17
17
|
let __currentEffect = null;
|
|
18
18
|
let __batchDepth = 0;
|
|
19
|
-
const __pendingEffects =
|
|
19
|
+
const __pendingEffects = new Set();
|
|
20
20
|
|
|
21
21
|
function __signal(initial) {
|
|
22
22
|
let _value = initial;
|
|
@@ -30,9 +30,7 @@ function __signal(initial) {
|
|
|
30
30
|
_value = args[0];
|
|
31
31
|
if (old !== _value) {
|
|
32
32
|
if (__batchDepth > 0) {
|
|
33
|
-
for (const fn of _subs)
|
|
34
|
-
if (!__pendingEffects.includes(fn)) __pendingEffects.push(fn);
|
|
35
|
-
}
|
|
33
|
+
for (const fn of _subs) __pendingEffects.add(fn);
|
|
36
34
|
} else {
|
|
37
35
|
for (const fn of [..._subs]) fn();
|
|
38
36
|
}
|
|
@@ -46,9 +44,7 @@ function __computed(fn) {
|
|
|
46
44
|
const recompute = () => {
|
|
47
45
|
_dirty = true;
|
|
48
46
|
if (__batchDepth > 0) {
|
|
49
|
-
for (const fn of _subs)
|
|
50
|
-
if (!__pendingEffects.includes(fn)) __pendingEffects.push(fn);
|
|
51
|
-
}
|
|
47
|
+
for (const fn of _subs) __pendingEffects.add(fn);
|
|
52
48
|
} else {
|
|
53
49
|
for (const fn of [..._subs]) fn();
|
|
54
50
|
}
|
|
@@ -85,7 +81,8 @@ function __batch(fn) {
|
|
|
85
81
|
} finally {
|
|
86
82
|
__batchDepth--;
|
|
87
83
|
if (__batchDepth === 0) {
|
|
88
|
-
const pending = __pendingEffects
|
|
84
|
+
const pending = [...__pendingEffects];
|
|
85
|
+
__pendingEffects.clear();
|
|
89
86
|
for (const f of pending) f();
|
|
90
87
|
}
|
|
91
88
|
}
|
package/lib/tree-walker.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* extracts branch templates, and replaces chains with comment anchors.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { parseHTML } from 'linkedom';
|
|
16
16
|
import { BOOLEAN_ATTRIBUTES } from './types.js';
|
|
17
17
|
|
|
18
18
|
/** @import { Binding, EventBinding, IfBlock, IfBranch, ShowBinding, AttrBinding, ForBlock, ModelBinding, SlotBinding, SlotProp, RefBinding, ChildComponentBinding, ChildPropBinding } from './types.js' */
|
|
@@ -358,8 +358,8 @@ function isChainPredecessor(el) {
|
|
|
358
358
|
* @returns {{ bindings: Binding[], events: EventBinding[], showBindings: ShowBinding[], attrBindings: AttrBinding[], modelBindings: ModelBinding[], slots: SlotBinding[], processedHtml: string }}
|
|
359
359
|
*/
|
|
360
360
|
export function walkBranch(html, signalNames, computedNames, propNames) {
|
|
361
|
-
const
|
|
362
|
-
const branchRoot =
|
|
361
|
+
const { document } = parseHTML(`<div id="__branchRoot">${html}</div>`);
|
|
362
|
+
const branchRoot = document.getElementById('__branchRoot');
|
|
363
363
|
|
|
364
364
|
// Use walkTree on the branch root to discover bindings/events
|
|
365
365
|
const result = walkTree(branchRoot, signalNames, computedNames, propNames);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sprlab/wccompiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Zero-runtime compiler that transforms .wcc single-file components into native web components with signals-based reactivity",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"esbuild": "^0.27.0",
|
|
35
|
-
"
|
|
35
|
+
"linkedom": "^0.18.12"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@types/jsdom": "^28.0.1",
|
|
39
38
|
"fast-check": "^4.1.1",
|
|
39
|
+
"jsdom": "^29.1.1",
|
|
40
40
|
"typescript": "^6.0.3",
|
|
41
41
|
"vitest": "^3.2.1"
|
|
42
42
|
},
|