macroforge 0.1.0 → 0.1.2

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.
Files changed (2) hide show
  1. package/README.md +371 -0
  2. package/package.json +10 -10
package/README.md ADDED
@@ -0,0 +1,371 @@
1
+ # macroforge
2
+
3
+ TypeScript macro expansion engine powered by Rust and SWC.
4
+
5
+ ## Overview
6
+
7
+ `macroforge` is a native Node.js module that enables compile-time code generation for TypeScript through a Rust-like derive macro system. It parses TypeScript using SWC, expands macros written in Rust, and outputs transformed TypeScript code with full source mapping support.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install macroforge
13
+ ```
14
+
15
+ The package includes pre-built binaries for:
16
+ - macOS (x64, arm64)
17
+ - Linux (x64, arm64)
18
+ - Windows (x64, arm64)
19
+
20
+ ## Quick Start
21
+
22
+ ### Using Built-in Macros
23
+
24
+ ```typescript
25
+ import { Derive, Debug, Clone, Eq } from "macroforge";
26
+
27
+ /** @derive(Debug, Clone, Eq) */
28
+ class User {
29
+ name: string;
30
+ age: number;
31
+
32
+ constructor(name: string, age: number) {
33
+ this.name = name;
34
+ this.age = age;
35
+ }
36
+ }
37
+
38
+ // After macro expansion, the class gains:
39
+ // - toString(): string (from Debug)
40
+ // - clone(): User (from Clone)
41
+ // - equals(other: User): boolean (from Eq)
42
+ ```
43
+
44
+ ### Programmatic API
45
+
46
+ ```typescript
47
+ import { expandSync, NativePlugin } from "macroforge";
48
+
49
+ // One-shot expansion
50
+ const result = expandSync(sourceCode, "file.ts", {
51
+ keepDecorators: false
52
+ });
53
+
54
+ console.log(result.code); // Transformed TypeScript
55
+ console.log(result.diagnostics); // Any warnings/errors
56
+ console.log(result.metadata); // Macro IR metadata (JSON)
57
+
58
+ // Cached expansion (for language servers)
59
+ const plugin = new NativePlugin();
60
+ const cached = plugin.processFile("file.ts", sourceCode, {
61
+ version: "1.0.0" // Cache key
62
+ });
63
+ ```
64
+
65
+ ## API Reference
66
+
67
+ ### Core Functions
68
+
69
+ #### `expandSync(code, filepath, options?)`
70
+
71
+ Expands macros in TypeScript code synchronously.
72
+
73
+ ```typescript
74
+ function expandSync(
75
+ code: string,
76
+ filepath: string,
77
+ options?: ExpandOptions
78
+ ): ExpandResult;
79
+
80
+ interface ExpandOptions {
81
+ keepDecorators?: boolean; // Keep @derive decorators in output (default: false)
82
+ }
83
+
84
+ interface ExpandResult {
85
+ code: string; // Transformed TypeScript
86
+ types?: string; // Generated .d.ts content
87
+ metadata?: string; // Macro IR as JSON
88
+ diagnostics: MacroDiagnostic[]; // Warnings and errors
89
+ sourceMapping?: SourceMappingResult; // Position mapping data
90
+ }
91
+ ```
92
+
93
+ #### `transformSync(code, filepath)`
94
+
95
+ Lower-level transform that returns additional metadata.
96
+
97
+ ```typescript
98
+ function transformSync(code: string, filepath: string): TransformResult;
99
+
100
+ interface TransformResult {
101
+ code: string;
102
+ map?: string; // Source map (not yet implemented)
103
+ types?: string; // Generated declarations
104
+ metadata?: string;
105
+ }
106
+ ```
107
+
108
+ #### `checkSyntax(code, filepath)`
109
+
110
+ Validates TypeScript syntax without macro expansion.
111
+
112
+ ```typescript
113
+ function checkSyntax(code: string, filepath: string): SyntaxCheckResult;
114
+
115
+ interface SyntaxCheckResult {
116
+ ok: boolean;
117
+ error?: string;
118
+ }
119
+ ```
120
+
121
+ #### `parseImportSources(code, filepath)`
122
+
123
+ Extracts import information from TypeScript code.
124
+
125
+ ```typescript
126
+ function parseImportSources(code: string, filepath: string): ImportSourceResult[];
127
+
128
+ interface ImportSourceResult {
129
+ local: string; // Local identifier name
130
+ module: string; // Module specifier
131
+ }
132
+ ```
133
+
134
+ ### Classes
135
+
136
+ #### `NativePlugin`
137
+
138
+ Stateful plugin with caching for language server integration.
139
+
140
+ ```typescript
141
+ class NativePlugin {
142
+ constructor();
143
+
144
+ // Process file with version-based caching
145
+ processFile(
146
+ filepath: string,
147
+ code: string,
148
+ options?: ProcessFileOptions
149
+ ): ExpandResult;
150
+
151
+ // Get position mapper for a cached file
152
+ getMapper(filepath: string): NativeMapper | null;
153
+
154
+ // Map diagnostics from expanded to original positions
155
+ mapDiagnostics(filepath: string, diags: JsDiagnostic[]): JsDiagnostic[];
156
+
157
+ // Logging utilities
158
+ log(message: string): void;
159
+ setLogFile(path: string): void;
160
+ }
161
+ ```
162
+
163
+ #### `NativeMapper` / `PositionMapper`
164
+
165
+ Maps positions between original and expanded code.
166
+
167
+ ```typescript
168
+ class NativeMapper {
169
+ constructor(mapping: SourceMappingResult);
170
+
171
+ isEmpty(): boolean;
172
+ originalToExpanded(pos: number): number;
173
+ expandedToOriginal(pos: number): number | null;
174
+ generatedBy(pos: number): string | null;
175
+ mapSpanToOriginal(start: number, length: number): SpanResult | null;
176
+ mapSpanToExpanded(start: number, length: number): SpanResult;
177
+ isInGenerated(pos: number): boolean;
178
+ }
179
+ ```
180
+
181
+ ### Built-in Decorators
182
+
183
+ | Decorator | Description |
184
+ |-----------|-------------|
185
+ | `@Derive(...features)` | Class decorator that triggers macro expansion |
186
+ | `@Debug` | Generates `toString(): string` method |
187
+ | `@Clone` | Generates `clone(): T` method |
188
+ | `@Eq` | Generates `equals(other: T): boolean` method |
189
+
190
+ ## Writing Custom Macros
191
+
192
+ Custom macros are written in Rust and compiled to native Node.js addons. See the [playground/macro](../../playground/macro) directory for examples.
193
+
194
+ ### Minimal Macro Crate
195
+
196
+ **Cargo.toml:**
197
+ ```toml
198
+ [package]
199
+ name = "my-macros"
200
+ version = "0.1.0"
201
+ edition = "2024"
202
+
203
+ [lib]
204
+ crate-type = ["cdylib"]
205
+
206
+ [dependencies]
207
+ macroforge_ts = "0.1.0"
208
+ napi = { version = "3.5.2", features = ["napi8", "compat-mode"] }
209
+ napi-derive = "3.3.3"
210
+
211
+ [build-dependencies]
212
+ napi-build = "2.3.1"
213
+ ```
214
+
215
+ **src/lib.rs:**
216
+ ```rust
217
+ use macroforge_ts::ts_macro_derive::ts_macro_derive;
218
+ use macroforge_ts::ts_quote::body;
219
+ use macroforge_ts::ts_syn::{
220
+ Data, DeriveInput, MacroforgeError, TsStream, parse_ts_macro_input,
221
+ };
222
+
223
+ #[ts_macro_derive(
224
+ JSON,
225
+ description = "Generates toJSON() returning a plain object"
226
+ )]
227
+ pub fn derive_json(mut input: TsStream) -> Result<TsStream, MacroforgeError> {
228
+ let input = parse_ts_macro_input!(input as DeriveInput);
229
+
230
+ match &input.data {
231
+ Data::Class(class) => {
232
+ Ok(body! {
233
+ toJSON(): Record<string, unknown> {
234
+ return {
235
+ {#for field in class.field_names()}
236
+ @{field}: this.@{field},
237
+ {/for}
238
+ };
239
+ }
240
+ })
241
+ }
242
+ _ => Err(MacroforgeError::new(
243
+ input.decorator_span(),
244
+ "@derive(JSON) only works on classes",
245
+ )),
246
+ }
247
+ }
248
+ ```
249
+
250
+ ### Template Syntax
251
+
252
+ The `ts_template!` and `body!` macros support:
253
+
254
+ | Syntax | Description |
255
+ |--------|-------------|
256
+ | `@{expr}` | Interpolate Rust expression as identifier/code |
257
+ | `{#for x in iter}...{/for}` | Loop over iterables |
258
+ | `{%let name = expr}` | Local variable binding |
259
+ | `{#if cond}...{/if}` | Conditional blocks |
260
+
261
+ ### Re-exported Crates
262
+
263
+ `macroforge_ts` re-exports everything needed for macro development:
264
+
265
+ ```rust
266
+ // All available via macroforge_ts::*
267
+ pub extern crate ts_syn; // AST types, parsing
268
+ pub extern crate ts_quote; // Code generation templates
269
+ pub extern crate ts_macro_derive; // #[ts_macro_derive] attribute
270
+ pub extern crate inventory; // Macro registration
271
+ pub extern crate serde_json; // Serialization
272
+ pub extern crate napi; // Node.js bindings
273
+ pub extern crate napi_derive; // NAPI proc-macros
274
+
275
+ // SWC modules
276
+ pub use ts_syn::swc_core;
277
+ pub use ts_syn::swc_common;
278
+ pub use ts_syn::swc_ecma_ast;
279
+ ```
280
+
281
+ ## Integration
282
+
283
+ ### Vite Plugin
284
+
285
+ ```typescript
286
+ // vite.config.ts
287
+ import macroforge from "@macroforge/vite-plugin";
288
+
289
+ export default defineConfig({
290
+ plugins: [
291
+ macroforge({
292
+ typesOutputDir: ".macroforge/types",
293
+ metadataOutputDir: ".macroforge/meta",
294
+ generateTypes: true,
295
+ emitMetadata: true,
296
+ }),
297
+ ],
298
+ });
299
+ ```
300
+
301
+ ### TypeScript Plugin
302
+
303
+ Add to `tsconfig.json` for IDE support:
304
+
305
+ ```json
306
+ {
307
+ "compilerOptions": {
308
+ "plugins": [
309
+ {
310
+ "name": "@macroforge/typescript-plugin"
311
+ }
312
+ ]
313
+ }
314
+ }
315
+ ```
316
+
317
+ ## Debug API
318
+
319
+ For debugging macro registration:
320
+
321
+ ```typescript
322
+ import {
323
+ __macroforgeGetManifest,
324
+ __macroforgeGetMacroNames,
325
+ __macroforgeIsMacroPackage,
326
+ __macroforgeDebugDescriptors,
327
+ __macroforgeDebugLookup,
328
+ } from "macroforge";
329
+
330
+ // Get all registered macros
331
+ const manifest = __macroforgeGetManifest();
332
+ console.log(manifest.macros);
333
+
334
+ // Check if current package exports macros
335
+ console.log(__macroforgeIsMacroPackage());
336
+
337
+ // List macro names
338
+ console.log(__macroforgeGetMacroNames());
339
+
340
+ // Debug lookup
341
+ console.log(__macroforgeDebugLookup("@my/macros", "JSON"));
342
+ ```
343
+
344
+ ## Architecture
345
+
346
+ ```
347
+ ┌─────────────────────────────────────────────────────────┐
348
+ │ Node.js / Vite │
349
+ ├─────────────────────────────────────────────────────────┤
350
+ │ NAPI-RS Bindings │
351
+ ├─────────────────────────────────────────────────────────┤
352
+ │ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │
353
+ │ │ ts_syn │ │ ts_quote │ │ts_macro_derive│ │
354
+ │ │ (parsing) │ │ (templating) │ │ (proc-macro) │ │
355
+ │ └─────────────┘ └──────────────┘ └───────────────┘ │
356
+ ├─────────────────────────────────────────────────────────┤
357
+ │ SWC Core │
358
+ │ (TypeScript parsing & codegen) │
359
+ └─────────────────────────────────────────────────────────┘
360
+ ```
361
+
362
+ ## Performance
363
+
364
+ - **Thread-safe expansion**: Each expansion runs in an isolated thread with a 32MB stack to handle deep AST recursion
365
+ - **Caching**: `NativePlugin` caches expansion results by file version
366
+ - **Binary search**: Position mapping uses O(log n) lookups
367
+ - **Zero-copy parsing**: SWC's arena allocator minimizes allocations
368
+
369
+ ## License
370
+
371
+ MIT
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "macroforge",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "TypeScript macro expansion engine powered by Rust and SWC",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/rymskip/macroforge.git"
9
+ "url": "git+https://github.com/rymskip/macroforge-ts.git"
10
10
  },
11
11
  "keywords": [
12
12
  "typescript",
@@ -18,9 +18,9 @@
18
18
  "author": "macroforge contributors",
19
19
  "license": "MIT",
20
20
  "bugs": {
21
- "url": "https://github.com/rymskip/macroforge/issues"
21
+ "url": "https://github.com/rymskip/macroforge-ts/issues"
22
22
  },
23
- "homepage": "https://github.com/rymskip/macroforge#readme",
23
+ "homepage": "https://github.com/rymskip/macroforge-ts#readme",
24
24
  "napi": {
25
25
  "binaryName": "macroforge",
26
26
  "packageName": "@macroforge/bin",
@@ -41,12 +41,12 @@
41
41
  "node": ">= 18"
42
42
  },
43
43
  "optionalDependencies": {
44
- "@macroforge/bin-darwin-x64": "0.1.0",
45
- "@macroforge/bin-darwin-arm64": "0.1.0",
46
- "@macroforge/bin-linux-x64-gnu": "0.1.0",
47
- "@macroforge/bin-linux-arm64-gnu": "0.1.0",
48
- "@macroforge/bin-win32-x64-msvc": "0.1.0",
49
- "@macroforge/bin-win32-arm64-msvc": "0.1.0"
44
+ "@macroforge/bin-darwin-x64": "0.1.1",
45
+ "@macroforge/bin-darwin-arm64": "0.1.1",
46
+ "@macroforge/bin-linux-x64-gnu": "0.1.1",
47
+ "@macroforge/bin-linux-arm64-gnu": "0.1.1",
48
+ "@macroforge/bin-win32-x64-msvc": "0.1.1",
49
+ "@macroforge/bin-win32-arm64-msvc": "0.1.1"
50
50
  },
51
51
  "scripts": {
52
52
  "artifacts": "napi artifacts --npm-dir npm"