@macroforge/svelte-preprocessor 0.1.79 → 0.1.83
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/README.md +10 -43
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -49
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,37 +1,10 @@
|
|
|
1
1
|
# @macroforge/svelte-preprocessor
|
|
2
2
|
|
|
3
|
-
Svelte preprocessor for expanding Macroforge macros in component script blocks
|
|
4
|
-
|
|
5
3
|
[](https://www.npmjs.com/package/@macroforge/svelte-preprocessor)
|
|
6
4
|
|
|
7
5
|
## Overview
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Svelte preprocessor for expanding Macroforge macros in component script blocks.
|
|
12
|
-
|
|
13
|
-
This module provides integration between Macroforge's macro expansion system and
|
|
14
|
-
Svelte's preprocessing pipeline. It intercepts `<script>` blocks in `.svelte`
|
|
15
|
-
files, detects `@derive` decorators, and expands them into generated code before
|
|
16
|
-
TypeScript compilation occurs.
|
|
17
|
-
|
|
18
|
-
## How It Works
|
|
19
|
-
|
|
20
|
-
1. The preprocessor is registered in `svelte.config.js` as part of the
|
|
21
|
-
preprocess array
|
|
22
|
-
2. When Svelte compiles a component, it passes each `<script>` block to this
|
|
23
|
-
preprocessor
|
|
24
|
-
3. The preprocessor checks if the script contains `@derive` decorators
|
|
25
|
-
4. If found, it calls the native `macroforge` binding to expand the macros
|
|
26
|
-
5. The expanded code replaces the original script content
|
|
27
|
-
|
|
28
|
-
## Important Notes
|
|
29
|
-
|
|
30
|
-
- Must be placed BEFORE other preprocessors (like `vitePreprocess()`) in the
|
|
31
|
-
chain
|
|
32
|
-
- Uses lazy-loading for native bindings to avoid initialization overhead
|
|
33
|
-
- Gracefully degrades if native bindings are unavailable
|
|
34
|
-
- Only processes TypeScript blocks by default (configurable via options)
|
|
7
|
+
Svelte preprocessor for expanding Macroforge macros in component script blocks
|
|
35
8
|
|
|
36
9
|
## Installation
|
|
37
10
|
|
|
@@ -43,29 +16,23 @@ npm install @macroforge/svelte-preprocessor
|
|
|
43
16
|
|
|
44
17
|
### Functions
|
|
45
18
|
|
|
46
|
-
- **`macroforgePreprocess`** -
|
|
47
|
-
|
|
19
|
+
- **`macroforgePreprocess`** - Creates a Svelte preprocessor that expands Macroforge macros in
|
|
20
|
+
`<script>` blocks.
|
|
48
21
|
|
|
49
|
-
###
|
|
22
|
+
### Interfaces
|
|
50
23
|
|
|
51
|
-
- **`
|
|
52
|
-
|
|
53
|
-
- **`MacroforgePreprocessorOptions`** - Configuration options for the Macroforge
|
|
54
|
-
Svelte preprocessor.
|
|
24
|
+
- **`MacroforgePreprocessorOptions`** - Configuration options for the Macroforge Svelte
|
|
25
|
+
preprocessor.
|
|
55
26
|
|
|
56
|
-
|
|
27
|
+
### Constants
|
|
57
28
|
|
|
58
|
-
|
|
59
|
-
macroforgePreprocess();
|
|
60
|
-
macroforgePreprocess({ keepDecorators: true });
|
|
61
|
-
macroforgePreprocess({ processJavaScript: true });
|
|
62
|
-
```
|
|
29
|
+
- **`default`** - Default export for convenient importing.
|
|
63
30
|
|
|
64
31
|
## Documentation
|
|
65
32
|
|
|
66
33
|
See the
|
|
67
|
-
[full documentation](https://macroforge.dev/docs/api/reference/typescript/svelte-preprocessor)
|
|
68
|
-
|
|
34
|
+
[full documentation](https://macroforge.dev/docs/api/reference/typescript/svelte-preprocessor) on
|
|
35
|
+
the Macroforge website.
|
|
69
36
|
|
|
70
37
|
## License
|
|
71
38
|
|
package/dist/index.d.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*
|
|
26
26
|
* @packageDocumentation
|
|
27
27
|
*/
|
|
28
|
-
import type { PreprocessorGroup } from
|
|
28
|
+
import type { PreprocessorGroup } from 'svelte/compiler';
|
|
29
29
|
/**
|
|
30
30
|
* Configuration options for the Macroforge Svelte preprocessor.
|
|
31
31
|
*
|
|
@@ -92,15 +92,18 @@ export interface MacroforgePreprocessorOptions {
|
|
|
92
92
|
* The preprocessor performs these steps for each script block:
|
|
93
93
|
*
|
|
94
94
|
* 1. **Language Check** - Verifies the script is TypeScript (or JavaScript if enabled)
|
|
95
|
-
* 2. **
|
|
96
|
-
* 3. **
|
|
97
|
-
* 4. **
|
|
98
|
-
*
|
|
95
|
+
* 2. **Expansion** - Calls the native engine to expand macros
|
|
96
|
+
* 3. **Diagnostics** - Logs any errors or warnings from expansion
|
|
97
|
+
* 4. **Return** - Returns transformed code or `undefined` if unchanged
|
|
98
|
+
*
|
|
99
|
+
* Every matching script block is passed to the engine; there is no
|
|
100
|
+
* pre-scan for `@derive`, so unchanged blocks simply come back identical.
|
|
99
101
|
*
|
|
100
102
|
* ## Error Handling
|
|
101
103
|
*
|
|
102
104
|
* The preprocessor is designed to be resilient:
|
|
103
|
-
* - If native bindings fail to load, it
|
|
105
|
+
* - If native bindings fail to load, it logs a warning, skips the block, and
|
|
106
|
+
* retries the import on the next block (so a warning is emitted per attempt)
|
|
104
107
|
* - If macro expansion throws, it logs a warning and continues
|
|
105
108
|
* - Svelte compilation proceeds even if preprocessing fails
|
|
106
109
|
*
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AA+OvE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,6BAA6B;IAC1C;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,wBAAgB,oBAAoB,CAChC,OAAO,GAAE,6BAAkC,GAC5C,iBAAiB,CA+InB;AAED;;;;;;;GAOG;AACH,eAAe,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -25,14 +25,13 @@
|
|
|
25
25
|
*
|
|
26
26
|
* @packageDocumentation
|
|
27
27
|
*/
|
|
28
|
-
import { hasMacroAnnotations } from "@macroforge/shared";
|
|
29
28
|
// ============================================================================
|
|
30
29
|
// Source Map Generation
|
|
31
30
|
// ============================================================================
|
|
32
|
-
const VLQ_CHARS =
|
|
31
|
+
const VLQ_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
33
32
|
function vlqEncode(value) {
|
|
34
33
|
let vlq = value < 0 ? (-value << 1) | 1 : value << 1;
|
|
35
|
-
let encoded =
|
|
34
|
+
let encoded = '';
|
|
36
35
|
do {
|
|
37
36
|
let digit = vlq & 0x1f;
|
|
38
37
|
vlq >>>= 5;
|
|
@@ -46,7 +45,7 @@ function vlqEncode(value) {
|
|
|
46
45
|
function buildLineOffsets(text) {
|
|
47
46
|
const offsets = [0];
|
|
48
47
|
for (let i = 0; i < text.length; i++) {
|
|
49
|
-
if (text[i] ===
|
|
48
|
+
if (text[i] === '\n')
|
|
50
49
|
offsets.push(i + 1);
|
|
51
50
|
}
|
|
52
51
|
return offsets;
|
|
@@ -80,7 +79,7 @@ function buildSourceMap(original, expanded, mapping, filename) {
|
|
|
80
79
|
lineSegments.get(exp.line).push({
|
|
81
80
|
expCol: exp.column,
|
|
82
81
|
origLine: orig.line,
|
|
83
|
-
origCol: orig.column
|
|
82
|
+
origCol: orig.column
|
|
84
83
|
});
|
|
85
84
|
}
|
|
86
85
|
// Encode VLQ mappings
|
|
@@ -90,7 +89,7 @@ function buildSourceMap(original, expanded, mapping, filename) {
|
|
|
90
89
|
for (let line = 0; line < totalLines; line++) {
|
|
91
90
|
const segs = lineSegments.get(line);
|
|
92
91
|
if (!segs || segs.length === 0) {
|
|
93
|
-
mappingsArr.push(
|
|
92
|
+
mappingsArr.push('');
|
|
94
93
|
continue;
|
|
95
94
|
}
|
|
96
95
|
segs.sort((a, b) => a.expCol - b.expCol);
|
|
@@ -105,14 +104,14 @@ function buildSourceMap(original, expanded, mapping, filename) {
|
|
|
105
104
|
prevOrigLine = seg.origLine;
|
|
106
105
|
prevOrigCol = seg.origCol;
|
|
107
106
|
}
|
|
108
|
-
mappingsArr.push(parts.join(
|
|
107
|
+
mappingsArr.push(parts.join(','));
|
|
109
108
|
}
|
|
110
109
|
return {
|
|
111
110
|
version: 3,
|
|
112
111
|
sources: [filename],
|
|
113
112
|
sourcesContent: [original],
|
|
114
113
|
names: [],
|
|
115
|
-
mappings: mappingsArr.join(
|
|
114
|
+
mappings: mappingsArr.join(';')
|
|
116
115
|
};
|
|
117
116
|
}
|
|
118
117
|
/**
|
|
@@ -121,7 +120,7 @@ function buildSourceMap(original, expanded, mapping, filename) {
|
|
|
121
120
|
* This variable implements a lazy-loading pattern:
|
|
122
121
|
* - Initially `null`, indicating the binding hasn't been loaded yet
|
|
123
122
|
* - Set to the actual function after first successful load
|
|
124
|
-
* - Remains `null` if loading fails
|
|
123
|
+
* - Remains `null` if loading fails, so the next call retries the import
|
|
125
124
|
*
|
|
126
125
|
* The lazy-loading approach avoids loading native bindings at module import time,
|
|
127
126
|
* which improves startup performance when the preprocessor is registered but no
|
|
@@ -137,17 +136,20 @@ let expandSync = null;
|
|
|
137
136
|
*
|
|
138
137
|
* 1. On first call, dynamically imports the `macroforge` package
|
|
139
138
|
* 2. Extracts and caches the `expandSync` function
|
|
140
|
-
* 3. On subsequent calls, returns the cached function
|
|
139
|
+
* 3. On subsequent calls after a successful load, returns the cached function
|
|
140
|
+
* immediately
|
|
141
141
|
*
|
|
142
142
|
* The function is async because dynamic imports return promises, even though
|
|
143
|
-
* the underlying `expandSync` function is synchronous.
|
|
144
|
-
*
|
|
143
|
+
* the underlying `expandSync` function is synchronous. After a successful
|
|
144
|
+
* initialization, the cached sync function is used directly.
|
|
145
145
|
*
|
|
146
146
|
* ## Error Handling
|
|
147
147
|
*
|
|
148
148
|
* If the native bindings fail to load (e.g., missing native module, architecture
|
|
149
|
-
* mismatch), the function logs a warning and returns `null`.
|
|
150
|
-
*
|
|
149
|
+
* mismatch), the function logs a warning and returns `null`. Because failure
|
|
150
|
+
* leaves the cache `null`, the import (and the warning) is retried on every
|
|
151
|
+
* subsequent call. This allows the preprocessor to skip macro expansion rather
|
|
152
|
+
* than crashing the build.
|
|
151
153
|
*
|
|
152
154
|
* @returns The cached `expandSync` function, or `null` if loading failed
|
|
153
155
|
* @internal
|
|
@@ -156,12 +158,12 @@ async function ensureExpandSync() {
|
|
|
156
158
|
if (expandSync === null) {
|
|
157
159
|
try {
|
|
158
160
|
// Dynamic import defers loading until first use
|
|
159
|
-
const macroforge = await import(
|
|
161
|
+
const macroforge = await import('macroforge');
|
|
160
162
|
expandSync = macroforge.expandSync;
|
|
161
163
|
}
|
|
162
164
|
catch (error) {
|
|
163
165
|
// Log warning but don't throw - allows graceful degradation
|
|
164
|
-
console.warn(
|
|
166
|
+
console.warn('[@macroforge/svelte-preprocessor] Failed to load macroforge native bindings:', error);
|
|
165
167
|
expandSync = null;
|
|
166
168
|
}
|
|
167
169
|
}
|
|
@@ -186,15 +188,18 @@ async function ensureExpandSync() {
|
|
|
186
188
|
* The preprocessor performs these steps for each script block:
|
|
187
189
|
*
|
|
188
190
|
* 1. **Language Check** - Verifies the script is TypeScript (or JavaScript if enabled)
|
|
189
|
-
* 2. **
|
|
190
|
-
* 3. **
|
|
191
|
-
* 4. **
|
|
192
|
-
*
|
|
191
|
+
* 2. **Expansion** - Calls the native engine to expand macros
|
|
192
|
+
* 3. **Diagnostics** - Logs any errors or warnings from expansion
|
|
193
|
+
* 4. **Return** - Returns transformed code or `undefined` if unchanged
|
|
194
|
+
*
|
|
195
|
+
* Every matching script block is passed to the engine; there is no
|
|
196
|
+
* pre-scan for `@derive`, so unchanged blocks simply come back identical.
|
|
193
197
|
*
|
|
194
198
|
* ## Error Handling
|
|
195
199
|
*
|
|
196
200
|
* The preprocessor is designed to be resilient:
|
|
197
|
-
* - If native bindings fail to load, it
|
|
201
|
+
* - If native bindings fail to load, it logs a warning, skips the block, and
|
|
202
|
+
* retries the import on the next block (so a warning is emitted per attempt)
|
|
198
203
|
* - If macro expansion throws, it logs a warning and continues
|
|
199
204
|
* - Svelte compilation proceeds even if preprocessing fails
|
|
200
205
|
*
|
|
@@ -243,9 +248,9 @@ export function macroforgePreprocess(options = {}) {
|
|
|
243
248
|
*
|
|
244
249
|
* @returns An object with `code` property if transformed, or `undefined` if no changes
|
|
245
250
|
*/
|
|
246
|
-
const scriptPreprocessor = async ({ content, filename, attributes
|
|
247
|
-
if (filename?.includes(
|
|
248
|
-
console.log(`[PREPROC DEBUG] file=${filename}, lang=${attributes.lang}, type=${attributes.type}, has @derive=${content.includes(
|
|
251
|
+
const scriptPreprocessor = async ({ content, filename, attributes }) => {
|
|
252
|
+
if (filename?.includes('macroforge.svelte')) {
|
|
253
|
+
console.log(`[PREPROC DEBUG] file=${filename}, lang=${attributes.lang}, type=${attributes.type}, has @derive=${content.includes('@derive')}, has serviceDeserialize=${content.includes('function serviceDeserialize')}`);
|
|
249
254
|
}
|
|
250
255
|
/*
|
|
251
256
|
* STEP 1: Language Detection
|
|
@@ -261,25 +266,15 @@ export function macroforgePreprocess(options = {}) {
|
|
|
261
266
|
* - <script type="module"> → isJavaScript = true
|
|
262
267
|
*/
|
|
263
268
|
const lang = attributes.lang || attributes.type;
|
|
264
|
-
const isTypeScript = lang ===
|
|
265
|
-
const isJavaScript = !lang || lang ===
|
|
266
|
-
lang ===
|
|
269
|
+
const isTypeScript = lang === 'ts' || lang === 'typescript';
|
|
270
|
+
const isJavaScript = !lang || lang === 'js' || lang === 'javascript' ||
|
|
271
|
+
lang === 'module';
|
|
267
272
|
// Skip non-TypeScript blocks unless processJavaScript is enabled
|
|
268
273
|
if (!isTypeScript && !(processJavaScript && isJavaScript)) {
|
|
269
274
|
return; // Return undefined = no changes, Svelte keeps original content
|
|
270
275
|
}
|
|
271
276
|
/*
|
|
272
|
-
* STEP 2:
|
|
273
|
-
*
|
|
274
|
-
* Before loading native bindings, do a cheap string check for "@derive".
|
|
275
|
-
* Most components won't have macros, so this saves the cost of loading
|
|
276
|
-
* and calling the native expansion engine in the common case.
|
|
277
|
-
*/
|
|
278
|
-
if (!hasMacroAnnotations(content)) {
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
/*
|
|
282
|
-
* STEP 3: Load Native Bindings
|
|
277
|
+
* STEP 2: Load Native Bindings
|
|
283
278
|
*
|
|
284
279
|
* The expansion engine is a native module (Rust compiled to Node addon).
|
|
285
280
|
* We lazy-load it on first use to avoid startup overhead.
|
|
@@ -287,36 +282,37 @@ export function macroforgePreprocess(options = {}) {
|
|
|
287
282
|
const expand = await ensureExpandSync();
|
|
288
283
|
if (!expand) {
|
|
289
284
|
// Native bindings unavailable (missing module, architecture mismatch, etc.)
|
|
290
|
-
//
|
|
285
|
+
// ensureExpandSync already logged a warning; skip this block. The import
|
|
286
|
+
// is retried on the next block, so the warning repeats per attempt.
|
|
291
287
|
return;
|
|
292
288
|
}
|
|
293
289
|
try {
|
|
294
290
|
/*
|
|
295
|
-
* STEP
|
|
291
|
+
* STEP 3: Macro Expansion
|
|
296
292
|
*
|
|
297
293
|
* Call the native engine to parse the TypeScript, find @derive decorators,
|
|
298
294
|
* and generate the expanded code with all macro-derived methods/properties.
|
|
299
295
|
*/
|
|
300
|
-
const result = expand(content, filename ||
|
|
301
|
-
keepDecorators
|
|
296
|
+
const result = expand(content, filename || 'component.svelte', {
|
|
297
|
+
keepDecorators
|
|
302
298
|
});
|
|
303
299
|
/*
|
|
304
|
-
* STEP
|
|
300
|
+
* STEP 4: Diagnostic Reporting
|
|
305
301
|
*
|
|
306
302
|
* The expansion engine may report errors (invalid macro syntax, unknown macros)
|
|
307
303
|
* or warnings (deprecated patterns, suggestions). Log these to help developers
|
|
308
304
|
* debug issues without failing the build.
|
|
309
305
|
*/
|
|
310
306
|
for (const diag of result.diagnostics) {
|
|
311
|
-
if (diag.level ===
|
|
307
|
+
if (diag.level === 'error') {
|
|
312
308
|
console.error(`[@macroforge/svelte-preprocessor] Error in ${filename}: ${diag.message}`);
|
|
313
309
|
}
|
|
314
|
-
else if (diag.level ===
|
|
310
|
+
else if (diag.level === 'warning') {
|
|
315
311
|
console.warn(`[@macroforge/svelte-preprocessor] Warning in ${filename}: ${diag.message}`);
|
|
316
312
|
}
|
|
317
313
|
}
|
|
318
314
|
/*
|
|
319
|
-
* STEP
|
|
315
|
+
* STEP 5: Return Transformed Code
|
|
320
316
|
*
|
|
321
317
|
* Only return a result if the code was actually modified. Returning undefined
|
|
322
318
|
* tells Svelte to keep the original content, which is more efficient than
|
|
@@ -324,7 +320,7 @@ export function macroforgePreprocess(options = {}) {
|
|
|
324
320
|
*
|
|
325
321
|
* The return object can include:
|
|
326
322
|
* - code: The transformed source code (required)
|
|
327
|
-
* - map:
|
|
323
|
+
* - map: v3 source map built from the engine's segment mapping (when available)
|
|
328
324
|
*/
|
|
329
325
|
if (result.code && result.code !== content) {
|
|
330
326
|
const mapping = result.sourceMapping;
|
|
@@ -358,8 +354,8 @@ export function macroforgePreprocess(options = {}) {
|
|
|
358
354
|
* Note: We only handle script blocks, not markup or style blocks.
|
|
359
355
|
*/
|
|
360
356
|
return {
|
|
361
|
-
name:
|
|
362
|
-
script: scriptPreprocessor
|
|
357
|
+
name: 'macroforge',
|
|
358
|
+
script: scriptPreprocessor
|
|
363
359
|
};
|
|
364
360
|
}
|
|
365
361
|
/**
|
package/package.json
CHANGED
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"build": "deno install --node-modules-dir --quiet && deno run -A npm:typescript@5/tsc -p tsconfig.json",
|
|
41
41
|
"clean": "rm -rf dist",
|
|
42
42
|
"cleanbuild": "deno task clean && deno task build",
|
|
43
|
-
"test": "deno
|
|
43
|
+
"test": "deno test --allow-read --allow-env --allow-write --unstable-detect-cjs tests/"
|
|
44
44
|
},
|
|
45
45
|
"type": "module",
|
|
46
46
|
"types": "dist/index.d.ts",
|
|
47
|
-
"version": "0.1.
|
|
47
|
+
"version": "0.1.83"
|
|
48
48
|
}
|