@sprlab/wccompiler 0.4.0 → 0.4.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/bin/wcc.js +2 -1
- package/lib/codegen.js +19 -2
- package/lib/compiler.js +3 -0
- package/lib/dev-server.js +30 -1
- package/lib/parser-extractors.js +23 -0
- package/lib/tree-walker.js +7 -8
- package/lib/types.js +1 -0
- package/package.json +1 -1
- package/types/wcc.d.ts +1 -1
package/bin/wcc.js
CHANGED
|
@@ -67,7 +67,7 @@ async function main() {
|
|
|
67
67
|
} else if (command === 'dev') {
|
|
68
68
|
await build(config, cwd);
|
|
69
69
|
const outputDir = resolve(cwd, config.output);
|
|
70
|
-
startDevServer({ port: config.port, root: cwd, outputDir });
|
|
70
|
+
const devServer = startDevServer({ port: config.port, root: cwd, outputDir });
|
|
71
71
|
const inputDir = resolve(cwd, config.input);
|
|
72
72
|
console.log(`Watching ${inputDir} for changes...`);
|
|
73
73
|
watch(inputDir, { recursive: true }, async (eventType, filename) => {
|
|
@@ -85,6 +85,7 @@ async function main() {
|
|
|
85
85
|
console.log(`Compiled: ${filename}`);
|
|
86
86
|
} catch (err) {
|
|
87
87
|
console.error(`Error compiling ${filename}: ${err.message}`);
|
|
88
|
+
devServer.notifyError(`${filename}\n\n${err.message}`);
|
|
88
89
|
}
|
|
89
90
|
});
|
|
90
91
|
} else {
|
package/lib/codegen.js
CHANGED
|
@@ -517,6 +517,7 @@ export function generateComponent(parseResult) {
|
|
|
517
517
|
refBindings = [],
|
|
518
518
|
childComponents = [],
|
|
519
519
|
childImports = [],
|
|
520
|
+
exposeNames = [],
|
|
520
521
|
} = parseResult;
|
|
521
522
|
|
|
522
523
|
const signalNames = signals.map(s => s.name);
|
|
@@ -614,9 +615,11 @@ export function generateComponent(parseResult) {
|
|
|
614
615
|
lines.push(` this.${s.varName} = ${pathExpr(s.path, '__root')};`);
|
|
615
616
|
}
|
|
616
617
|
|
|
617
|
-
// Assign DOM refs for child component instances
|
|
618
|
+
// Assign DOM refs for child component instances (only if they have prop bindings)
|
|
618
619
|
for (const cc of childComponents) {
|
|
619
|
-
|
|
620
|
+
if (cc.propBindings.length > 0) {
|
|
621
|
+
lines.push(` this.${cc.varName} = ${pathExpr(cc.path, '__root')};`);
|
|
622
|
+
}
|
|
620
623
|
}
|
|
621
624
|
|
|
622
625
|
// Assign DOM refs for attr bindings (reuse ref when same path)
|
|
@@ -1157,6 +1160,20 @@ export function generateComponent(parseResult) {
|
|
|
1157
1160
|
}
|
|
1158
1161
|
}
|
|
1159
1162
|
|
|
1163
|
+
// ── defineExpose: public getters/methods ──
|
|
1164
|
+
for (const name of exposeNames) {
|
|
1165
|
+
if (computedNames.includes(name)) {
|
|
1166
|
+
lines.push(` get ${name}() { return this._c_${name}(); }`);
|
|
1167
|
+
} else if (signalNames.includes(name)) {
|
|
1168
|
+
lines.push(` get ${name}() { return this._${name}(); }`);
|
|
1169
|
+
} else if (methodNames.includes(name)) {
|
|
1170
|
+
lines.push(` ${name}(...args) { return this._${name}(...args); }`);
|
|
1171
|
+
} else if (constantNames.includes(name)) {
|
|
1172
|
+
lines.push(` get ${name}() { return this._const_${name}; }`);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
if (exposeNames.length > 0) lines.push('');
|
|
1176
|
+
|
|
1160
1177
|
// ── if setup methods ──
|
|
1161
1178
|
for (const ifBlock of ifBlocks) {
|
|
1162
1179
|
const vn = ifBlock.varName;
|
package/lib/compiler.js
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
extractLifecycleHooks,
|
|
33
33
|
extractRefs,
|
|
34
34
|
extractConstants,
|
|
35
|
+
extractExpose,
|
|
35
36
|
validatePropsAssignment,
|
|
36
37
|
validateDuplicateProps,
|
|
37
38
|
validatePropsConflicts,
|
|
@@ -147,6 +148,7 @@ async function compileSFC(filePath, config) {
|
|
|
147
148
|
const methods = extractFunctions(sourceForExtraction);
|
|
148
149
|
const refs = extractRefs(sourceForExtraction);
|
|
149
150
|
const constantVars = extractConstants(sourceForExtraction);
|
|
151
|
+
const exposeNames = extractExpose(source);
|
|
150
152
|
|
|
151
153
|
// 9. Extract props (array form — after type strip, if generic didn't find any)
|
|
152
154
|
const propsFromArray = propsFromGeneric.length > 0 ? [] : extractPropsArray(source);
|
|
@@ -218,6 +220,7 @@ async function compileSFC(filePath, config) {
|
|
|
218
220
|
refBindings: [],
|
|
219
221
|
childComponents: [],
|
|
220
222
|
childImports: [],
|
|
223
|
+
exposeNames,
|
|
221
224
|
};
|
|
222
225
|
|
|
223
226
|
// 16. Process template through jsdom → tree-walker → codegen (same as compile())
|
package/lib/dev-server.js
CHANGED
|
@@ -36,8 +36,25 @@ const MIME_TYPES = {
|
|
|
36
36
|
const SSE_SNIPPET = `<script>
|
|
37
37
|
(function() {
|
|
38
38
|
var es = new EventSource('/__sse');
|
|
39
|
+
var overlay = null;
|
|
40
|
+
function showError(msg) {
|
|
41
|
+
hideError();
|
|
42
|
+
overlay = document.createElement('div');
|
|
43
|
+
overlay.id = '__wcc_error_overlay';
|
|
44
|
+
overlay.style.cssText = 'position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,0.85);color:#fff;font-family:monospace;font-size:14px;padding:32px;overflow:auto;display:flex;align-items:flex-start;justify-content:center;';
|
|
45
|
+
var box = document.createElement('div');
|
|
46
|
+
box.style.cssText = 'background:#1e1e1e;border:2px solid #f44;border-radius:8px;padding:24px;max-width:700px;width:100%;white-space:pre-wrap;word-break:break-word;';
|
|
47
|
+
box.innerHTML = '<div style="color:#f44;font-size:16px;font-weight:bold;margin-bottom:12px;">\\u274C Compilation Error</div>' + msg.replace(/</g,'<').replace(/>/g,'>');
|
|
48
|
+
overlay.appendChild(box);
|
|
49
|
+
overlay.addEventListener('click', hideError);
|
|
50
|
+
document.body.appendChild(overlay);
|
|
51
|
+
}
|
|
52
|
+
function hideError() {
|
|
53
|
+
if (overlay) { overlay.remove(); overlay = null; }
|
|
54
|
+
}
|
|
39
55
|
es.onmessage = function(e) {
|
|
40
|
-
if (e.data === 'reload') location.reload();
|
|
56
|
+
if (e.data === 'reload') { hideError(); location.reload(); }
|
|
57
|
+
else if (e.data.startsWith('error:')) { showError(e.data.slice(6).replace(/\\\\n/g,'\\n')); }
|
|
41
58
|
};
|
|
42
59
|
es.onerror = function() {
|
|
43
60
|
es.close();
|
|
@@ -70,6 +87,17 @@ export function startDevServer({ port, root, outputDir }) {
|
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
|
|
90
|
+
/** Send an error event to all connected SSE clients */
|
|
91
|
+
function notifyError(message) {
|
|
92
|
+
for (const res of sseClients) {
|
|
93
|
+
try {
|
|
94
|
+
res.write(`data: error:${message.replace(/\n/g, '\\n')}\n\n`);
|
|
95
|
+
} catch {
|
|
96
|
+
sseClients.delete(res);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
73
101
|
const server = createServer((req, res) => {
|
|
74
102
|
const url = req.url.split('?')[0];
|
|
75
103
|
|
|
@@ -151,6 +179,7 @@ export function startDevServer({ port, root, outputDir }) {
|
|
|
151
179
|
|
|
152
180
|
return {
|
|
153
181
|
server,
|
|
182
|
+
notifyError,
|
|
154
183
|
close() {
|
|
155
184
|
// Close all SSE connections
|
|
156
185
|
for (const res of sseClients) {
|
package/lib/parser-extractors.js
CHANGED
|
@@ -1027,3 +1027,26 @@ export function extractRefs(source) {
|
|
|
1027
1027
|
}
|
|
1028
1028
|
return refs;
|
|
1029
1029
|
}
|
|
1030
|
+
|
|
1031
|
+
// ── defineExpose extraction ─────────────────────────────────────────
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* Extract property names from defineExpose({ key1, key2, ... }).
|
|
1035
|
+
* Supports shorthand properties: defineExpose({ doubled, handleUpdate })
|
|
1036
|
+
*
|
|
1037
|
+
* @param {string} source — Source code (after type stripping)
|
|
1038
|
+
* @returns {string[]} Array of exposed property names
|
|
1039
|
+
*/
|
|
1040
|
+
export function extractExpose(source) {
|
|
1041
|
+
const m = source.match(/defineExpose\(\s*\{([^}]*)\}\s*\)/);
|
|
1042
|
+
if (!m) return [];
|
|
1043
|
+
|
|
1044
|
+
const body = m[1];
|
|
1045
|
+
const names = [];
|
|
1046
|
+
const re = /\b(\w+)\b/g;
|
|
1047
|
+
let match;
|
|
1048
|
+
while ((match = re.exec(body)) !== null) {
|
|
1049
|
+
names.push(match[1]);
|
|
1050
|
+
}
|
|
1051
|
+
return names;
|
|
1052
|
+
}
|
package/lib/tree-walker.js
CHANGED
|
@@ -131,14 +131,13 @@ export function walkTree(rootEl, signalNames, computedNames, propNames = new Set
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
134
|
+
// Always register child component for auto-import (even without prop bindings)
|
|
135
|
+
childComponents.push({
|
|
136
|
+
tag: tagLower,
|
|
137
|
+
varName: `__child${childIdx++}`,
|
|
138
|
+
path: [...pathParts],
|
|
139
|
+
propBindings,
|
|
140
|
+
});
|
|
142
141
|
}
|
|
143
142
|
|
|
144
143
|
// Check for @event attributes
|
package/lib/types.js
CHANGED
|
@@ -99,6 +99,7 @@
|
|
|
99
99
|
* @property {RefBinding[]} refBindings — ref attribute bindings from template (empty array if none)
|
|
100
100
|
* @property {ChildComponentBinding[]} childComponents — Child component bindings (empty array if none)
|
|
101
101
|
* @property {ChildComponentImport[]} childImports — Resolved child component imports (empty array if none)
|
|
102
|
+
* @property {string[]} exposeNames — Property names from defineExpose (empty array if none)
|
|
102
103
|
*/
|
|
103
104
|
|
|
104
105
|
/**
|
package/package.json
CHANGED
package/types/wcc.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ declare module 'wcc' {
|
|
|
19
19
|
export function defineEmits<T>(): T;
|
|
20
20
|
export function defineEmits(names: string[]): (name: string, detail?: any) => void;
|
|
21
21
|
|
|
22
|
-
export function templateRef(name: string): { value: HTMLElement | null };
|
|
22
|
+
export function templateRef<T = HTMLElement>(name: string): { value: (T & HTMLElement) | null };
|
|
23
23
|
|
|
24
24
|
export function onMount(fn: () => void | Promise<void>): void;
|
|
25
25
|
export function onDestroy(fn: () => void | Promise<void>): void;
|