@soda-gql/swc 0.11.2 → 0.11.4

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.
@@ -6,6 +6,11 @@
6
6
  */
7
7
 
8
8
  import { describe, expect, it } from "bun:test";
9
+ import { mkdirSync, mkdtempSync, realpathSync, writeFileSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { dirname, join } from "node:path";
12
+ import type { BuilderArtifact } from "@soda-gql/builder";
13
+ import type { ResolvedSodaGqlConfig } from "@soda-gql/config";
9
14
  import { loadTestCases, normalizeCode, type TransformTestCase } from "@soda-gql/tsc/test";
10
15
 
11
16
  // Check if native module is available before running tests
@@ -207,3 +212,197 @@ describe("swc", async () => {
207
212
  });
208
213
  }
209
214
  });
215
+
216
+ /**
217
+ * Helper to write a file, creating parent directories if needed.
218
+ */
219
+ const writeFile = (filePath: string, content: string): void => {
220
+ mkdirSync(dirname(filePath), { recursive: true });
221
+ writeFileSync(filePath, content);
222
+ };
223
+
224
+ /**
225
+ * Create a test config with configurable paths.
226
+ */
227
+ const createStubTestConfig = (options: { outdir: string; scalarsPath: string; adapterPath?: string }): ResolvedSodaGqlConfig => ({
228
+ analyzer: "ts",
229
+ outdir: options.outdir,
230
+ graphqlSystemAliases: ["@/graphql-system"],
231
+ include: [],
232
+ exclude: [],
233
+ schemas: {
234
+ default: {
235
+ schema: [],
236
+ inject: {
237
+ scalars: options.scalarsPath,
238
+ adapter: options.adapterPath,
239
+ },
240
+ defaultInputDepth: 3,
241
+ inputDepthOverrides: {},
242
+ },
243
+ },
244
+ styles: { importExtension: false },
245
+ plugins: {},
246
+ });
247
+
248
+ /**
249
+ * Create an empty artifact for testing.
250
+ */
251
+ const createEmptyArtifact = (): BuilderArtifact => ({
252
+ elements: {},
253
+ report: {
254
+ durationMs: 0,
255
+ warnings: [],
256
+ stats: { hits: 0, misses: 0, skips: 0 },
257
+ },
258
+ });
259
+
260
+ /**
261
+ * Create a canonical temp directory path.
262
+ * On macOS, /var is a symlink to /private/var, so we use realpathSync
263
+ * to get the canonical path that matches what resolveCanonicalPath produces.
264
+ */
265
+ const createCanonicalTempDir = (prefix: string): string => {
266
+ const tmpDir = mkdtempSync(join(tmpdir(), prefix));
267
+ return realpathSync(tmpDir);
268
+ };
269
+
270
+ describe("swc internal module stubbing", () => {
271
+ it.skipIf(!nativeModuleAvailable)("stubs graphql-system/index.ts to export {}", async () => {
272
+ const tmpDir = createCanonicalTempDir("swc-stub-test-");
273
+ const outdir = join(tmpDir, "graphql-system");
274
+ const scalarsPath = join(tmpDir, "scalars.ts");
275
+ const graphqlSystemPath = join(outdir, "index.ts");
276
+
277
+ writeFile(graphqlSystemPath, "export const gql = { default: () => {} };");
278
+ writeFile(scalarsPath, "export const scalar = {};");
279
+
280
+ const config = createStubTestConfig({ outdir, scalarsPath });
281
+ const transformer = await createTransformer({
282
+ config,
283
+ artifact: createEmptyArtifact(),
284
+ });
285
+
286
+ const result = transformer.transform({
287
+ sourceCode: "export const gql = { default: () => {} };",
288
+ sourcePath: graphqlSystemPath,
289
+ });
290
+
291
+ expect(result.sourceCode).toBe("export {};");
292
+ });
293
+
294
+ it.skipIf(!nativeModuleAvailable)("stubs scalars file to export {}", async () => {
295
+ const tmpDir = createCanonicalTempDir("swc-stub-test-");
296
+ const outdir = join(tmpDir, "graphql-system");
297
+ const scalarsPath = join(tmpDir, "scalars.ts");
298
+
299
+ writeFile(scalarsPath, "export const scalar = { ID: {}, String: {} };");
300
+
301
+ const config = createStubTestConfig({ outdir, scalarsPath });
302
+ const transformer = await createTransformer({
303
+ config,
304
+ artifact: createEmptyArtifact(),
305
+ });
306
+
307
+ const result = transformer.transform({
308
+ sourceCode: "export const scalar = { ID: {}, String: {} };",
309
+ sourcePath: scalarsPath,
310
+ });
311
+
312
+ expect(result.sourceCode).toBe("export {};");
313
+ });
314
+
315
+ it.skipIf(!nativeModuleAvailable)("stubs adapter file to export {}", async () => {
316
+ const tmpDir = createCanonicalTempDir("swc-stub-test-");
317
+ const outdir = join(tmpDir, "graphql-system");
318
+ const scalarsPath = join(tmpDir, "scalars.ts");
319
+ const adapterPath = join(tmpDir, "adapter.ts");
320
+
321
+ writeFile(scalarsPath, "export const scalar = {};");
322
+ writeFile(adapterPath, "export const adapter = { fetch: () => {} };");
323
+
324
+ const config = createStubTestConfig({ outdir, scalarsPath, adapterPath });
325
+ const transformer = await createTransformer({
326
+ config,
327
+ artifact: createEmptyArtifact(),
328
+ });
329
+
330
+ const result = transformer.transform({
331
+ sourceCode: "export const adapter = { fetch: () => {} };",
332
+ sourcePath: adapterPath,
333
+ });
334
+
335
+ expect(result.sourceCode).toBe("export {};");
336
+ });
337
+
338
+ it.skipIf(!nativeModuleAvailable)("does not stub regular source files", async () => {
339
+ const tmpDir = createCanonicalTempDir("swc-stub-test-");
340
+ const outdir = join(tmpDir, "graphql-system");
341
+ const scalarsPath = join(tmpDir, "scalars.ts");
342
+ const regularPath = join(tmpDir, "regular.ts");
343
+
344
+ writeFile(scalarsPath, "export const scalar = {};");
345
+ writeFile(regularPath, "export const foo = 'bar';");
346
+
347
+ const config = createStubTestConfig({ outdir, scalarsPath });
348
+ const transformer = await createTransformer({
349
+ config,
350
+ artifact: createEmptyArtifact(),
351
+ });
352
+
353
+ const result = transformer.transform({
354
+ sourceCode: "export const foo = 'bar';",
355
+ sourcePath: regularPath,
356
+ });
357
+
358
+ // Regular files should not be stubbed
359
+ expect(result.sourceCode).not.toBe("export {};");
360
+ expect(result.sourceCode).toContain("foo");
361
+ });
362
+
363
+ it.skipIf(!nativeModuleAvailable)("handles multiple schemas with different inject paths", async () => {
364
+ const tmpDir = createCanonicalTempDir("swc-stub-test-");
365
+ const outdir = join(tmpDir, "graphql-system");
366
+ const scalars1 = join(tmpDir, "schema1", "scalars.ts");
367
+ const scalars2 = join(tmpDir, "schema2", "scalars.ts");
368
+ const adapter2 = join(tmpDir, "schema2", "adapter.ts");
369
+
370
+ writeFile(scalars1, "export const scalar = {};");
371
+ writeFile(scalars2, "export const scalar = {};");
372
+ writeFile(adapter2, "export const adapter = {};");
373
+
374
+ const config: ResolvedSodaGqlConfig = {
375
+ analyzer: "ts",
376
+ outdir,
377
+ graphqlSystemAliases: ["@/graphql-system"],
378
+ include: [],
379
+ exclude: [],
380
+ schemas: {
381
+ schema1: {
382
+ schema: [],
383
+ inject: { scalars: scalars1 },
384
+ defaultInputDepth: 3,
385
+ inputDepthOverrides: {},
386
+ },
387
+ schema2: {
388
+ schema: [],
389
+ inject: { scalars: scalars2, adapter: adapter2 },
390
+ defaultInputDepth: 3,
391
+ inputDepthOverrides: {},
392
+ },
393
+ },
394
+ styles: { importExtension: false },
395
+ plugins: {},
396
+ };
397
+
398
+ const transformer = await createTransformer({
399
+ config,
400
+ artifact: createEmptyArtifact(),
401
+ });
402
+
403
+ // All inject files should be stubbed
404
+ expect(transformer.transform({ sourceCode: "export const s = {};", sourcePath: scalars1 }).sourceCode).toBe("export {};");
405
+ expect(transformer.transform({ sourceCode: "export const s = {};", sourcePath: scalars2 }).sourceCode).toBe("export {};");
406
+ expect(transformer.transform({ sourceCode: "export const a = {};", sourcePath: adapter2 }).sourceCode).toBe("export {};");
407
+ });
408
+ });
@@ -21,6 +21,12 @@ pub struct TransformConfig {
21
21
  #[serde(default)]
22
22
  pub graphql_system_path: Option<String>,
23
23
 
24
+ /// Canonical paths to inject module files (scalars, adapter).
25
+ /// When the source file matches any of these paths, it will be stubbed out.
26
+ /// These are resolved by the TypeScript wrapper and passed to Rust.
27
+ #[serde(default)]
28
+ pub inject_paths: Vec<String>,
29
+
24
30
  /// Whether to generate source maps.
25
31
  /// If true, a source map will be included in the output.
26
32
  #[serde(default)]
@@ -33,6 +39,7 @@ impl Default for TransformConfig {
33
39
  graphql_system_aliases: vec!["@/graphql-system".to_string()],
34
40
  is_cjs: false,
35
41
  graphql_system_path: None,
42
+ inject_paths: Vec::new(),
36
43
  source_map: false,
37
44
  }
38
45
  }