@soda-gql/swc 0.11.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.mjs","names":[],"sources":["../src/native/index.js"],"sourcesContent":["/* tslint:disable */\n/* eslint-disable */\n/* prettier-ignore */\n\n/* auto-generated by NAPI-RS */\n\nconst { existsSync, readFileSync } = require('fs')\nconst { join } = require('path')\n\nconst { platform, arch } = process\n\nlet nativeBinding = null\nlet localFileExisted = false\nlet loadError = null\n\nfunction isMusl() {\n // For Node 10\n if (!process.report || typeof process.report.getReport !== 'function') {\n try {\n const lddPath = require('child_process').execSync('which ldd').toString().trim()\n return readFileSync(lddPath, 'utf8').includes('musl')\n } catch (e) {\n return true\n }\n } else {\n const { glibcVersionRuntime } = process.report.getReport().header\n return !glibcVersionRuntime\n }\n}\n\nswitch (platform) {\n case 'android':\n switch (arch) {\n case 'arm64':\n localFileExisted = existsSync(join(__dirname, 'swc.android-arm64.node'))\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'android-arm64.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'android-arm64')\n }\n } catch (e) {\n loadError = e\n }\n break\n case 'arm':\n localFileExisted = existsSync(join(__dirname, 'swc.android-arm-eabi.node'))\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'android-arm-eabi.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'android-arm-eabi')\n }\n } catch (e) {\n loadError = e\n }\n break\n default:\n throw new Error(`Unsupported architecture on Android ${arch}`)\n }\n break\n case 'win32':\n switch (arch) {\n case 'x64':\n localFileExisted = existsSync(\n join(__dirname, 'swc.win32-x64-msvc.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'win32-x64-msvc.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'win32-x64-msvc')\n }\n } catch (e) {\n loadError = e\n }\n break\n case 'ia32':\n localFileExisted = existsSync(\n join(__dirname, 'swc.win32-ia32-msvc.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'win32-ia32-msvc.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'win32-ia32-msvc')\n }\n } catch (e) {\n loadError = e\n }\n break\n case 'arm64':\n localFileExisted = existsSync(\n join(__dirname, 'swc.win32-arm64-msvc.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'win32-arm64-msvc.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'win32-arm64-msvc')\n }\n } catch (e) {\n loadError = e\n }\n break\n default:\n throw new Error(`Unsupported architecture on Windows: ${arch}`)\n }\n break\n case 'darwin':\n localFileExisted = existsSync(join(__dirname, 'swc.darwin-universal.node'))\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'darwin-universal.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'darwin-universal')\n }\n break\n } catch {}\n switch (arch) {\n case 'x64':\n localFileExisted = existsSync(join(__dirname, 'swc.darwin-x64.node'))\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'darwin-x64.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'darwin-x64')\n }\n } catch (e) {\n loadError = e\n }\n break\n case 'arm64':\n localFileExisted = existsSync(\n join(__dirname, 'swc.darwin-arm64.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'darwin-arm64.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'darwin-arm64')\n }\n } catch (e) {\n loadError = e\n }\n break\n default:\n throw new Error(`Unsupported architecture on macOS: ${arch}`)\n }\n break\n case 'freebsd':\n if (arch !== 'x64') {\n throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)\n }\n localFileExisted = existsSync(join(__dirname, 'swc.freebsd-x64.node'))\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'freebsd-x64.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'freebsd-x64')\n }\n } catch (e) {\n loadError = e\n }\n break\n case 'linux':\n switch (arch) {\n case 'x64':\n if (isMusl()) {\n localFileExisted = existsSync(\n join(__dirname, 'swc.linux-x64-musl.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'linux-x64-musl.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'linux-x64-musl')\n }\n } catch (e) {\n loadError = e\n }\n } else {\n localFileExisted = existsSync(\n join(__dirname, 'swc.linux-x64-gnu.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'linux-x64-gnu.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'linux-x64-gnu')\n }\n } catch (e) {\n loadError = e\n }\n }\n break\n case 'arm64':\n if (isMusl()) {\n localFileExisted = existsSync(\n join(__dirname, 'swc.linux-arm64-musl.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'linux-arm64-musl.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'linux-arm64-musl')\n }\n } catch (e) {\n loadError = e\n }\n } else {\n localFileExisted = existsSync(\n join(__dirname, 'swc.linux-arm64-gnu.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'linux-arm64-gnu.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'linux-arm64-gnu')\n }\n } catch (e) {\n loadError = e\n }\n }\n break\n case 'arm':\n if (isMusl()) {\n localFileExisted = existsSync(\n join(__dirname, 'swc.linux-arm-musleabihf.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'linux-arm-musleabihf.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'linux-arm-musleabihf')\n }\n } catch (e) {\n loadError = e\n }\n } else {\n localFileExisted = existsSync(\n join(__dirname, 'swc.linux-arm-gnueabihf.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'linux-arm-gnueabihf.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'linux-arm-gnueabihf')\n }\n } catch (e) {\n loadError = e\n }\n }\n break\n case 'riscv64':\n if (isMusl()) {\n localFileExisted = existsSync(\n join(__dirname, 'swc.linux-riscv64-musl.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'linux-riscv64-musl.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'linux-riscv64-musl')\n }\n } catch (e) {\n loadError = e\n }\n } else {\n localFileExisted = existsSync(\n join(__dirname, 'swc.linux-riscv64-gnu.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'linux-riscv64-gnu.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'linux-riscv64-gnu')\n }\n } catch (e) {\n loadError = e\n }\n }\n break\n case 's390x':\n localFileExisted = existsSync(\n join(__dirname, 'swc.linux-s390x-gnu.node')\n )\n try {\n if (localFileExisted) {\n nativeBinding = require('./swc.'.slice(0) + 'linux-s390x-gnu.node')\n } else {\n nativeBinding = require('@soda-gql/swc-'.slice(0) + 'linux-s390x-gnu')\n }\n } catch (e) {\n loadError = e\n }\n break\n default:\n throw new Error(`Unsupported architecture on Linux: ${arch}`)\n }\n break\n default:\n throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)\n}\n\nif (!nativeBinding) {\n if (loadError) {\n throw loadError\n }\n throw new Error(`Failed to load native binding`)\n}\n\nconst { transform, SwcTransformer } = nativeBinding\n\nmodule.exports.transform = transform\nmodule.exports.SwcTransformer = SwcTransformer\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@soda-gql/swc",
3
+ "version": "0.11.0",
4
+ "description": "SWC-based native transformer for soda-gql",
5
+ "type": "module",
6
+ "private": false,
7
+ "license": "MIT",
8
+ "files": [
9
+ "dist",
10
+ "src"
11
+ ],
12
+ "author": {
13
+ "name": "Shota Hatada",
14
+ "email": "shota.hatada@whatasoda.me",
15
+ "url": "https://github.com/whatasoda"
16
+ },
17
+ "keywords": [
18
+ "graphql",
19
+ "codegen",
20
+ "zero-runtime",
21
+ "typescript",
22
+ "swc",
23
+ "transformer",
24
+ "native"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/whatasoda/soda-gql.git",
29
+ "directory": "packages/swc"
30
+ },
31
+ "homepage": "https://github.com/whatasoda/soda-gql#readme",
32
+ "bugs": {
33
+ "url": "https://github.com/whatasoda/soda-gql/issues"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "main": "./dist/index.mjs",
39
+ "module": "./dist/index.mjs",
40
+ "types": "./dist/index.d.mts",
41
+ "exports": {
42
+ "./native": {
43
+ "@soda-gql": "./@x-native.ts",
44
+ "types": "./dist/native.d.mts",
45
+ "import": "./dist/native.mjs",
46
+ "require": "./dist/native.cjs",
47
+ "default": "./dist/native.mjs"
48
+ },
49
+ ".": {
50
+ "@soda-gql": "./@x-index.ts",
51
+ "types": "./dist/index.d.mts",
52
+ "import": "./dist/index.mjs",
53
+ "require": "./dist/index.cjs",
54
+ "default": "./dist/index.mjs"
55
+ },
56
+ "./package.json": "./package.json"
57
+ },
58
+ "dependencies": {
59
+ "@ampproject/remapping": "^2.3.0",
60
+ "@soda-gql/builder": "0.11.0",
61
+ "@soda-gql/common": "0.11.0",
62
+ "@soda-gql/config": "0.11.0",
63
+ "@soda-gql/core": "0.11.0"
64
+ },
65
+ "devDependencies": {
66
+ "@napi-rs/cli": "^2.18.4",
67
+ "@soda-gql/tsc": "0.11.0",
68
+ "prettier": "^3.4.2"
69
+ },
70
+ "peerDependencies": {
71
+ "@swc/core": "^1.0.0"
72
+ },
73
+ "optionalDependencies": {
74
+ "@soda-gql/swc-darwin-x64": "0.11.0",
75
+ "@soda-gql/swc-darwin-arm64": "0.11.0",
76
+ "@soda-gql/swc-linux-x64-musl": "0.11.0",
77
+ "@soda-gql/swc-win32-x64-msvc": "0.11.0",
78
+ "@soda-gql/swc-linux-x64-gnu": "0.11.0"
79
+ }
80
+ }
package/src/index.ts ADDED
@@ -0,0 +1,290 @@
1
+ /**
2
+ * SWC-based transformer for soda-gql GraphQL code generation.
3
+ *
4
+ * This module provides a TypeScript wrapper around the native Rust transformer.
5
+ */
6
+
7
+ import { realpathSync } from "node:fs";
8
+ import { resolve } from "node:path";
9
+ import remapping from "@ampproject/remapping";
10
+ import type { BuilderArtifact } from "@soda-gql/builder";
11
+ import type { ResolvedSodaGqlConfig } from "@soda-gql/config";
12
+
13
+ // The native module will be loaded at runtime via the napi-rs generated loader
14
+ let nativeModule: NativeModule | null = null;
15
+
16
+ interface NativeModule {
17
+ transform(inputJson: string): string;
18
+ SwcTransformer: new (artifactJson: string, configJson: string) => NativeTransformer;
19
+ }
20
+
21
+ interface NativeTransformer {
22
+ transform(sourceCode: string, sourcePath: string): string;
23
+ }
24
+
25
+ /**
26
+ * Plugin error from the SWC transformer.
27
+ * This matches the Rust PluginError structure for consistent error reporting.
28
+ */
29
+ export type SwcPluginError = {
30
+ /** Always "PluginError" for type discrimination */
31
+ readonly type: "PluginError";
32
+ /** Error code for programmatic handling (e.g., "SODA_GQL_METADATA_NOT_FOUND") */
33
+ readonly code: string;
34
+ /** Human-readable error message */
35
+ readonly message: string;
36
+ /** Stage where the error occurred */
37
+ readonly stage: "analysis" | "transform";
38
+ /** Source filename if applicable */
39
+ readonly filename?: string;
40
+ /** Canonical ID if applicable */
41
+ readonly canonicalId?: string;
42
+ /** Artifact type if applicable */
43
+ readonly artifactType?: string;
44
+ /** Builder type if applicable */
45
+ readonly builderType?: string;
46
+ /** Argument name if applicable */
47
+ readonly argName?: string;
48
+ };
49
+
50
+ interface TransformResult {
51
+ outputCode: string;
52
+ transformed: boolean;
53
+ sourceMap?: string;
54
+ errors?: SwcPluginError[];
55
+ }
56
+
57
+ /**
58
+ * Load the native module.
59
+ * Uses the napi-rs generated loader which handles platform detection.
60
+ */
61
+ const loadNativeModule = async (): Promise<NativeModule> => {
62
+ if (nativeModule) {
63
+ return nativeModule;
64
+ }
65
+
66
+ try {
67
+ // Use require() for the napi-rs generated loader (CommonJS)
68
+ const { createRequire } = await import("node:module");
69
+ const require = createRequire(import.meta.url);
70
+ nativeModule = require("./native/index.js") as NativeModule;
71
+ return nativeModule;
72
+ } catch (error) {
73
+ throw new Error(
74
+ "Failed to load @soda-gql/swc native module. " +
75
+ "Make sure the native module is built for your platform. " +
76
+ `Run 'bun run build' in the packages/swc directory. (${error})`,
77
+ );
78
+ }
79
+ };
80
+
81
+ export type ModuleFormat = "esm" | "cjs";
82
+
83
+ export type TransformOptions = {
84
+ /** Compiler options for output format */
85
+ compilerOptions?: {
86
+ /** Module format: CommonJS or ESNext */
87
+ module?: "CommonJS" | "ESNext";
88
+ };
89
+ /** Resolved soda-gql configuration */
90
+ config: ResolvedSodaGqlConfig;
91
+ /** Pre-built artifact from the builder */
92
+ artifact: BuilderArtifact;
93
+ /** Whether to generate source maps */
94
+ sourceMap?: boolean;
95
+ };
96
+
97
+ export type TransformInput = {
98
+ /** Source code to transform */
99
+ sourceCode: string;
100
+ /** Path to the source file */
101
+ sourcePath: string;
102
+ /** Input source map from previous transformer (JSON string) */
103
+ inputSourceMap?: string;
104
+ };
105
+
106
+ /**
107
+ * Normalize path separators to forward slashes (cross-platform).
108
+ * This matches the behavior of @soda-gql/common normalizePath.
109
+ */
110
+ const normalizePath = (value: string): string => value.replace(/\\/g, "/");
111
+
112
+ /**
113
+ * Filter artifact to only include elements for the given source file.
114
+ * This significantly reduces JSON serialization overhead for large codebases.
115
+ *
116
+ * Canonical IDs have the format: "filepath::astPath"
117
+ * We filter by matching the filepath prefix.
118
+ */
119
+ const filterArtifactForFile = (artifact: BuilderArtifact, sourcePath: string): BuilderArtifact => {
120
+ const prefix = `${sourcePath}::`;
121
+
122
+ const filteredElements: BuilderArtifact["elements"] = {};
123
+ for (const [id, element] of Object.entries(artifact.elements)) {
124
+ if (id.startsWith(prefix)) {
125
+ (filteredElements as Record<string, typeof element>)[id] = element;
126
+ }
127
+ }
128
+
129
+ return {
130
+ elements: filteredElements,
131
+ report: { stats: { hits: 0, misses: 0, skips: 0 }, durationMs: 0, warnings: [] },
132
+ };
133
+ };
134
+
135
+ /**
136
+ * Resolve the canonical path to the graphql-system file.
137
+ * Uses realpath to resolve symlinks for accurate comparison.
138
+ */
139
+ const resolveGraphqlSystemPath = (config: ResolvedSodaGqlConfig): string => {
140
+ const graphqlSystemPath = resolve(config.outdir, "index.ts");
141
+ try {
142
+ return normalizePath(realpathSync(graphqlSystemPath));
143
+ } catch {
144
+ // If realpath fails (file doesn't exist yet), fall back to resolved path
145
+ return normalizePath(resolve(graphqlSystemPath));
146
+ }
147
+ };
148
+
149
+ export type TransformOutput = {
150
+ /** Whether any transformation was performed */
151
+ transformed: boolean;
152
+ /** The transformed source code (or original if no transformation) */
153
+ sourceCode: string;
154
+ /** Source map JSON, if source map generation was enabled */
155
+ sourceMap?: string;
156
+ /** Errors encountered during transformation (non-fatal) */
157
+ errors: SwcPluginError[];
158
+ };
159
+
160
+ /**
161
+ * Transformer interface.
162
+ */
163
+ export interface Transformer {
164
+ transform(input: TransformInput): TransformOutput;
165
+ }
166
+
167
+ /**
168
+ * Create a transformer instance.
169
+ *
170
+ * @param options - Transform options including config and artifact
171
+ * @returns A transformer that can transform source files
172
+ */
173
+ export const createTransformer = async (options: TransformOptions): Promise<Transformer> => {
174
+ const native = await loadNativeModule();
175
+
176
+ const isCJS = options.compilerOptions?.module === "CommonJS";
177
+
178
+ // Resolve the graphql-system file path for stubbing
179
+ const graphqlSystemPath = resolveGraphqlSystemPath(options.config);
180
+
181
+ const configJson = JSON.stringify({
182
+ graphqlSystemAliases: options.config.graphqlSystemAliases,
183
+ isCjs: isCJS,
184
+ graphqlSystemPath,
185
+ sourceMap: options.sourceMap ?? false,
186
+ });
187
+
188
+ // Store full artifact for per-file filtering
189
+ const fullArtifact = options.artifact;
190
+
191
+ return {
192
+ transform: ({ sourceCode, sourcePath, inputSourceMap }: TransformInput): TransformOutput => {
193
+ // Resolve to absolute path and normalize for canonical ID consistency
194
+ // This ensures bundlers can pass relative paths safely
195
+ const normalizedPath = normalizePath(resolve(sourcePath));
196
+
197
+ // Filter artifact to only include elements for this file
198
+ // This significantly reduces JSON serialization overhead for large codebases
199
+ const filteredArtifact = filterArtifactForFile(fullArtifact, normalizedPath);
200
+ const filteredArtifactJson = JSON.stringify(filteredArtifact);
201
+
202
+ // Create per-file transformer with filtered artifact
203
+ const fileTransformer = new native.SwcTransformer(filteredArtifactJson, configJson);
204
+ const resultJson = fileTransformer.transform(sourceCode, normalizedPath);
205
+ const result: TransformResult = JSON.parse(resultJson);
206
+
207
+ // Handle source map chaining
208
+ let finalSourceMap: string | undefined;
209
+ if (result.sourceMap) {
210
+ if (inputSourceMap) {
211
+ // Chain source maps: our map -> input map -> original source
212
+ const merged = remapping([JSON.parse(result.sourceMap), JSON.parse(inputSourceMap)], () => null);
213
+ finalSourceMap = JSON.stringify(merged);
214
+ } else {
215
+ finalSourceMap = result.sourceMap;
216
+ }
217
+ }
218
+
219
+ return {
220
+ transformed: result.transformed,
221
+ sourceCode: result.outputCode,
222
+ sourceMap: finalSourceMap,
223
+ errors: result.errors ?? [],
224
+ };
225
+ },
226
+ };
227
+ };
228
+
229
+ /**
230
+ * Transform a single source file (one-shot).
231
+ *
232
+ * For transforming multiple files, use createTransformer() to reuse the artifact.
233
+ *
234
+ * @param input - Transform input including source, path, artifact, and config
235
+ * @returns Transform output
236
+ */
237
+ export const transform = async (
238
+ input: TransformInput & {
239
+ artifact: BuilderArtifact;
240
+ config: ResolvedSodaGqlConfig;
241
+ isCjs?: boolean;
242
+ sourceMap?: boolean;
243
+ },
244
+ ): Promise<TransformOutput> => {
245
+ const native = await loadNativeModule();
246
+
247
+ // Resolve to absolute path and normalize for canonical ID consistency
248
+ // This ensures bundlers can pass relative paths safely
249
+ const normalizedPath = normalizePath(resolve(input.sourcePath));
250
+
251
+ // Filter artifact to only include elements for this file
252
+ const filteredArtifact = filterArtifactForFile(input.artifact, normalizedPath);
253
+
254
+ // Resolve the graphql-system file path for stubbing
255
+ const graphqlSystemPath = resolveGraphqlSystemPath(input.config);
256
+
257
+ const inputJson = JSON.stringify({
258
+ sourceCode: input.sourceCode,
259
+ sourcePath: normalizedPath,
260
+ artifactJson: JSON.stringify(filteredArtifact),
261
+ config: {
262
+ graphqlSystemAliases: input.config.graphqlSystemAliases,
263
+ isCjs: input.isCjs ?? false,
264
+ graphqlSystemPath,
265
+ sourceMap: input.sourceMap ?? false,
266
+ },
267
+ });
268
+
269
+ const resultJson = native.transform(inputJson);
270
+ const result: TransformResult = JSON.parse(resultJson);
271
+
272
+ // Handle source map chaining
273
+ let finalSourceMap: string | undefined;
274
+ if (result.sourceMap) {
275
+ if (input.inputSourceMap) {
276
+ // Chain source maps: our map -> input map -> original source
277
+ const merged = remapping([JSON.parse(result.sourceMap), JSON.parse(input.inputSourceMap)], () => null);
278
+ finalSourceMap = JSON.stringify(merged);
279
+ } else {
280
+ finalSourceMap = result.sourceMap;
281
+ }
282
+ }
283
+
284
+ return {
285
+ transformed: result.transformed,
286
+ sourceCode: result.outputCode,
287
+ sourceMap: finalSourceMap,
288
+ errors: result.errors ?? [],
289
+ };
290
+ };
package/src/lib.rs ADDED
@@ -0,0 +1,87 @@
1
+ //! SWC-based transformer for soda-gql GraphQL code generation.
2
+ //!
3
+ //! This crate provides a native Node.js module using napi-rs that transforms
4
+ //! `gql.default()` calls into `gqlRuntime.*` calls at build time.
5
+
6
+ mod transform;
7
+ mod types;
8
+
9
+ use napi::bindgen_prelude::*;
10
+ use napi_derive::napi;
11
+ use types::config::{TransformConfig, TransformInput, TransformInputRef};
12
+ use types::BuilderArtifact;
13
+
14
+ /// Transform a single source file.
15
+ ///
16
+ /// # Arguments
17
+ /// * `input_json` - JSON-serialized TransformInput containing source code, file path, artifact, and config
18
+ ///
19
+ /// # Returns
20
+ /// JSON-serialized TransformResult containing the transformed code
21
+ #[napi]
22
+ pub fn transform(input_json: String) -> Result<String> {
23
+ let input: TransformInput = serde_json::from_str(&input_json)
24
+ .map_err(|e| Error::from_reason(format!("Failed to parse input: {}", e)))?;
25
+
26
+ let result = transform::transformer::transform_source(&input)
27
+ .map_err(|e| Error::from_reason(e))?;
28
+
29
+ serde_json::to_string(&result)
30
+ .map_err(|e| Error::from_reason(format!("Failed to serialize result: {}", e)))
31
+ }
32
+
33
+ /// Stateful transformer that caches artifact and config for multiple file transformations.
34
+ ///
35
+ /// The artifact is parsed once in the constructor and reused for all subsequent
36
+ /// transform calls, avoiding repeated JSON parsing overhead.
37
+ #[napi]
38
+ pub struct SwcTransformer {
39
+ /// Pre-parsed BuilderArtifact (parsed once in constructor)
40
+ artifact: BuilderArtifact,
41
+ config: TransformConfig,
42
+ }
43
+
44
+ #[napi]
45
+ impl SwcTransformer {
46
+ /// Create a new transformer instance.
47
+ ///
48
+ /// # Arguments
49
+ /// * `artifact_json` - JSON-serialized BuilderArtifact
50
+ /// * `config_json` - JSON-serialized TransformConfig
51
+ #[napi(constructor)]
52
+ pub fn new(artifact_json: String, config_json: String) -> Result<Self> {
53
+ let config: TransformConfig = serde_json::from_str(&config_json)
54
+ .map_err(|e| Error::from_reason(format!("Failed to parse config: {}", e)))?;
55
+
56
+ // Parse artifact once in constructor to avoid repeated parsing
57
+ let artifact: BuilderArtifact = serde_json::from_str(&artifact_json)
58
+ .map_err(|e| Error::from_reason(format!("Failed to parse artifact: {}", e)))?;
59
+
60
+ Ok(SwcTransformer { artifact, config })
61
+ }
62
+
63
+ /// Transform a single source file.
64
+ ///
65
+ /// # Arguments
66
+ /// * `source_code` - The source code to transform
67
+ /// * `source_path` - The file path of the source
68
+ ///
69
+ /// # Returns
70
+ /// JSON-serialized TransformResult
71
+ #[napi]
72
+ pub fn transform(&self, source_code: String, source_path: String) -> Result<String> {
73
+ // Use pre-parsed artifact reference instead of re-parsing JSON
74
+ let input = TransformInputRef {
75
+ source_code,
76
+ source_path,
77
+ artifact: &self.artifact,
78
+ config: self.config.clone(),
79
+ };
80
+
81
+ let result = transform::transformer::transform_source_ref(&input)
82
+ .map_err(|e| Error::from_reason(e))?;
83
+
84
+ serde_json::to_string(&result)
85
+ .map_err(|e| Error::from_reason(format!("Failed to serialize result: {}", e)))
86
+ }
87
+ }
@@ -0,0 +1,42 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ /**
7
+ * Transform a single source file.
8
+ *
9
+ * # Arguments
10
+ * * `input_json` - JSON-serialized TransformInput containing source code, file path, artifact, and config
11
+ *
12
+ * # Returns
13
+ * JSON-serialized TransformResult containing the transformed code
14
+ */
15
+ export declare function transform(inputJson: string): string
16
+ /**
17
+ * Stateful transformer that caches artifact and config for multiple file transformations.
18
+ *
19
+ * The artifact is parsed once in the constructor and reused for all subsequent
20
+ * transform calls, avoiding repeated JSON parsing overhead.
21
+ */
22
+ export declare class SwcTransformer {
23
+ /**
24
+ * Create a new transformer instance.
25
+ *
26
+ * # Arguments
27
+ * * `artifact_json` - JSON-serialized BuilderArtifact
28
+ * * `config_json` - JSON-serialized TransformConfig
29
+ */
30
+ constructor(artifactJson: string, configJson: string)
31
+ /**
32
+ * Transform a single source file.
33
+ *
34
+ * # Arguments
35
+ * * `source_code` - The source code to transform
36
+ * * `source_path` - The file path of the source
37
+ *
38
+ * # Returns
39
+ * JSON-serialized TransformResult
40
+ */
41
+ transform(sourceCode: string, sourcePath: string): string
42
+ }