graphql-data-generator 0.4.5 → 0.4.6-alpha.1

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
@@ -13,6 +13,7 @@ tests.
13
13
  - [Transforms](#transforms)
14
14
  - [Testing](#testing)
15
15
  - [Extra properties](#extra)
16
+ - [Browser usage](#browser-usage)
16
17
 
17
18
  ### Key Concepts
18
19
 
@@ -341,3 +342,56 @@ const build = init<
341
342
 
342
343
  build.CreatePost({ internal: true }).internal; // true
343
344
  ```
345
+
346
+ ## Browser usage
347
+
348
+ `graphql-data-generator` can be used in browser environments by providing a
349
+ custom `loadDocument` option to `init`. The default file-system-based loader is
350
+ skipped automatically when Node.js or Deno APIs are not detected.
351
+
352
+ Since browsers don't have file system access, you provide operation documents
353
+ directly — either as strings or pre-parsed `DocumentNode` objects:
354
+
355
+ ```ts
356
+ import { init } from "graphql-data-generator";
357
+
358
+ const schema = `
359
+ type Query {
360
+ getUser(id: ID): User
361
+ }
362
+ type User {
363
+ id: ID!
364
+ name: String!
365
+ }
366
+ `;
367
+
368
+ const operations = {
369
+ GetUser: `
370
+ query GetUser($id: ID) {
371
+ getUser(id: $id) {
372
+ id
373
+ name
374
+ }
375
+ }
376
+ `,
377
+ };
378
+
379
+ const build = init(
380
+ schema,
381
+ { GetUser: "GetUser" },
382
+ {},
383
+ {},
384
+ { User: ["id", "name"] },
385
+ [],
386
+ { ID: (t) => `${t}-id`, String: (t) => `${t}-string` },
387
+ { loadDocument: (key) => operations[key] },
388
+ )(() => ({}));
389
+
390
+ build.User(); // { __typename: "User", id: "User-id", name: "User-string" }
391
+ build.GetUser(); // { request: { query: ..., variables: ... }, result: { data: { getUser: ... } } }
392
+ ```
393
+
394
+ The `loadDocument` function receives the path string from the generated types
395
+ file and should return either a GraphQL string or a `DocumentNode`. This allows
396
+ you to load operations from any source — bundled strings, a network request, or
397
+ an in-memory map.
package/esm/browser.js ADDED
@@ -0,0 +1,3 @@
1
+ export { init } from "./init.js";
2
+ export { clear, operation, proxy } from "./proxy.js";
3
+ export { toObject } from "./util.js";
package/esm/codegen.js CHANGED
@@ -244,7 +244,8 @@ const serializeType = (type, variables = false, depth = 0) => {
244
244
  case "List":
245
245
  if (type.value.optional ||
246
246
  (type.value.kind === "Object" &&
247
- ((type.value.conditionals?.length ?? 0) -
247
+ ((type.value.conditionals?.length ?? 0) +
248
+ (type.value.nonExhaustive?.length ? 1 : 0) -
248
249
  (Object.keys(type.value.value).length ? 0 : 1)))) {
249
250
  return `(${serializeType(type.value, variables, depth)})[]${type.optional ? " | null" : ""}`;
250
251
  }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Default document loader for Node.js and Deno environments.
3
+ * In browser environments, this module loads successfully but the exported
4
+ * function throws a helpful error if called without Node/Deno APIs available.
5
+ */
6
+ import * as dntShim from "./_dnt.shims.js";
7
+ import { createRequire } from "node:module";
8
+ import { readFileSync } from "node:fs";
9
+ import { dirname, resolve } from "node:path";
10
+ import nodeProcess from "node:process";
11
+ import { Kind, parse } from "graphql";
12
+ import { gqlPluckFromCodeStringSync } from "@graphql-tools/graphql-tag-pluck";
13
+ /** Check if input is already a DocumentNode. */
14
+ const isDocumentNode = (input) => typeof input === "object" && input !== null && "kind" in input &&
15
+ input.kind === Kind.DOCUMENT;
16
+ // deno-lint-ignore no-explicit-any
17
+ const isDeno = typeof dntShim.dntGlobalThis.Deno?.version?.deno === "string";
18
+ const isNode = typeof globalThis.process?.versions?.node === "string";
19
+ let _impl;
20
+ if (isDeno || isNode) {
21
+ // deno-lint-ignore no-explicit-any
22
+ const cwd = dntShim.dntGlobalThis.Deno?.cwd?.() ?? nodeProcess.cwd();
23
+ globalThis.require ??= createRequire(cwd);
24
+ const files = {};
25
+ const loadFile = (path) => {
26
+ if (files[path])
27
+ return files[path];
28
+ const raw = readFileSync(path, "utf-8");
29
+ const imports = Array.from(raw.matchAll(/#import "(.*)"/gm), ([, importPath]) => loadFile(require.resolve(importPath.startsWith(".")
30
+ ? resolve(cwd, dirname(path), importPath)
31
+ : importPath, { paths: [cwd] })));
32
+ if (!imports.length)
33
+ return files[path] = raw;
34
+ return files[path] = [raw, ...imports].join("\n\n");
35
+ };
36
+ const cache = {};
37
+ _impl = (path) => {
38
+ const existing = cache[path];
39
+ if (existing) {
40
+ if (isDocumentNode(existing))
41
+ return existing;
42
+ return loadFile(path);
43
+ }
44
+ try {
45
+ const doc = require(require.resolve(path, { paths: [cwd] }));
46
+ if (isDocumentNode(doc)) {
47
+ cache[path] = doc;
48
+ return doc;
49
+ }
50
+ }
51
+ catch {
52
+ // Otherwise load it manually
53
+ }
54
+ const fileContent = loadFile(path);
55
+ try {
56
+ const sources = gqlPluckFromCodeStringSync(path, fileContent);
57
+ cache[path] = Object.fromEntries(sources.map((s) => {
58
+ const document = parse(s);
59
+ const firstOp = document.definitions.find((d) => d.kind === Kind.OPERATION_DEFINITION);
60
+ if (!firstOp) {
61
+ throw new Error(`Could not find an operation in ${path}`);
62
+ }
63
+ return [firstOp.name?.value, document];
64
+ }));
65
+ }
66
+ catch {
67
+ cache[path] = parse(fileContent);
68
+ }
69
+ return loadFile(path);
70
+ };
71
+ }
72
+ /**
73
+ * Default document loader for Node.js and Deno environments.
74
+ * Loads GraphQL documents from the filesystem with support for:
75
+ * - Direct .gql/.graphql files
76
+ * - #import directives
77
+ * - GraphQL tag plucking from JS/TS files
78
+ * - require() with GraphQL loaders
79
+ *
80
+ * In browser environments, throws an error directing users to pass
81
+ * a custom `loadDocument` option to `init`.
82
+ *
83
+ * @param path - Path to the GraphQL operation file
84
+ * @returns The document content as a string or pre-parsed DocumentNode
85
+ */
86
+ export const defaultLoader = (path) => {
87
+ if (!_impl) {
88
+ throw new Error("defaultLoader requires Node.js or Deno file system APIs. " +
89
+ "In browser environments, pass a custom `loadDocument` option to `init`.");
90
+ }
91
+ return _impl(path);
92
+ };
package/esm/index.js CHANGED
@@ -1,4 +1,9 @@
1
- export { init } from "./init.js";
1
+ import { init as _init } from "./init.js";
2
+ import { defaultLoader } from "./defaultLoader.js";
3
+ export const init = (schema, queries, mutations, subscriptions, types, inputs, scalars, options) => _init(schema, queries, mutations, subscriptions, types, inputs, scalars, {
4
+ loadDocument: defaultLoader,
5
+ ...options,
6
+ });
2
7
  export { codegen } from "./codegen.js";
3
8
  export { clear, operation, proxy } from "./proxy.js";
4
9
  export { toObject } from "./util.js";
package/esm/init.js CHANGED
@@ -1,88 +1,44 @@
1
- import * as dntShim from "./_dnt.shims.js";
2
- import { createRequire } from "node:module";
3
- import { readFileSync } from "node:fs";
4
1
  import { Kind, parse, } from "graphql";
5
- import { gqlPluckFromCodeStringSync } from "@graphql-tools/graphql-tag-pluck";
6
2
  import { _proxy, operation, withGetDefaultPatch, } from "./proxy.js";
7
- import { toObject } from "./util.js";
8
- import { dirname, resolve } from "node:path";
9
- globalThis.require ??= createRequire(dntShim.Deno.cwd());
10
- const files = {};
11
- const loadFile = (path) => {
12
- if (files[path])
13
- return files[path];
14
- const raw = readFileSync(path, "utf-8");
15
- const imports = Array.from(raw.matchAll(/#import "(.*)"/gm), ([, importPath]) => loadFile(require.resolve(importPath.startsWith(".")
16
- ? resolve(dntShim.Deno.cwd(), dirname(path), importPath)
17
- : importPath, { paths: [dntShim.Deno.cwd()] })));
18
- if (!imports.length)
19
- return files[path] = raw;
20
- return files[path] = [raw, ...imports].join("\n\n");
21
- };
22
- const getOperationContentMap = {};
23
- const getOperationContent = (path, operationName) => {
24
- const existing = getOperationContentMap[path];
25
- if (existing) {
26
- if (existing.kind === Kind.DOCUMENT)
27
- return existing;
28
- return getOperationContentMap[path][operationName];
29
- }
30
- try {
31
- // First try loading via require, depending on GQL loaders
32
- const doc = require(require.resolve(path, { paths: [dntShim.Deno.cwd()] }));
33
- if (doc && typeof doc === "object" && "kind" in doc && doc.kind === "Document") {
34
- getOperationContentMap[path] = doc;
35
- }
36
- }
37
- catch {
38
- // Otherwise load it manually
39
- const fileContent = loadFile(path);
40
- try {
41
- const sources = gqlPluckFromCodeStringSync(path, fileContent);
42
- getOperationContentMap[path] = Object.fromEntries(sources.map((s) => {
43
- const document = parse(s);
44
- const firstOp = document.definitions.find((d) => d.kind === Kind.OPERATION_DEFINITION);
45
- if (!firstOp)
46
- throw new Error(`Could not find an operation in ${path}`);
47
- return [firstOp.name?.value, document];
48
- }));
49
- }
50
- catch {
51
- getOperationContentMap[path] = parse(fileContent);
52
- }
53
- }
54
- return getOperationContent(path, operationName);
55
- };
3
+ import { raise, toObject } from "./util.js";
4
+ /** Converts a string or DocumentNode to a DocumentNode. */
5
+ const ensureDocument = (input) => typeof input === "string" ? parse(input) : input;
56
6
  // Helper: Unwraps non-null and list wrappers to get the named type.
57
7
  const getNamedType = (type) => type.kind === Kind.NAMED_TYPE ? type.name.value : getNamedType(type.type);
58
8
  // Helper: Find a type definition in the document by name.
59
9
  const getTypeDef = (definitions, typeName) => definitions.find((def) => "name" in def && def.name?.value === typeName);
60
10
  /**
61
11
  * Initialize the data builder.
62
- * @param schema The plain text of your schema.
12
+ * @param schema The schema as a string or pre-parsed DocumentNode.
63
13
  * @param queries List of queries exported from generated types.
64
14
  * @param mutations List of mutations exported from generated types.
65
15
  * @param subscriptions List of subscriptions exported from generated types.
66
16
  * @param types List of types exported from generated types.
67
17
  * @param inputs List of types exported from generated types.
68
18
  * @param scalars A mapping to generate scalar values. Function values will be invoked with their `__typename`.
19
+ * @param options.loadDocument Function to load operation documents. Defaults to the Node/Deno file loader.
20
+ * @param options.finalizeOperation Optional function to finalize operation mocks.
69
21
  */
70
22
  export const init = (schema, queries, mutations, subscriptions, types, inputs, scalars, options) => (fn) => {
71
- const doc = parse(schema);
23
+ const doc = ensureDocument(schema);
24
+ // Memoize document loading so each path is only loaded once
25
+ const documentCache = new Map();
26
+ const loadDocument = (path) => {
27
+ const cached = documentCache.get(path);
28
+ if (cached)
29
+ return cached;
30
+ const result = (options?.loadDocument ?? (() => raise("No loadDocument option provided. " +
31
+ "Use the default import from 'graphql-data-generator' for Node/Deno, " +
32
+ "or pass a custom loadDocument option for browser environments.")))(path);
33
+ const document = ensureDocument(result);
34
+ documentCache.set(path, document);
35
+ return document;
36
+ };
72
37
  // Collect all fragment definitions from operation files, deduplicating by name
73
38
  const fragmentDefinitionsMap = new Map();
74
- const collectFragments = (filePath, operationName) => {
39
+ const collectFragments = (filePath) => {
75
40
  try {
76
- // If we have an operation name, use it; otherwise try to parse the file directly
77
- let document;
78
- if (operationName) {
79
- document = getOperationContent(filePath, operationName);
80
- }
81
- else {
82
- // Parse the file directly to get all definitions including fragments
83
- const fileContent = loadFile(filePath);
84
- document = parse(fileContent);
85
- }
41
+ const document = loadDocument(filePath);
86
42
  for (const def of document.definitions) {
87
43
  if (def.kind === Kind.FRAGMENT_DEFINITION) {
88
44
  // Only add if not already seen (dedup by fragment name)
@@ -305,7 +261,7 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
305
261
  };
306
262
  const operationBuilder = (name, path) => {
307
263
  const builder = (...patches) => {
308
- const query = getOperationContent(path, name);
264
+ const query = loadDocument(path);
309
265
  if (transforms[name] && "default" in transforms[name]) {
310
266
  patches = [transforms[name].default, ...patches];
311
267
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-data-generator",
3
- "version": "0.4.5",
3
+ "version": "0.4.6-alpha.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voces/graphql-data-generator.git"
@@ -19,6 +19,12 @@
19
19
  "types": "./types/index.d.ts",
20
20
  "default": "./esm/index.js"
21
21
  },
22
+ "./browser": {
23
+ "import": "./esm/browser.js",
24
+ "require": "./script/browser.js",
25
+ "types": "./types/browser.d.ts",
26
+ "default": "./esm/browser.js"
27
+ },
22
28
  "./plugin": {
23
29
  "import": "./esm/plugin.cjs",
24
30
  "require": "./script/plugin.cjs",
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toObject = exports.proxy = exports.operation = exports.clear = exports.init = void 0;
4
+ var init_js_1 = require("./init.js");
5
+ Object.defineProperty(exports, "init", { enumerable: true, get: function () { return init_js_1.init; } });
6
+ var proxy_js_1 = require("./proxy.js");
7
+ Object.defineProperty(exports, "clear", { enumerable: true, get: function () { return proxy_js_1.clear; } });
8
+ Object.defineProperty(exports, "operation", { enumerable: true, get: function () { return proxy_js_1.operation; } });
9
+ Object.defineProperty(exports, "proxy", { enumerable: true, get: function () { return proxy_js_1.proxy; } });
10
+ var util_js_1 = require("./util.js");
11
+ Object.defineProperty(exports, "toObject", { enumerable: true, get: function () { return util_js_1.toObject; } });
package/script/codegen.js CHANGED
@@ -250,7 +250,8 @@ const serializeType = (type, variables = false, depth = 0) => {
250
250
  case "List":
251
251
  if (type.value.optional ||
252
252
  (type.value.kind === "Object" &&
253
- ((type.value.conditionals?.length ?? 0) -
253
+ ((type.value.conditionals?.length ?? 0) +
254
+ (type.value.nonExhaustive?.length ? 1 : 0) -
254
255
  (Object.keys(type.value.value).length ? 0 : 1)))) {
255
256
  return `(${serializeType(type.value, variables, depth)})[]${type.optional ? " | null" : ""}`;
256
257
  }
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.defaultLoader = void 0;
30
+ /**
31
+ * Default document loader for Node.js and Deno environments.
32
+ * In browser environments, this module loads successfully but the exported
33
+ * function throws a helpful error if called without Node/Deno APIs available.
34
+ */
35
+ const dntShim = __importStar(require("./_dnt.shims.js"));
36
+ const node_module_1 = require("node:module");
37
+ const node_fs_1 = require("node:fs");
38
+ const node_path_1 = require("node:path");
39
+ const node_process_1 = __importDefault(require("node:process"));
40
+ const graphql_1 = require("graphql");
41
+ const graphql_tag_pluck_1 = require("@graphql-tools/graphql-tag-pluck");
42
+ /** Check if input is already a DocumentNode. */
43
+ const isDocumentNode = (input) => typeof input === "object" && input !== null && "kind" in input &&
44
+ input.kind === graphql_1.Kind.DOCUMENT;
45
+ // deno-lint-ignore no-explicit-any
46
+ const isDeno = typeof dntShim.dntGlobalThis.Deno?.version?.deno === "string";
47
+ const isNode = typeof globalThis.process?.versions?.node === "string";
48
+ let _impl;
49
+ if (isDeno || isNode) {
50
+ // deno-lint-ignore no-explicit-any
51
+ const cwd = dntShim.dntGlobalThis.Deno?.cwd?.() ?? node_process_1.default.cwd();
52
+ globalThis.require ??= (0, node_module_1.createRequire)(cwd);
53
+ const files = {};
54
+ const loadFile = (path) => {
55
+ if (files[path])
56
+ return files[path];
57
+ const raw = (0, node_fs_1.readFileSync)(path, "utf-8");
58
+ const imports = Array.from(raw.matchAll(/#import "(.*)"/gm), ([, importPath]) => loadFile(require.resolve(importPath.startsWith(".")
59
+ ? (0, node_path_1.resolve)(cwd, (0, node_path_1.dirname)(path), importPath)
60
+ : importPath, { paths: [cwd] })));
61
+ if (!imports.length)
62
+ return files[path] = raw;
63
+ return files[path] = [raw, ...imports].join("\n\n");
64
+ };
65
+ const cache = {};
66
+ _impl = (path) => {
67
+ const existing = cache[path];
68
+ if (existing) {
69
+ if (isDocumentNode(existing))
70
+ return existing;
71
+ return loadFile(path);
72
+ }
73
+ try {
74
+ const doc = require(require.resolve(path, { paths: [cwd] }));
75
+ if (isDocumentNode(doc)) {
76
+ cache[path] = doc;
77
+ return doc;
78
+ }
79
+ }
80
+ catch {
81
+ // Otherwise load it manually
82
+ }
83
+ const fileContent = loadFile(path);
84
+ try {
85
+ const sources = (0, graphql_tag_pluck_1.gqlPluckFromCodeStringSync)(path, fileContent);
86
+ cache[path] = Object.fromEntries(sources.map((s) => {
87
+ const document = (0, graphql_1.parse)(s);
88
+ const firstOp = document.definitions.find((d) => d.kind === graphql_1.Kind.OPERATION_DEFINITION);
89
+ if (!firstOp) {
90
+ throw new Error(`Could not find an operation in ${path}`);
91
+ }
92
+ return [firstOp.name?.value, document];
93
+ }));
94
+ }
95
+ catch {
96
+ cache[path] = (0, graphql_1.parse)(fileContent);
97
+ }
98
+ return loadFile(path);
99
+ };
100
+ }
101
+ /**
102
+ * Default document loader for Node.js and Deno environments.
103
+ * Loads GraphQL documents from the filesystem with support for:
104
+ * - Direct .gql/.graphql files
105
+ * - #import directives
106
+ * - GraphQL tag plucking from JS/TS files
107
+ * - require() with GraphQL loaders
108
+ *
109
+ * In browser environments, throws an error directing users to pass
110
+ * a custom `loadDocument` option to `init`.
111
+ *
112
+ * @param path - Path to the GraphQL operation file
113
+ * @returns The document content as a string or pre-parsed DocumentNode
114
+ */
115
+ const defaultLoader = (path) => {
116
+ if (!_impl) {
117
+ throw new Error("defaultLoader requires Node.js or Deno file system APIs. " +
118
+ "In browser environments, pass a custom `loadDocument` option to `init`.");
119
+ }
120
+ return _impl(path);
121
+ };
122
+ exports.defaultLoader = defaultLoader;
package/script/index.js CHANGED
@@ -1,8 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toObject = exports.proxy = exports.operation = exports.clear = exports.codegen = exports.init = void 0;
4
- var init_js_1 = require("./init.js");
5
- Object.defineProperty(exports, "init", { enumerable: true, get: function () { return init_js_1.init; } });
4
+ const init_js_1 = require("./init.js");
5
+ const defaultLoader_js_1 = require("./defaultLoader.js");
6
+ const init = (schema, queries, mutations, subscriptions, types, inputs, scalars, options) => (0, init_js_1.init)(schema, queries, mutations, subscriptions, types, inputs, scalars, {
7
+ loadDocument: defaultLoader_js_1.defaultLoader,
8
+ ...options,
9
+ });
10
+ exports.init = init;
6
11
  var codegen_js_1 = require("./codegen.js");
7
12
  Object.defineProperty(exports, "codegen", { enumerable: true, get: function () { return codegen_js_1.codegen; } });
8
13
  var proxy_js_1 = require("./proxy.js");
package/script/init.js CHANGED
@@ -1,114 +1,47 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  exports.init = void 0;
27
- const dntShim = __importStar(require("./_dnt.shims.js"));
28
- const node_module_1 = require("node:module");
29
- const node_fs_1 = require("node:fs");
30
4
  const graphql_1 = require("graphql");
31
- const graphql_tag_pluck_1 = require("@graphql-tools/graphql-tag-pluck");
32
5
  const proxy_js_1 = require("./proxy.js");
33
6
  const util_js_1 = require("./util.js");
34
- const node_path_1 = require("node:path");
35
- globalThis.require ??= (0, node_module_1.createRequire)(dntShim.Deno.cwd());
36
- const files = {};
37
- const loadFile = (path) => {
38
- if (files[path])
39
- return files[path];
40
- const raw = (0, node_fs_1.readFileSync)(path, "utf-8");
41
- const imports = Array.from(raw.matchAll(/#import "(.*)"/gm), ([, importPath]) => loadFile(require.resolve(importPath.startsWith(".")
42
- ? (0, node_path_1.resolve)(dntShim.Deno.cwd(), (0, node_path_1.dirname)(path), importPath)
43
- : importPath, { paths: [dntShim.Deno.cwd()] })));
44
- if (!imports.length)
45
- return files[path] = raw;
46
- return files[path] = [raw, ...imports].join("\n\n");
47
- };
48
- const getOperationContentMap = {};
49
- const getOperationContent = (path, operationName) => {
50
- const existing = getOperationContentMap[path];
51
- if (existing) {
52
- if (existing.kind === graphql_1.Kind.DOCUMENT)
53
- return existing;
54
- return getOperationContentMap[path][operationName];
55
- }
56
- try {
57
- // First try loading via require, depending on GQL loaders
58
- const doc = require(require.resolve(path, { paths: [dntShim.Deno.cwd()] }));
59
- if (doc && typeof doc === "object" && "kind" in doc && doc.kind === "Document") {
60
- getOperationContentMap[path] = doc;
61
- }
62
- }
63
- catch {
64
- // Otherwise load it manually
65
- const fileContent = loadFile(path);
66
- try {
67
- const sources = (0, graphql_tag_pluck_1.gqlPluckFromCodeStringSync)(path, fileContent);
68
- getOperationContentMap[path] = Object.fromEntries(sources.map((s) => {
69
- const document = (0, graphql_1.parse)(s);
70
- const firstOp = document.definitions.find((d) => d.kind === graphql_1.Kind.OPERATION_DEFINITION);
71
- if (!firstOp)
72
- throw new Error(`Could not find an operation in ${path}`);
73
- return [firstOp.name?.value, document];
74
- }));
75
- }
76
- catch {
77
- getOperationContentMap[path] = (0, graphql_1.parse)(fileContent);
78
- }
79
- }
80
- return getOperationContent(path, operationName);
81
- };
7
+ /** Converts a string or DocumentNode to a DocumentNode. */
8
+ const ensureDocument = (input) => typeof input === "string" ? (0, graphql_1.parse)(input) : input;
82
9
  // Helper: Unwraps non-null and list wrappers to get the named type.
83
10
  const getNamedType = (type) => type.kind === graphql_1.Kind.NAMED_TYPE ? type.name.value : getNamedType(type.type);
84
11
  // Helper: Find a type definition in the document by name.
85
12
  const getTypeDef = (definitions, typeName) => definitions.find((def) => "name" in def && def.name?.value === typeName);
86
13
  /**
87
14
  * Initialize the data builder.
88
- * @param schema The plain text of your schema.
15
+ * @param schema The schema as a string or pre-parsed DocumentNode.
89
16
  * @param queries List of queries exported from generated types.
90
17
  * @param mutations List of mutations exported from generated types.
91
18
  * @param subscriptions List of subscriptions exported from generated types.
92
19
  * @param types List of types exported from generated types.
93
20
  * @param inputs List of types exported from generated types.
94
21
  * @param scalars A mapping to generate scalar values. Function values will be invoked with their `__typename`.
22
+ * @param options.loadDocument Function to load operation documents. Defaults to the Node/Deno file loader.
23
+ * @param options.finalizeOperation Optional function to finalize operation mocks.
95
24
  */
96
25
  const init = (schema, queries, mutations, subscriptions, types, inputs, scalars, options) => (fn) => {
97
- const doc = (0, graphql_1.parse)(schema);
26
+ const doc = ensureDocument(schema);
27
+ // Memoize document loading so each path is only loaded once
28
+ const documentCache = new Map();
29
+ const loadDocument = (path) => {
30
+ const cached = documentCache.get(path);
31
+ if (cached)
32
+ return cached;
33
+ const result = (options?.loadDocument ?? (() => (0, util_js_1.raise)("No loadDocument option provided. " +
34
+ "Use the default import from 'graphql-data-generator' for Node/Deno, " +
35
+ "or pass a custom loadDocument option for browser environments.")))(path);
36
+ const document = ensureDocument(result);
37
+ documentCache.set(path, document);
38
+ return document;
39
+ };
98
40
  // Collect all fragment definitions from operation files, deduplicating by name
99
41
  const fragmentDefinitionsMap = new Map();
100
- const collectFragments = (filePath, operationName) => {
42
+ const collectFragments = (filePath) => {
101
43
  try {
102
- // If we have an operation name, use it; otherwise try to parse the file directly
103
- let document;
104
- if (operationName) {
105
- document = getOperationContent(filePath, operationName);
106
- }
107
- else {
108
- // Parse the file directly to get all definitions including fragments
109
- const fileContent = loadFile(filePath);
110
- document = (0, graphql_1.parse)(fileContent);
111
- }
44
+ const document = loadDocument(filePath);
112
45
  for (const def of document.definitions) {
113
46
  if (def.kind === graphql_1.Kind.FRAGMENT_DEFINITION) {
114
47
  // Only add if not already seen (dedup by fragment name)
@@ -331,7 +264,7 @@ const init = (schema, queries, mutations, subscriptions, types, inputs, scalars,
331
264
  };
332
265
  const operationBuilder = (name, path) => {
333
266
  const builder = (...patches) => {
334
- const query = getOperationContent(path, name);
267
+ const query = loadDocument(path);
335
268
  if (transforms[name] && "default" in transforms[name]) {
336
269
  patches = [transforms[name].default, ...patches];
337
270
  }
@@ -0,0 +1,4 @@
1
+ export { init } from "./init.js";
2
+ export type { DeepPartial, OperationMock, Patch } from "./types.js";
3
+ export { clear, operation, proxy } from "./proxy.js";
4
+ export { toObject } from "./util.js";
@@ -0,0 +1,16 @@
1
+ import type { DocumentInput } from "./init.js";
2
+ /**
3
+ * Default document loader for Node.js and Deno environments.
4
+ * Loads GraphQL documents from the filesystem with support for:
5
+ * - Direct .gql/.graphql files
6
+ * - #import directives
7
+ * - GraphQL tag plucking from JS/TS files
8
+ * - require() with GraphQL loaders
9
+ *
10
+ * In browser environments, throws an error directing users to pass
11
+ * a custom `loadDocument` option to `init`.
12
+ *
13
+ * @param path - Path to the GraphQL operation file
14
+ * @returns The document content as a string or pre-parsed DocumentNode
15
+ */
16
+ export declare const defaultLoader: (path: string) => DocumentInput;
@@ -25,13 +25,6 @@ type PrefixKeys<T, Prefix extends string> = {
25
25
  type MapObjectsToBuilders<T, Transforms> = {
26
26
  [K in keyof T]: ObjectBuilder<T[K], K extends keyof Transforms ? Transforms[K] : ContravariantEmpty>;
27
27
  };
28
- export type MapObjectsToTransforms<Objects extends Record<string, Record<string, unknown>>> = {
29
- [Object in keyof Objects]?: Record<string, DefaultObjectTransform<Objects[Object], Objects[Object]> | ((prev: Objects[Object], ...args: any[]) => Patch<Objects[Object]>)>;
30
- };
31
- type InnerMapOperationsToTransforms<Operations> = {
32
- [Operation in keyof Operations]?: Record<string, InferOperationTransforms<Operations[Operation]>>;
33
- };
34
- export type MapOperationsToTransforms<Queries, Mutations, Subscriptions, Types, Inputs> = InnerMapOperationsToTransforms<ResolveConflicts<Queries, Mutations, Subscriptions, Types, Inputs>>;
35
28
  type MapOperationsToBuilders<T, Transforms, Extra> = {
36
29
  [K in keyof T]: OperationBuilder<T[K] extends {
37
30
  data: infer U;
@@ -49,14 +42,31 @@ type DefaultOperationTransform<T> = {
49
42
  variables: infer V;
50
43
  } ? Patch<V> : never;
51
44
  };
52
- type OperationTransform<T> = (b: {
53
- data: T extends {
45
+ type AllKeysOf<T> = T extends any ? keyof T : never;
46
+ type ValueOf<T, K extends PropertyKey> = T extends any ? K extends keyof T ? T[K] : never : never;
47
+ type ExcessCheck<R, T> = Record<Exclude<keyof R, AllKeysOf<T>>, never> & {
48
+ [K in keyof R & AllKeysOf<T>]: R[K] extends (...args: any[]) => any ? unknown : NonNullable<ValueOf<T, K>> extends any[] ? unknown : NonNullable<R[K]> extends object ? NonNullable<ValueOf<T, K>> extends object ? ExcessCheck<NonNullable<R[K]>, NonNullable<ValueOf<T, K>>> : unknown : unknown;
49
+ };
50
+ type ObjectTransformValue<Value, Obj> = Value extends (...args: any[]) => infer R ? (prev: Obj, ...args: any[]) => Patch<Obj extends Record<string, unknown> ? Obj : never> & ExcessCheck<R, Obj> : DefaultObjectTransform<Obj extends Record<string, unknown> ? Obj : never, Obj extends Record<string, unknown> ? Obj : never> & Record<Exclude<keyof Value, AllKeysOf<Obj>>, never>;
51
+ type OperationTransformValue<Value, Op> = Value extends (...args: any[]) => infer R ? (b: {
52
+ data: Op extends {
54
53
  data: infer U;
55
54
  } ? U : never;
56
- variables: T extends {
55
+ variables: Op extends {
57
56
  variables: infer U;
58
57
  } ? U : never;
59
- }, ...args: any[]) => Patch<T>;
60
- type InferOperationTransforms<T> = DefaultOperationTransform<T> | OperationTransform<T>;
58
+ }, ...args: any[]) => Patch<Op> & ExcessCheck<R, Op> : DefaultOperationTransform<Op> & Record<Exclude<keyof Value, "data" | "variables">, never>;
59
+ /**
60
+ * Self-referential constraint that ensures:
61
+ * 1. Top-level keys are known type/input/operation names (unknown → never)
62
+ * 2. Transform values don't have excess properties
63
+ */
64
+ export type Transforms<T, Objects extends Record<string, Record<string, unknown>>, Queries, Mutations, Subscriptions, Types extends Record<string, Record<string, unknown>>, Inputs extends Record<string, Record<string, unknown>>> = {
65
+ [K in keyof T]: K extends keyof Objects ? {
66
+ [TK in keyof T[K]]: ObjectTransformValue<T[K][TK], Objects[K]>;
67
+ } : K extends keyof ResolveConflicts<Queries, Mutations, Subscriptions, Types, Inputs> ? {
68
+ [TK in keyof T[K]]: OperationTransformValue<T[K][TK], ResolveConflicts<Queries, Mutations, Subscriptions, Types, Inputs>[K]>;
69
+ } : never;
70
+ };
61
71
  export type Build<Queries, Mutations, Subscriptions, Types, Inputs, Transforms, Extra> = MapObjectsToBuilders<Types & Inputs, Transforms> & MapOperationsToBuilders<ResolveConflicts<Queries, Mutations, Subscriptions, Types, Inputs>, Transforms, Extra>;
62
72
  export {};
package/types/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export { init } from "./init.js";
1
+ import { init as _init } from "./init.js";
2
+ export declare const init: typeof _init;
2
3
  export type { DeepPartial, OperationMock, Patch } from "./types.js";
3
4
  export { codegen } from "./codegen.js";
4
5
  export { clear, operation, proxy } from "./proxy.js";
package/types/init.d.ts CHANGED
@@ -1,14 +1,19 @@
1
- import type { Build, MapObjectsToTransforms, MapOperationsToTransforms } from "./extendedTypes.js";
1
+ import { DocumentNode } from "graphql";
2
+ import type { Build, Transforms } from "./extendedTypes.js";
2
3
  import type { ContravariantEmpty, OperationMock } from "./types.js";
4
+ /** A GraphQL document can be provided as a string or a pre-parsed DocumentNode. */
5
+ export type DocumentInput = string | DocumentNode;
3
6
  /**
4
7
  * Initialize the data builder.
5
- * @param schema The plain text of your schema.
8
+ * @param schema The schema as a string or pre-parsed DocumentNode.
6
9
  * @param queries List of queries exported from generated types.
7
10
  * @param mutations List of mutations exported from generated types.
8
11
  * @param subscriptions List of subscriptions exported from generated types.
9
12
  * @param types List of types exported from generated types.
10
13
  * @param inputs List of types exported from generated types.
11
14
  * @param scalars A mapping to generate scalar values. Function values will be invoked with their `__typename`.
15
+ * @param options.loadDocument Function to load operation documents. Defaults to the Node/Deno file loader.
16
+ * @param options.finalizeOperation Optional function to finalize operation mocks.
12
17
  */
13
18
  export declare const init: <Query extends Record<string, {
14
19
  data: Record<string, unknown>;
@@ -19,8 +24,10 @@ export declare const init: <Query extends Record<string, {
19
24
  }>, Subscription extends Record<string, {
20
25
  data: Record<string, unknown>;
21
26
  variables?: Record<string, unknown>;
22
- }>, Types extends Record<string, Record<string, unknown>>, Inputs extends Record<string, Record<string, unknown>>, Extra = object>(schema: string, queries: { [operation in keyof Query]: string; }, mutations: { [operation in keyof Mutation]: string; }, subscriptions: { [operation in keyof Subscription]: string; }, types: { readonly [type in keyof Types]: readonly string[]; }, inputs: readonly (keyof Inputs & string)[], scalars: {
27
+ }>, Types extends Record<string, Record<string, unknown>>, Inputs extends Record<string, Record<string, unknown>>, Extra = object>(schema: DocumentInput, queries: { [operation in keyof Query]: string; }, mutations: { [operation in keyof Mutation]: string; }, subscriptions: { [operation in keyof Subscription]: string; }, types: { readonly [type in keyof Types]: readonly string[]; }, inputs: readonly (keyof Inputs & string)[], scalars: {
23
28
  [name: string]: ((typeName: string, fieldName: string) => unknown) | string | number | boolean | null;
24
29
  }, options?: {
30
+ /** Load an operation document from a path. Returns string or DocumentNode. */
31
+ loadDocument?: (path: string) => DocumentInput;
25
32
  finalizeOperation?: <T extends OperationMock & Partial<Extra>>(operation: T) => T;
26
- }) => <Transforms extends MapObjectsToTransforms<Types & Inputs> & MapOperationsToTransforms<Query, Mutation, Subscription, Types, Inputs>>(fn: (b: Build<Query, Mutation, Subscription, Types, Inputs, ContravariantEmpty, Extra>) => Transforms) => Build<Query, Mutation, Subscription, Types, Inputs, Transforms, Extra>;
33
+ }) => <T extends Transforms<T, Types & Inputs, Query, Mutation, Subscription, Types, Inputs>>(fn: (b: Build<Query, Mutation, Subscription, Types, Inputs, ContravariantEmpty, Extra>) => T) => Build<Query, Mutation, Subscription, Types, Inputs, T, Extra>;