@macroforge/svelte-preprocessor 0.1.80 → 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 CHANGED
@@ -16,13 +16,13 @@ npm install @macroforge/svelte-preprocessor
16
16
 
17
17
  ### Functions
18
18
 
19
- - **`macroforgePreprocess`** - Creates a Svelte preprocessor that expands
20
- Macroforge macros in `<script>` blocks.
19
+ - **`macroforgePreprocess`** - Creates a Svelte preprocessor that expands Macroforge macros in
20
+ `<script>` blocks.
21
21
 
22
22
  ### Interfaces
23
23
 
24
- - **`MacroforgePreprocessorOptions`** - Configuration options for the Macroforge
25
- Svelte preprocessor.
24
+ - **`MacroforgePreprocessorOptions`** - Configuration options for the Macroforge Svelte
25
+ preprocessor.
26
26
 
27
27
  ### Constants
28
28
 
@@ -31,8 +31,8 @@ npm install @macroforge/svelte-preprocessor
31
31
  ## Documentation
32
32
 
33
33
  See the
34
- [full documentation](https://macroforge.dev/docs/api/reference/typescript/svelte-preprocessor)
35
- on the Macroforge website.
34
+ [full documentation](https://macroforge.dev/docs/api/reference/typescript/svelte-preprocessor) on
35
+ the Macroforge website.
36
36
 
37
37
  ## License
38
38
 
package/dist/index.d.ts CHANGED
@@ -25,7 +25,7 @@
25
25
  *
26
26
  * @packageDocumentation
27
27
  */
28
- import type { PreprocessorGroup } from "svelte/compiler";
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. **Quick Scan** - Skips blocks without `@derive` (performance optimization)
96
- * 3. **Expansion** - Calls the native engine to expand macros
97
- * 4. **Diagnostics** - Logs any errors or warnings from expansion
98
- * 5. **Return** - Returns transformed code or `undefined` if unchanged
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 silently skips processing
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
  *
@@ -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;AA6OvE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,6BAA6B;IAC5C;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,6BAAkC,GAC1C,iBAAiB,CAoJnB;AAED;;;;;;;GAOG;AACH,eAAe,oBAAoB,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
@@ -28,10 +28,10 @@
28
28
  // ============================================================================
29
29
  // Source Map Generation
30
30
  // ============================================================================
31
- const VLQ_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
31
+ const VLQ_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
32
32
  function vlqEncode(value) {
33
33
  let vlq = value < 0 ? (-value << 1) | 1 : value << 1;
34
- let encoded = "";
34
+ let encoded = '';
35
35
  do {
36
36
  let digit = vlq & 0x1f;
37
37
  vlq >>>= 5;
@@ -45,7 +45,7 @@ function vlqEncode(value) {
45
45
  function buildLineOffsets(text) {
46
46
  const offsets = [0];
47
47
  for (let i = 0; i < text.length; i++) {
48
- if (text[i] === "\n")
48
+ if (text[i] === '\n')
49
49
  offsets.push(i + 1);
50
50
  }
51
51
  return offsets;
@@ -79,7 +79,7 @@ function buildSourceMap(original, expanded, mapping, filename) {
79
79
  lineSegments.get(exp.line).push({
80
80
  expCol: exp.column,
81
81
  origLine: orig.line,
82
- origCol: orig.column,
82
+ origCol: orig.column
83
83
  });
84
84
  }
85
85
  // Encode VLQ mappings
@@ -89,7 +89,7 @@ function buildSourceMap(original, expanded, mapping, filename) {
89
89
  for (let line = 0; line < totalLines; line++) {
90
90
  const segs = lineSegments.get(line);
91
91
  if (!segs || segs.length === 0) {
92
- mappingsArr.push("");
92
+ mappingsArr.push('');
93
93
  continue;
94
94
  }
95
95
  segs.sort((a, b) => a.expCol - b.expCol);
@@ -104,14 +104,14 @@ function buildSourceMap(original, expanded, mapping, filename) {
104
104
  prevOrigLine = seg.origLine;
105
105
  prevOrigCol = seg.origCol;
106
106
  }
107
- mappingsArr.push(parts.join(","));
107
+ mappingsArr.push(parts.join(','));
108
108
  }
109
109
  return {
110
110
  version: 3,
111
111
  sources: [filename],
112
112
  sourcesContent: [original],
113
113
  names: [],
114
- mappings: mappingsArr.join(";"),
114
+ mappings: mappingsArr.join(';')
115
115
  };
116
116
  }
117
117
  /**
@@ -120,7 +120,7 @@ function buildSourceMap(original, expanded, mapping, filename) {
120
120
  * This variable implements a lazy-loading pattern:
121
121
  * - Initially `null`, indicating the binding hasn't been loaded yet
122
122
  * - Set to the actual function after first successful load
123
- * - Remains `null` if loading fails (graceful degradation)
123
+ * - Remains `null` if loading fails, so the next call retries the import
124
124
  *
125
125
  * The lazy-loading approach avoids loading native bindings at module import time,
126
126
  * which improves startup performance when the preprocessor is registered but no
@@ -136,17 +136,20 @@ let expandSync = null;
136
136
  *
137
137
  * 1. On first call, dynamically imports the `macroforge` package
138
138
  * 2. Extracts and caches the `expandSync` function
139
- * 3. On subsequent calls, returns the cached function immediately
139
+ * 3. On subsequent calls after a successful load, returns the cached function
140
+ * immediately
140
141
  *
141
142
  * The function is async because dynamic imports return promises, even though
142
- * the underlying `expandSync` function is synchronous. This async wrapper
143
- * only runs once; after initialization, the cached sync function is used directly.
143
+ * the underlying `expandSync` function is synchronous. After a successful
144
+ * initialization, the cached sync function is used directly.
144
145
  *
145
146
  * ## Error Handling
146
147
  *
147
148
  * If the native bindings fail to load (e.g., missing native module, architecture
148
- * mismatch), the function logs a warning and returns `null`. This allows the
149
- * preprocessor to gracefully skip macro expansion rather than crashing the build.
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.
150
153
  *
151
154
  * @returns The cached `expandSync` function, or `null` if loading failed
152
155
  * @internal
@@ -155,12 +158,12 @@ async function ensureExpandSync() {
155
158
  if (expandSync === null) {
156
159
  try {
157
160
  // Dynamic import defers loading until first use
158
- const macroforge = await import("macroforge");
161
+ const macroforge = await import('macroforge');
159
162
  expandSync = macroforge.expandSync;
160
163
  }
161
164
  catch (error) {
162
165
  // Log warning but don't throw - allows graceful degradation
163
- console.warn("[@macroforge/svelte-preprocessor] Failed to load macroforge native bindings:", error);
166
+ console.warn('[@macroforge/svelte-preprocessor] Failed to load macroforge native bindings:', error);
164
167
  expandSync = null;
165
168
  }
166
169
  }
@@ -185,15 +188,18 @@ async function ensureExpandSync() {
185
188
  * The preprocessor performs these steps for each script block:
186
189
  *
187
190
  * 1. **Language Check** - Verifies the script is TypeScript (or JavaScript if enabled)
188
- * 2. **Quick Scan** - Skips blocks without `@derive` (performance optimization)
189
- * 3. **Expansion** - Calls the native engine to expand macros
190
- * 4. **Diagnostics** - Logs any errors or warnings from expansion
191
- * 5. **Return** - Returns transformed code or `undefined` if unchanged
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.
192
197
  *
193
198
  * ## Error Handling
194
199
  *
195
200
  * The preprocessor is designed to be resilient:
196
- * - If native bindings fail to load, it silently skips processing
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)
197
203
  * - If macro expansion throws, it logs a warning and continues
198
204
  * - Svelte compilation proceeds even if preprocessing fails
199
205
  *
@@ -242,9 +248,9 @@ export function macroforgePreprocess(options = {}) {
242
248
  *
243
249
  * @returns An object with `code` property if transformed, or `undefined` if no changes
244
250
  */
245
- const scriptPreprocessor = async ({ content, filename, attributes, }) => {
246
- if (filename?.includes("macroforge.svelte")) {
247
- console.log(`[PREPROC DEBUG] file=${filename}, lang=${attributes.lang}, type=${attributes.type}, has @derive=${content.includes("@derive")}, has serviceDeserialize=${content.includes("function serviceDeserialize")}`);
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')}`);
248
254
  }
249
255
  /*
250
256
  * STEP 1: Language Detection
@@ -260,18 +266,15 @@ export function macroforgePreprocess(options = {}) {
260
266
  * - <script type="module"> → isJavaScript = true
261
267
  */
262
268
  const lang = attributes.lang || attributes.type;
263
- const isTypeScript = lang === "ts" || lang === "typescript";
264
- const isJavaScript = !lang || lang === "js" || lang === "javascript" ||
265
- lang === "module";
269
+ const isTypeScript = lang === 'ts' || lang === 'typescript';
270
+ const isJavaScript = !lang || lang === 'js' || lang === 'javascript' ||
271
+ lang === 'module';
266
272
  // Skip non-TypeScript blocks unless processJavaScript is enabled
267
273
  if (!isTypeScript && !(processJavaScript && isJavaScript)) {
268
274
  return; // Return undefined = no changes, Svelte keeps original content
269
275
  }
270
276
  /*
271
- * STEP 2: Quick Scan Optimization
272
- *
273
- /*
274
- * STEP 3: Load Native Bindings
277
+ * STEP 2: Load Native Bindings
275
278
  *
276
279
  * The expansion engine is a native module (Rust compiled to Node addon).
277
280
  * We lazy-load it on first use to avoid startup overhead.
@@ -279,36 +282,37 @@ export function macroforgePreprocess(options = {}) {
279
282
  const expand = await ensureExpandSync();
280
283
  if (!expand) {
281
284
  // Native bindings unavailable (missing module, architecture mismatch, etc.)
282
- // Silently skip - the component will fail later if macros are actually needed
285
+ // ensureExpandSync already logged a warning; skip this block. The import
286
+ // is retried on the next block, so the warning repeats per attempt.
283
287
  return;
284
288
  }
285
289
  try {
286
290
  /*
287
- * STEP 4: Macro Expansion
291
+ * STEP 3: Macro Expansion
288
292
  *
289
293
  * Call the native engine to parse the TypeScript, find @derive decorators,
290
294
  * and generate the expanded code with all macro-derived methods/properties.
291
295
  */
292
- const result = expand(content, filename || "component.svelte", {
293
- keepDecorators,
296
+ const result = expand(content, filename || 'component.svelte', {
297
+ keepDecorators
294
298
  });
295
299
  /*
296
- * STEP 5: Diagnostic Reporting
300
+ * STEP 4: Diagnostic Reporting
297
301
  *
298
302
  * The expansion engine may report errors (invalid macro syntax, unknown macros)
299
303
  * or warnings (deprecated patterns, suggestions). Log these to help developers
300
304
  * debug issues without failing the build.
301
305
  */
302
306
  for (const diag of result.diagnostics) {
303
- if (diag.level === "error") {
307
+ if (diag.level === 'error') {
304
308
  console.error(`[@macroforge/svelte-preprocessor] Error in ${filename}: ${diag.message}`);
305
309
  }
306
- else if (diag.level === "warning") {
310
+ else if (diag.level === 'warning') {
307
311
  console.warn(`[@macroforge/svelte-preprocessor] Warning in ${filename}: ${diag.message}`);
308
312
  }
309
313
  }
310
314
  /*
311
- * STEP 6: Return Transformed Code
315
+ * STEP 5: Return Transformed Code
312
316
  *
313
317
  * Only return a result if the code was actually modified. Returning undefined
314
318
  * tells Svelte to keep the original content, which is more efficient than
@@ -316,11 +320,10 @@ export function macroforgePreprocess(options = {}) {
316
320
  *
317
321
  * The return object can include:
318
322
  * - code: The transformed source code (required)
319
- * - map: Source map for debugging (optional, not yet implemented)
323
+ * - map: v3 source map built from the engine's segment mapping (when available)
320
324
  */
321
325
  if (result.code && result.code !== content) {
322
- const mapping = result
323
- .sourceMapping;
326
+ const mapping = result.sourceMapping;
324
327
  const map = mapping?.segments?.length && filename
325
328
  ? buildSourceMap(content, result.code, mapping, filename)
326
329
  : undefined;
@@ -351,8 +354,8 @@ export function macroforgePreprocess(options = {}) {
351
354
  * Note: We only handle script blocks, not markup or style blocks.
352
355
  */
353
356
  return {
354
- name: "macroforge",
355
- script: scriptPreprocessor,
357
+ name: 'macroforge',
358
+ script: scriptPreprocessor
356
359
  };
357
360
  }
358
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 task build && node --test tests/**/*.test.js"
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.80"
47
+ "version": "0.1.83"
48
48
  }