macroforge 0.1.78 → 0.1.80
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 +28 -51
- package/js/buildtime/index.d.ts +107 -0
- package/js/buildtime/index.mjs +65 -0
- package/js/buildtime/index.ts +176 -0
- package/js/cli/svelte-check-wrapper.js +177 -0
- package/js/cli/tsc-wrapper.js +198 -0
- package/js/rules/index.d.ts +94 -0
- package/js/rules/index.mjs +9 -0
- package/js/rules/index.ts +109 -0
- package/package.json +73 -86
- package/pkg/macroforge_ts.d.ts +132 -0
- package/pkg/macroforge_ts.js +1197 -0
- package/pkg/macroforge_ts_bg.wasm +0 -0
- package/pkg/macroforge_ts_bg.wasm.d.ts +69 -0
- package/index.d.ts +0 -1341
- package/index.js +0 -605
package/README.md
CHANGED
|
@@ -5,9 +5,13 @@ TypeScript macro expansion engine - write compile-time macros in Rust
|
|
|
5
5
|
[](https://crates.io/crates/macroforge_ts)
|
|
6
6
|
[](https://docs.rs/macroforge_ts)
|
|
7
7
|
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
8
10
|
This crate provides a TypeScript macro expansion engine that brings Rust-like derive macros to
|
|
9
|
-
TypeScript. It
|
|
10
|
-
|
|
11
|
+
TypeScript. It supports multiple output targets via feature flags:
|
|
12
|
+
|
|
13
|
+
- `wasm`: (Default) Universal WebAssembly module via wasm-bindgen for browser and edge environments.
|
|
14
|
+
- `node`: Optional native Node.js bindings via NAPI-RS.
|
|
11
15
|
|
|
12
16
|
## Overview
|
|
13
17
|
|
|
@@ -19,31 +23,30 @@ concrete implementations. For example, a class decorated with `@derive(Debug, Cl
|
|
|
19
23
|
|
|
20
24
|
The crate is organized into several key components:
|
|
21
25
|
|
|
22
|
-
- **
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
- **Unified API** (`api` module): An output-agnostic trait-based interface (`MacroforgeApi`) that
|
|
27
|
+
defines all macro operations.
|
|
28
|
+
- **Target Bindings**:
|
|
29
|
+
- `bindings_napi`: Node.js specific entry points using NAPI-RS.
|
|
30
|
+
- `bindings_wasm`: Universal entry points using `wasm-bindgen`.
|
|
31
|
+
- **Position Mapping** (`api_types::SourceMappingResult`): Bidirectional source mapping for IDE
|
|
32
|
+
integration.
|
|
33
|
+
- **Macro Host** (`host` module): Core expansion engine with registry and dispatcher.
|
|
34
|
+
- **Built-in Macros** (`builtin` module): Standard derive macros (Debug, Clone, Serialize, etc.).
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
- Implements early bailout for files without `@derive` decorators
|
|
32
|
-
- Caches expansion results keyed by filepath and version
|
|
33
|
-
- Uses binary search for O(log n) position mapping lookups
|
|
36
|
+
## Usage
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
### From Node.js
|
|
36
39
|
|
|
37
40
|
```javascript
|
|
38
|
-
const {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const plugin = new NativePlugin();
|
|
41
|
+
const { expandSync } = require('macroforge');
|
|
42
|
+
const result = expandSync(code, filepath, { keep_decorators: false });
|
|
43
|
+
```
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
const result = plugin.process_file(filepath, code, { version: '1.0' });
|
|
45
|
+
### From WASM
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
```javascript
|
|
48
|
+
import init, { expand_sync } from './pkg/macroforge_ts.js';
|
|
49
|
+
await init();
|
|
47
50
|
const result = expand_sync(code, filepath, { keep_decorators: false });
|
|
48
51
|
```
|
|
49
52
|
|
|
@@ -53,7 +56,7 @@ This crate re-exports several dependencies for convenience when writing custom m
|
|
|
53
56
|
|
|
54
57
|
- `ts_syn`: TypeScript syntax types for AST manipulation
|
|
55
58
|
- `macros`: Macro attributes and quote templates
|
|
56
|
-
- `swc_core`, `swc_common`, `swc_ecma_ast`: SWC
|
|
59
|
+
- `swc_core`, `swc_common`, `swc_ecma_ast`: SWC compatibility infrastructure
|
|
57
60
|
|
|
58
61
|
## Installation
|
|
59
62
|
|
|
@@ -61,41 +64,15 @@ Add this to your `Cargo.toml`:
|
|
|
61
64
|
|
|
62
65
|
```toml
|
|
63
66
|
[dependencies]
|
|
64
|
-
macroforge_ts = "0.1.
|
|
67
|
+
macroforge_ts = "0.1.79"
|
|
65
68
|
```
|
|
66
69
|
|
|
67
70
|
## Key Exports
|
|
68
71
|
|
|
69
|
-
### Structs
|
|
70
|
-
|
|
71
|
-
- **`TransformResult`** - Result of transforming TypeScript code through the macro system.
|
|
72
|
-
- **`MacroDiagnostic`** - A diagnostic message produced during macro expansion.
|
|
73
|
-
- **`MappingSegmentResult`** - A segment mapping a range in the original source to a range in the
|
|
74
|
-
expanded source.
|
|
75
|
-
- **`GeneratedRegionResult`** - A region in the expanded source that was generated by a macro.
|
|
76
|
-
- **`SourceMappingResult`** - Complete source mapping information for a macro expansion.
|
|
77
|
-
- **`ExpandResult`** - Result of expanding macros in TypeScript source code.
|
|
78
|
-
- **`ImportSourceResult`** - Information about an imported identifier from a TypeScript module.
|
|
79
|
-
- **`SyntaxCheckResult`** - Result of checking TypeScript syntax validity.
|
|
80
|
-
- **`SpanResult`** - A span (range) in source code, represented as start position and length.
|
|
81
|
-
- **`JsDiagnostic`** - A diagnostic from the TypeScript/JavaScript compiler or IDE.
|
|
82
|
-
- ... and 8 more
|
|
83
|
-
|
|
84
72
|
### Functions
|
|
85
73
|
|
|
86
|
-
- **`
|
|
87
|
-
- **`
|
|
88
|
-
- **`is_empty`** - Checks if this mapper has no mapping data.
|
|
89
|
-
- **`original_to_expanded`** - Converts a position in the original source to the corresponding
|
|
90
|
-
position in expanded source.
|
|
91
|
-
- **`expanded_to_original`** - Converts a position in the expanded source back to the original
|
|
92
|
-
source position.
|
|
93
|
-
- **`generated_by`** - Returns the name of the macro that generated code at the given position.
|
|
94
|
-
- **`map_span_to_original`** - Maps a span (start + length) from expanded source to original source.
|
|
95
|
-
- **`map_span_to_expanded`** - Maps a span (start + length) from original source to expanded source.
|
|
96
|
-
- **`is_in_generated`** - Checks if a position is inside macro-generated code.
|
|
97
|
-
- **`new`** - Creates a new mapper wrapping the given source mapping.
|
|
98
|
-
- ... and 23 more
|
|
74
|
+
- **`__macroforge_ffi_free`** - Free a buffer allocated by an FFI function.
|
|
75
|
+
- **`__macroforge_ffi_get_manifest`** - Returns the full MacroManifest as JSON via FFI.
|
|
99
76
|
|
|
100
77
|
## API Reference
|
|
101
78
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # Macroforge Buildtime Module
|
|
3
|
+
*
|
|
4
|
+
* Compile-time JavaScript evaluation — the Zig-comptime primitive for
|
|
5
|
+
* TypeScript. Annotate a top-level `const` or `function` declaration with
|
|
6
|
+
* `/** @buildtime *\/` and the macroforge build pass evaluates it in a
|
|
7
|
+
* sandboxed JS context, serializes the result, and splices a plain TS
|
|
8
|
+
* literal back into the module.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* /** @buildtime *\/
|
|
12
|
+
* const BUILT_AT = buildtime.time.iso();
|
|
13
|
+
*
|
|
14
|
+
* /** @buildtime *\/
|
|
15
|
+
* const SCHEMA = buildtime.fs.readJson("./schema.json");
|
|
16
|
+
*
|
|
17
|
+
* /** @buildtime *\/
|
|
18
|
+
* function generateValidators() {
|
|
19
|
+
* const schema = buildtime.fs.readJson("./schema.json");
|
|
20
|
+
* return schema.fields.map(f =>
|
|
21
|
+
* `export function is_${f.name}(v: unknown): v is ${f.type} {
|
|
22
|
+
* return typeof v === "${f.jsType}";
|
|
23
|
+
* }`
|
|
24
|
+
* ).join("\n");
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* ## Runtime behavior
|
|
29
|
+
*
|
|
30
|
+
* Every export from this module is a sentinel. Calling any of them at
|
|
31
|
+
* runtime throws — they should have been resolved by the macroforge
|
|
32
|
+
* build pass and no longer exist in the output. If you see the runtime
|
|
33
|
+
* error, the build pass is not running on this file.
|
|
34
|
+
*
|
|
35
|
+
* @module macroforge/buildtime
|
|
36
|
+
*/
|
|
37
|
+
export interface BuildtimeFs {
|
|
38
|
+
/** Read a file as UTF-8 text. Path is resolved relative to the file the
|
|
39
|
+
* @buildtime declaration lives in. Throws if the path is not in the
|
|
40
|
+
* `buildtime.capabilities.filesystem.read` allowlist. */
|
|
41
|
+
readText(path: string): string;
|
|
42
|
+
/** Read a file and parse its content as JSON. Same capability rules
|
|
43
|
+
* as `readText`. */
|
|
44
|
+
readJson(path: string): unknown;
|
|
45
|
+
/** Return whether the path exists on disk. Counts as a read for
|
|
46
|
+
* dependency tracking — if the file appears later, the cache
|
|
47
|
+
* invalidates. */
|
|
48
|
+
exists(path: string): boolean;
|
|
49
|
+
/** Return the names of the entries in a directory, sorted. Counts as
|
|
50
|
+
* a read. */
|
|
51
|
+
listDir(path: string): string[];
|
|
52
|
+
}
|
|
53
|
+
export interface BuildtimeCrypto {
|
|
54
|
+
/** SHA-256 of the input, lowercase hex. Pure — always allowed. */
|
|
55
|
+
sha256(input: string): string;
|
|
56
|
+
/** SHA-512 of the input, lowercase hex. Pure — always allowed. */
|
|
57
|
+
sha512(input: string): string;
|
|
58
|
+
}
|
|
59
|
+
export interface BuildtimeTime {
|
|
60
|
+
/** Current wall-clock time as an ISO 8601 string. Makes builds
|
|
61
|
+
* non-deterministic — prefer recording a fixed timestamp if
|
|
62
|
+
* determinism matters. */
|
|
63
|
+
now(): string;
|
|
64
|
+
/** Current wall-clock time in unix seconds. */
|
|
65
|
+
unix(): number;
|
|
66
|
+
/** Alias for {@link BuildtimeTime.now}. */
|
|
67
|
+
iso(): string;
|
|
68
|
+
}
|
|
69
|
+
export interface BuildtimeFlags {
|
|
70
|
+
/** True if the named flag was passed to the build (e.g. from a
|
|
71
|
+
* `--define` argument or from `config.buildtime.flags`). */
|
|
72
|
+
has(flag: string): boolean;
|
|
73
|
+
/** Value of the named flag, or undefined if not set. */
|
|
74
|
+
get(flag: string): string | undefined;
|
|
75
|
+
}
|
|
76
|
+
export interface BuildtimeLocation {
|
|
77
|
+
/** Absolute path of the source file the @buildtime declaration
|
|
78
|
+
* lives in. */
|
|
79
|
+
readonly file: string;
|
|
80
|
+
/** 1-based line number of the `/** @buildtime *\/` annotation. */
|
|
81
|
+
readonly line: number;
|
|
82
|
+
/** 1-based column number of the annotation. */
|
|
83
|
+
readonly column: number;
|
|
84
|
+
}
|
|
85
|
+
export interface Buildtime {
|
|
86
|
+
readonly fs: BuildtimeFs;
|
|
87
|
+
readonly crypto: BuildtimeCrypto;
|
|
88
|
+
readonly time: BuildtimeTime;
|
|
89
|
+
/** Environment variables the build was allowed to read. Populated
|
|
90
|
+
* from `buildtime.capabilities.env` in macroforge.config.js. Reads
|
|
91
|
+
* of variables not in the allowlist return `undefined`. */
|
|
92
|
+
readonly env: Record<string, string | undefined>;
|
|
93
|
+
readonly flags: BuildtimeFlags;
|
|
94
|
+
readonly location: BuildtimeLocation;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The compile-time API. Inside a `@buildtime` declaration, calls
|
|
98
|
+
* against this object are routed to native implementations. At
|
|
99
|
+
* runtime, every access throws — see module docs for why.
|
|
100
|
+
*/
|
|
101
|
+
export declare const buildtime: Buildtime;
|
|
102
|
+
/**
|
|
103
|
+
* Re-export with the name `$buildtime` for users who prefer the macroforge
|
|
104
|
+
* convention of prefixing compile-time identifiers with `$`. Points at the
|
|
105
|
+
* same object.
|
|
106
|
+
*/
|
|
107
|
+
export declare const $buildtime: Buildtime;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// js/buildtime/index.ts
|
|
2
|
+
var runtimeError = (method) => new Error(
|
|
3
|
+
`macroforge/buildtime.${method}: @buildtime APIs are only available inside @buildtime declarations evaluated by macroforge. If you're seeing this at runtime, the macroforge plugin is not installed or not running on this file.`
|
|
4
|
+
);
|
|
5
|
+
var buildtime = {
|
|
6
|
+
fs: {
|
|
7
|
+
readText(_path) {
|
|
8
|
+
throw runtimeError("fs.readText");
|
|
9
|
+
},
|
|
10
|
+
readJson(_path) {
|
|
11
|
+
throw runtimeError("fs.readJson");
|
|
12
|
+
},
|
|
13
|
+
exists(_path) {
|
|
14
|
+
throw runtimeError("fs.exists");
|
|
15
|
+
},
|
|
16
|
+
listDir(_path) {
|
|
17
|
+
throw runtimeError("fs.listDir");
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
crypto: {
|
|
21
|
+
sha256(_input) {
|
|
22
|
+
throw runtimeError("crypto.sha256");
|
|
23
|
+
},
|
|
24
|
+
sha512(_input) {
|
|
25
|
+
throw runtimeError("crypto.sha512");
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
time: {
|
|
29
|
+
now() {
|
|
30
|
+
throw runtimeError("time.now");
|
|
31
|
+
},
|
|
32
|
+
unix() {
|
|
33
|
+
throw runtimeError("time.unix");
|
|
34
|
+
},
|
|
35
|
+
iso() {
|
|
36
|
+
throw runtimeError("time.iso");
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
env: new Proxy(
|
|
40
|
+
{},
|
|
41
|
+
{
|
|
42
|
+
get(_target, _prop) {
|
|
43
|
+
throw runtimeError("env");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
),
|
|
47
|
+
flags: {
|
|
48
|
+
has(_flag) {
|
|
49
|
+
throw runtimeError("flags.has");
|
|
50
|
+
},
|
|
51
|
+
get(_flag) {
|
|
52
|
+
throw runtimeError("flags.get");
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
location: new Proxy({}, {
|
|
56
|
+
get(_target, _prop) {
|
|
57
|
+
throw runtimeError("location");
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
};
|
|
61
|
+
var $buildtime = buildtime;
|
|
62
|
+
export {
|
|
63
|
+
$buildtime,
|
|
64
|
+
buildtime
|
|
65
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # Macroforge Buildtime Module
|
|
3
|
+
*
|
|
4
|
+
* Compile-time JavaScript evaluation — the Zig-comptime primitive for
|
|
5
|
+
* TypeScript. Annotate a top-level `const` or `function` declaration with
|
|
6
|
+
* `/** @buildtime *\/` and the macroforge build pass evaluates it in a
|
|
7
|
+
* sandboxed JS context, serializes the result, and splices a plain TS
|
|
8
|
+
* literal back into the module.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* /** @buildtime *\/
|
|
12
|
+
* const BUILT_AT = buildtime.time.iso();
|
|
13
|
+
*
|
|
14
|
+
* /** @buildtime *\/
|
|
15
|
+
* const SCHEMA = buildtime.fs.readJson("./schema.json");
|
|
16
|
+
*
|
|
17
|
+
* /** @buildtime *\/
|
|
18
|
+
* function generateValidators() {
|
|
19
|
+
* const schema = buildtime.fs.readJson("./schema.json");
|
|
20
|
+
* return schema.fields.map(f =>
|
|
21
|
+
* `export function is_${f.name}(v: unknown): v is ${f.type} {
|
|
22
|
+
* return typeof v === "${f.jsType}";
|
|
23
|
+
* }`
|
|
24
|
+
* ).join("\n");
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* ## Runtime behavior
|
|
29
|
+
*
|
|
30
|
+
* Every export from this module is a sentinel. Calling any of them at
|
|
31
|
+
* runtime throws — they should have been resolved by the macroforge
|
|
32
|
+
* build pass and no longer exist in the output. If you see the runtime
|
|
33
|
+
* error, the build pass is not running on this file.
|
|
34
|
+
*
|
|
35
|
+
* @module macroforge/buildtime
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
const runtimeError = (method: string) =>
|
|
39
|
+
new Error(
|
|
40
|
+
`macroforge/buildtime.${method}: @buildtime APIs are only available inside @buildtime declarations evaluated by macroforge. ` +
|
|
41
|
+
`If you're seeing this at runtime, the macroforge plugin is not installed or not running on this file.`,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export interface BuildtimeFs {
|
|
45
|
+
/** Read a file as UTF-8 text. Path is resolved relative to the file the
|
|
46
|
+
* @buildtime declaration lives in. Throws if the path is not in the
|
|
47
|
+
* `buildtime.capabilities.filesystem.read` allowlist. */
|
|
48
|
+
readText(path: string): string;
|
|
49
|
+
/** Read a file and parse its content as JSON. Same capability rules
|
|
50
|
+
* as `readText`. */
|
|
51
|
+
readJson(path: string): unknown;
|
|
52
|
+
/** Return whether the path exists on disk. Counts as a read for
|
|
53
|
+
* dependency tracking — if the file appears later, the cache
|
|
54
|
+
* invalidates. */
|
|
55
|
+
exists(path: string): boolean;
|
|
56
|
+
/** Return the names of the entries in a directory, sorted. Counts as
|
|
57
|
+
* a read. */
|
|
58
|
+
listDir(path: string): string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface BuildtimeCrypto {
|
|
62
|
+
/** SHA-256 of the input, lowercase hex. Pure — always allowed. */
|
|
63
|
+
sha256(input: string): string;
|
|
64
|
+
/** SHA-512 of the input, lowercase hex. Pure — always allowed. */
|
|
65
|
+
sha512(input: string): string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface BuildtimeTime {
|
|
69
|
+
/** Current wall-clock time as an ISO 8601 string. Makes builds
|
|
70
|
+
* non-deterministic — prefer recording a fixed timestamp if
|
|
71
|
+
* determinism matters. */
|
|
72
|
+
now(): string;
|
|
73
|
+
/** Current wall-clock time in unix seconds. */
|
|
74
|
+
unix(): number;
|
|
75
|
+
/** Alias for {@link BuildtimeTime.now}. */
|
|
76
|
+
iso(): string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface BuildtimeFlags {
|
|
80
|
+
/** True if the named flag was passed to the build (e.g. from a
|
|
81
|
+
* `--define` argument or from `config.buildtime.flags`). */
|
|
82
|
+
has(flag: string): boolean;
|
|
83
|
+
/** Value of the named flag, or undefined if not set. */
|
|
84
|
+
get(flag: string): string | undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface BuildtimeLocation {
|
|
88
|
+
/** Absolute path of the source file the @buildtime declaration
|
|
89
|
+
* lives in. */
|
|
90
|
+
readonly file: string;
|
|
91
|
+
/** 1-based line number of the `/** @buildtime *\/` annotation. */
|
|
92
|
+
readonly line: number;
|
|
93
|
+
/** 1-based column number of the annotation. */
|
|
94
|
+
readonly column: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface Buildtime {
|
|
98
|
+
readonly fs: BuildtimeFs;
|
|
99
|
+
readonly crypto: BuildtimeCrypto;
|
|
100
|
+
readonly time: BuildtimeTime;
|
|
101
|
+
/** Environment variables the build was allowed to read. Populated
|
|
102
|
+
* from `buildtime.capabilities.env` in macroforge.config.js. Reads
|
|
103
|
+
* of variables not in the allowlist return `undefined`. */
|
|
104
|
+
readonly env: Record<string, string | undefined>;
|
|
105
|
+
readonly flags: BuildtimeFlags;
|
|
106
|
+
readonly location: BuildtimeLocation;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The compile-time API. Inside a `@buildtime` declaration, calls
|
|
111
|
+
* against this object are routed to native implementations. At
|
|
112
|
+
* runtime, every access throws — see module docs for why.
|
|
113
|
+
*/
|
|
114
|
+
export const buildtime: Buildtime = {
|
|
115
|
+
fs: {
|
|
116
|
+
readText(_path: string): string {
|
|
117
|
+
throw runtimeError("fs.readText");
|
|
118
|
+
},
|
|
119
|
+
readJson(_path: string): unknown {
|
|
120
|
+
throw runtimeError("fs.readJson");
|
|
121
|
+
},
|
|
122
|
+
exists(_path: string): boolean {
|
|
123
|
+
throw runtimeError("fs.exists");
|
|
124
|
+
},
|
|
125
|
+
listDir(_path: string): string[] {
|
|
126
|
+
throw runtimeError("fs.listDir");
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
crypto: {
|
|
130
|
+
sha256(_input: string): string {
|
|
131
|
+
throw runtimeError("crypto.sha256");
|
|
132
|
+
},
|
|
133
|
+
sha512(_input: string): string {
|
|
134
|
+
throw runtimeError("crypto.sha512");
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
time: {
|
|
138
|
+
now(): string {
|
|
139
|
+
throw runtimeError("time.now");
|
|
140
|
+
},
|
|
141
|
+
unix(): number {
|
|
142
|
+
throw runtimeError("time.unix");
|
|
143
|
+
},
|
|
144
|
+
iso(): string {
|
|
145
|
+
throw runtimeError("time.iso");
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
env: new Proxy(
|
|
149
|
+
{},
|
|
150
|
+
{
|
|
151
|
+
get(_target, _prop): never {
|
|
152
|
+
throw runtimeError("env");
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
),
|
|
156
|
+
flags: {
|
|
157
|
+
has(_flag: string): boolean {
|
|
158
|
+
throw runtimeError("flags.has");
|
|
159
|
+
},
|
|
160
|
+
get(_flag: string): string | undefined {
|
|
161
|
+
throw runtimeError("flags.get");
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
location: new Proxy({} as BuildtimeLocation, {
|
|
165
|
+
get(_target, _prop): never {
|
|
166
|
+
throw runtimeError("location");
|
|
167
|
+
},
|
|
168
|
+
}),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Re-export with the name `$buildtime` for users who prefer the macroforge
|
|
173
|
+
* convention of prefixing compile-time identifiers with `$`. Points at the
|
|
174
|
+
* same object.
|
|
175
|
+
*/
|
|
176
|
+
export const $buildtime = buildtime;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte-check wrapper — spawned by the CLI's `run_svelte_check_wrapper`.
|
|
3
|
+
*
|
|
4
|
+
* Patches `ts.sys.readFile` to expand macros before svelte-check sees the files.
|
|
5
|
+
* Since svelte-check uses TypeScript as a peer dependency and Node.js caches
|
|
6
|
+
* modules, the patched `ts.sys.readFile` is shared with svelte-check's internal
|
|
7
|
+
* TypeScript language service.
|
|
8
|
+
*
|
|
9
|
+
* Arguments:
|
|
10
|
+
* argv[2..] — forwarded to svelte-check CLI
|
|
11
|
+
*
|
|
12
|
+
* Environment:
|
|
13
|
+
* MACROFORGE_TYPE_REGISTRY_PATH — path to pre-built type registry JSON
|
|
14
|
+
*/
|
|
15
|
+
const { createRequire } = require("module");
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
const path = require("path");
|
|
18
|
+
const cwdRequire = createRequire(process.cwd() + "/package.json");
|
|
19
|
+
let ts;
|
|
20
|
+
let macros;
|
|
21
|
+
try {
|
|
22
|
+
ts = cwdRequire("typescript");
|
|
23
|
+
} catch {
|
|
24
|
+
console.error(
|
|
25
|
+
"[macroforge] error: typescript is not installed in this project",
|
|
26
|
+
);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
macros = cwdRequire("macroforge");
|
|
31
|
+
} catch {
|
|
32
|
+
console.error(
|
|
33
|
+
"[macroforge] error: macroforge is not installed in this project",
|
|
34
|
+
);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
if (macros.setupExternalMacros) {
|
|
38
|
+
let resolveDecoratorNames = function (packagePath) {
|
|
39
|
+
const candidates = [packagePath];
|
|
40
|
+
for (const id of candidates) {
|
|
41
|
+
try {
|
|
42
|
+
const pkg = req(id);
|
|
43
|
+
const names = [];
|
|
44
|
+
if (pkg.__macroforgeGetManifest) {
|
|
45
|
+
names.push(
|
|
46
|
+
...(pkg.__macroforgeGetManifest().decorators || []).map(
|
|
47
|
+
(d) => d.export,
|
|
48
|
+
),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
for (const key of Object.keys(pkg)) {
|
|
52
|
+
if (
|
|
53
|
+
key.startsWith("__macroforgeGetManifest_") &&
|
|
54
|
+
typeof pkg[key] === "function"
|
|
55
|
+
) {
|
|
56
|
+
names.push(...(pkg[key]().decorators || []).map((d) => d.export));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (names.length > 0) return [...new Set(names)];
|
|
60
|
+
} catch {}
|
|
61
|
+
}
|
|
62
|
+
return [];
|
|
63
|
+
},
|
|
64
|
+
runMacro = function (ctxJson) {
|
|
65
|
+
const ctx = JSON.parse(ctxJson);
|
|
66
|
+
const fnName = `__macroforgeRun${ctx.macro_name}`;
|
|
67
|
+
const candidates = [ctx.module_path];
|
|
68
|
+
for (const id of candidates) {
|
|
69
|
+
try {
|
|
70
|
+
const pkg = req(id);
|
|
71
|
+
const fn_ = pkg?.[fnName] || pkg?.default?.[fnName];
|
|
72
|
+
if (typeof fn_ === "function") return fn_(ctxJson);
|
|
73
|
+
} catch {}
|
|
74
|
+
}
|
|
75
|
+
throw new Error(`Macro ${fnName} not found in ${ctx.module_path}`);
|
|
76
|
+
};
|
|
77
|
+
var resolveDecoratorNames2 = resolveDecoratorNames,
|
|
78
|
+
runMacro2 = runMacro;
|
|
79
|
+
const req = createRequire(process.cwd() + "/package.json");
|
|
80
|
+
macros.setupExternalMacros(resolveDecoratorNames, runMacro);
|
|
81
|
+
}
|
|
82
|
+
const CONFIG_FILES = [
|
|
83
|
+
"macroforge.config.ts",
|
|
84
|
+
"macroforge.config.mts",
|
|
85
|
+
"macroforge.config.js",
|
|
86
|
+
"macroforge.config.mjs",
|
|
87
|
+
"macroforge.config.cjs",
|
|
88
|
+
];
|
|
89
|
+
let macroConfigPath = null;
|
|
90
|
+
let currentDir = process.cwd();
|
|
91
|
+
while (true) {
|
|
92
|
+
for (const filename of CONFIG_FILES) {
|
|
93
|
+
const candidate = path.join(currentDir, filename);
|
|
94
|
+
if (fs.existsSync(candidate)) {
|
|
95
|
+
macroConfigPath = candidate;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (macroConfigPath) break;
|
|
100
|
+
if (fs.existsSync(path.join(currentDir, "package.json"))) break;
|
|
101
|
+
const parent = path.dirname(currentDir);
|
|
102
|
+
if (parent === currentDir) break;
|
|
103
|
+
currentDir = parent;
|
|
104
|
+
}
|
|
105
|
+
if (macroConfigPath) {
|
|
106
|
+
try {
|
|
107
|
+
const configContent = fs.readFileSync(macroConfigPath, "utf8");
|
|
108
|
+
macros.loadConfig(configContent, macroConfigPath);
|
|
109
|
+
} catch {}
|
|
110
|
+
}
|
|
111
|
+
const typeRegistryPath = process.env.MACROFORGE_TYPE_REGISTRY_PATH;
|
|
112
|
+
let typeRegistryJson = void 0;
|
|
113
|
+
if (typeRegistryPath) {
|
|
114
|
+
try {
|
|
115
|
+
typeRegistryJson = fs.readFileSync(typeRegistryPath, "utf8");
|
|
116
|
+
} catch {}
|
|
117
|
+
}
|
|
118
|
+
const declarativeRegistryPath =
|
|
119
|
+
process.env.MACROFORGE_DECLARATIVE_REGISTRY_PATH;
|
|
120
|
+
let declarativeRegistryJson = void 0;
|
|
121
|
+
if (declarativeRegistryPath) {
|
|
122
|
+
try {
|
|
123
|
+
declarativeRegistryJson = fs.readFileSync(declarativeRegistryPath, "utf8");
|
|
124
|
+
} catch {}
|
|
125
|
+
}
|
|
126
|
+
const plugin = new macros.NativePlugin();
|
|
127
|
+
const expandOpts = {};
|
|
128
|
+
if (macroConfigPath) expandOpts.configPath = macroConfigPath;
|
|
129
|
+
if (typeRegistryJson) expandOpts.typeRegistryJson = typeRegistryJson;
|
|
130
|
+
if (declarativeRegistryJson) {
|
|
131
|
+
expandOpts.declarativeRegistryJson = declarativeRegistryJson;
|
|
132
|
+
}
|
|
133
|
+
const tsSys = ts.sys;
|
|
134
|
+
const origReadFile = tsSys.readFile.bind(tsSys);
|
|
135
|
+
// Text-level fast path matching the tsc wrapper — covers derive macros
|
|
136
|
+
// plus both sides of the declarative macro system (defining and consuming).
|
|
137
|
+
function hasMacroMarkers(sourceText) {
|
|
138
|
+
if (!sourceText) return false;
|
|
139
|
+
if (sourceText.includes("@derive")) return true;
|
|
140
|
+
if (sourceText.includes("macroforge/rules")) return true;
|
|
141
|
+
if (sourceText.includes("import macro")) return true;
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
tsSys.readFile = (filePath, encoding) => {
|
|
145
|
+
const content = origReadFile(filePath, encoding);
|
|
146
|
+
if (content == null) return content;
|
|
147
|
+
try {
|
|
148
|
+
if (
|
|
149
|
+
(filePath.endsWith(".ts") || filePath.endsWith(".tsx")) &&
|
|
150
|
+
!filePath.endsWith(".d.ts") &&
|
|
151
|
+
hasMacroMarkers(content)
|
|
152
|
+
) {
|
|
153
|
+
const result = plugin.processFile(filePath, content, expandOpts);
|
|
154
|
+
return result.code || content;
|
|
155
|
+
}
|
|
156
|
+
} catch {}
|
|
157
|
+
return content;
|
|
158
|
+
};
|
|
159
|
+
const args = ["svelte-check"];
|
|
160
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
161
|
+
args.push(process.argv[i]);
|
|
162
|
+
}
|
|
163
|
+
process.argv = [process.argv[0], ...args];
|
|
164
|
+
try {
|
|
165
|
+
cwdRequire("svelte-check");
|
|
166
|
+
} catch (e) {
|
|
167
|
+
if (e.code === "MODULE_NOT_FOUND") {
|
|
168
|
+
console.error(
|
|
169
|
+
"[macroforge] error: svelte-check is not installed in this project",
|
|
170
|
+
);
|
|
171
|
+
console.error(
|
|
172
|
+
"[macroforge] install it with: npm install --save-dev svelte-check",
|
|
173
|
+
);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
throw e;
|
|
177
|
+
}
|