@uni2c/graphqlapi4mp 1.0.2 → 1.0.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.
package/README.md ADDED
@@ -0,0 +1,360 @@
1
+ # @uni2c/graphqlapi4mp
2
+
3
+ 一个轻量、零运行时依赖的 GraphQL SDL 解析与查询文本生成工具。它可以从 SDL 中提取 Query、Mutation、对象类型、标量和枚举,并根据配置生成 selection set 与完整的 GraphQL operation。
4
+
5
+ 适合需要在小程序、Node.js 或前端项目中根据既有 GraphQL Schema 动态构造查询,同时希望控制运行时代码体积的场景。
6
+
7
+ ## 特性
8
+
9
+ - 解析 GraphQL SDL 中的 `type`、`scalar` 和 `enum`
10
+ - 提取 `Query`、`Mutation` 根字段及参数类型
11
+ - 自动选择标量和枚举字段
12
+ - 按配置递归展开对象字段
13
+ - 生成 Query 或 Mutation 文本
14
+ - 同时支持 ESM 和 CommonJS
15
+ - 内置 TypeScript 类型声明
16
+ - 零运行时依赖
17
+
18
+ ## 安装
19
+
20
+ ```bash
21
+ npm install @uni2c/graphqlapi4mp
22
+ ```
23
+
24
+ 也可以使用 pnpm 或 yarn:
25
+
26
+ ```bash
27
+ pnpm add @uni2c/graphqlapi4mp
28
+ # 或
29
+ yarn add @uni2c/graphqlapi4mp
30
+ ```
31
+
32
+ ## 模块导入
33
+
34
+ ESM / TypeScript:
35
+
36
+ ```ts
37
+ import {
38
+ buildGraphQLQuery,
39
+ buildSelectionFields,
40
+ parseSDL,
41
+ } from '@uni2c/graphqlapi4mp';
42
+ ```
43
+
44
+ CommonJS:
45
+
46
+ ```js
47
+ const {
48
+ buildGraphQLQuery,
49
+ buildSelectionFields,
50
+ parseSDL,
51
+ } = require('@uni2c/graphqlapi4mp');
52
+ ```
53
+
54
+ ## 完整示例
55
+
56
+ 假设有以下 SDL:
57
+
58
+ ```graphql
59
+ scalar DateTime
60
+
61
+ enum UserStatus {
62
+ ACTIVE
63
+ DISABLED
64
+ }
65
+
66
+ type Query {
67
+ user(id: ID!, includeDisabled: Boolean = false): User
68
+ }
69
+
70
+ type User {
71
+ id: ID!
72
+ name: String!
73
+ status: UserStatus!
74
+ createdAt: DateTime!
75
+ profile: Profile
76
+ }
77
+
78
+ type Profile {
79
+ bio: String
80
+ website: String
81
+ }
82
+ ```
83
+
84
+ 解析 SDL、选择返回字段并生成查询:
85
+
86
+ ```ts
87
+ import {
88
+ buildGraphQLQuery,
89
+ buildSelectionFields,
90
+ parseSDL,
91
+ type GraphQLOperation,
92
+ } from '@uni2c/graphqlapi4mp';
93
+
94
+ const sdl = `
95
+ scalar DateTime
96
+ enum UserStatus { ACTIVE DISABLED }
97
+
98
+ type Query {
99
+ user(id: ID!, includeDisabled: Boolean = false): User
100
+ }
101
+
102
+ type User {
103
+ id: ID!
104
+ name: String!
105
+ status: UserStatus!
106
+ createdAt: DateTime!
107
+ profile: Profile
108
+ }
109
+
110
+ type Profile {
111
+ bio: String
112
+ website: String
113
+ }
114
+ `;
115
+
116
+ const schema = parseSDL(sdl);
117
+ const sourceOperation = schema.query.user;
118
+
119
+ if (!sourceOperation) {
120
+ throw new Error('Query.user 不存在');
121
+ }
122
+
123
+ const operation: GraphQLOperation = {
124
+ ...sourceOperation,
125
+ fields: buildSelectionFields(schema, sourceOperation, {
126
+ profile: {
127
+ website: false,
128
+ },
129
+ }),
130
+ };
131
+
132
+ const query = buildGraphQLQuery(operation, 'query');
133
+ console.log(query);
134
+ ```
135
+
136
+ 输出:
137
+
138
+ ```graphql
139
+ query user($id: ID!, $includeDisabled: Boolean) {
140
+ user(
141
+ id: $id
142
+ includeDisabled: $includeDisabled
143
+ ) {
144
+ id
145
+ name
146
+ status
147
+ createdAt
148
+ profile {
149
+ bio
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ 请求时将变量单独传给 GraphQL 客户端:
156
+
157
+ ```ts
158
+ const variables = {
159
+ id: 'user-1',
160
+ includeDisabled: false,
161
+ };
162
+
163
+ await fetch('/graphql', {
164
+ method: 'POST',
165
+ headers: {
166
+ 'content-type': 'application/json',
167
+ },
168
+ body: JSON.stringify({ query, variables }),
169
+ });
170
+ ```
171
+
172
+ ## API
173
+
174
+ ### `parseSDL(sdl)`
175
+
176
+ 解析 SDL 字符串并返回 Schema 元数据。
177
+
178
+ ```ts
179
+ const schema = parseSDL(sdl);
180
+
181
+ schema.query; // Query 根字段
182
+ schema.mutation; // Mutation 根字段
183
+ schema.types; // 所有已解析的对象类型
184
+ schema.scalars; // 内置标量和自定义标量
185
+ schema.enums; // 枚举名称
186
+ ```
187
+
188
+ 返回类型:
189
+
190
+ ```ts
191
+ interface SchemaMeta {
192
+ query: Record<string, SchemaField>;
193
+ mutation: Record<string, SchemaField>;
194
+ types: Record<string, SchemaType>;
195
+ scalars: Set<string>;
196
+ enums: Set<string>;
197
+ }
198
+ ```
199
+
200
+ 字段的 `type` 会保留 GraphQL 类型修饰符,例如 `ID!`、`[User!]!`。
201
+
202
+ ### `buildSelectionFields(schema, operation, config?)`
203
+
204
+ 根据 operation 返回类型构造 selection set 字段树。
205
+
206
+ 规则:
207
+
208
+ - 标量和枚举字段默认包含
209
+ - 对象字段默认不展开
210
+ - 对象字段配置为 `true` 时展开,并包含其直接标量/枚举字段
211
+ - 对象字段配置为对象时,按该配置递归展开
212
+ - 任意字段配置为 `false` 时排除
213
+
214
+ ```ts
215
+ const fields = buildSelectionFields(schema, schema.query.user, {
216
+ name: false,
217
+ profile: {
218
+ bio: true,
219
+ website: false,
220
+ },
221
+ });
222
+ ```
223
+
224
+ 返回值类似:
225
+
226
+ ```ts
227
+ [
228
+ 'id',
229
+ 'status',
230
+ 'createdAt',
231
+ {
232
+ name: 'profile',
233
+ fields: ['bio'],
234
+ },
235
+ ]
236
+ ```
237
+
238
+ 注意:对标量字段设置 `true` 不会改变行为,因为标量字段本来就会默认包含。
239
+
240
+ ### `buildGraphQLQuery(operation, operationType?)`
241
+
242
+ 将 operation 转换为完整 GraphQL 文本。
243
+
244
+ ```ts
245
+ const query = buildGraphQLQuery(operation); // 默认为 query
246
+ const mutation = buildGraphQLQuery(operation, 'mutation');
247
+ ```
248
+
249
+ operation 的参数定义来自 SDL,生成器会同时生成变量声明和字段参数引用。它只生成查询文本,不负责提供实际变量值或发送网络请求。
250
+
251
+ ### `buildQuery(operation, operationType?)`
252
+
253
+ `buildGraphQLQuery` 的兼容别名。新代码建议使用 `buildGraphQLQuery`。
254
+
255
+ ### `unwrapType(type)`
256
+
257
+ 移除 GraphQL 列表和非空修饰符,返回基础类型名称。
258
+
259
+ ```ts
260
+ unwrapType('User'); // User
261
+ unwrapType('User!'); // User
262
+ unwrapType('[User!]!'); // User
263
+ ```
264
+
265
+ ## Mutation 示例
266
+
267
+ ```graphql
268
+ type Mutation {
269
+ updateUser(id: ID!, name: String!): User
270
+ }
271
+ ```
272
+
273
+ ```ts
274
+ const sourceOperation = schema.mutation.updateUser;
275
+ const mutation = buildGraphQLQuery(
276
+ {
277
+ ...sourceOperation,
278
+ fields: buildSelectionFields(schema, sourceOperation),
279
+ },
280
+ 'mutation',
281
+ );
282
+ ```
283
+
284
+ 输出:
285
+
286
+ ```graphql
287
+ mutation updateUser($id: ID!, $name: String!) {
288
+ updateUser(
289
+ id: $id
290
+ name: $name
291
+ ) {
292
+ id
293
+ name
294
+ status
295
+ createdAt
296
+ }
297
+ }
298
+ ```
299
+
300
+ ## 手动指定 selection set
301
+
302
+ 不使用 `buildSelectionFields` 时,也可以自行构造字段树:
303
+
304
+ ```ts
305
+ const query = buildGraphQLQuery({
306
+ name: 'user',
307
+ type: 'User',
308
+ args: { id: 'ID!' },
309
+ fields: [
310
+ 'id',
311
+ 'name',
312
+ {
313
+ name: 'profile',
314
+ fields: ['bio'],
315
+ },
316
+ ],
317
+ });
318
+ ```
319
+
320
+ ## 当前解析范围
321
+
322
+ 该库面向查询生成所需的轻量 SDL 元数据提取,并不是完整的 GraphQL 规范验证器。目前应注意:
323
+
324
+ - 根类型按常规名称 `Query` 和 `Mutation` 识别
325
+ - 解析对象 `type`、`scalar` 和 `enum`
326
+ - 支持字段参数、列表类型、非空类型、默认值与指令的跳过处理
327
+ - 不解析 `input`、`interface`、`union`、fragment 或 directive 定义为可查询对象
328
+ - 不验证字段值,也不执行 GraphQL operation
329
+
330
+ 需要完整 Schema 校验、执行或 introspection 时,应配合标准 GraphQL 实现使用。
331
+
332
+ ## 发布产物
333
+
334
+ 包通过条件导出自动选择模块格式:
335
+
336
+ - ESM:`dist/index.mjs`
337
+ - CommonJS:`dist/index.cjs`
338
+ - TypeScript 声明:`dist/index.d.ts`
339
+
340
+ 项目构建时会自动检查产物体积;超过预算或出现未登记文件时构建失败。
341
+
342
+ ## 开发
343
+
344
+ ```bash
345
+ npm install
346
+ npm run typecheck
347
+ npm test
348
+ ```
349
+
350
+ 其他命令:
351
+
352
+ ```bash
353
+ npm run build # 构建 CJS、ESM 和类型声明,并检查体积
354
+ npm run size # 检查 dist 文件及体积预算
355
+ npm run test:demo # 使用项目内大型 SDL 运行演示脚本
356
+ ```
357
+
358
+ ## License
359
+
360
+ ISC
package/dist/index.cjs CHANGED
@@ -1,315 +1,4 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region src/index.ts
3
- const STR = "\0s";
4
- const NUM = "\0n";
5
- const EMPTY_ARGS = Object.freeze(Object.create(null));
6
- const isNameStart = (c) => c === 95 || c >= 65 && c <= 90 || c >= 97 && c <= 122;
7
- const isNameChar = (c) => isNameStart(c) || c >= 48 && c <= 57;
8
- const isDigit = (c) => c >= 48 && c <= 57;
9
- const TYPE_NAME_CACHE = Object.create(null);
10
- function parseSDL(s) {
11
- let i = s.charCodeAt(0) === 65279 ? 1 : 0;
12
- const n = s.length;
13
- let look;
14
- const types = Object.create(null);
15
- const scalars = /* @__PURE__ */ new Set([
16
- "String",
17
- "Int",
18
- "Float",
19
- "Boolean",
20
- "ID"
21
- ]);
22
- const enums = /* @__PURE__ */ new Set();
23
- function scan() {
24
- for (;;) {
25
- if (i >= n) return;
26
- const c = s.charCodeAt(i);
27
- if (c === 32 || c === 9 || c === 10 || c === 13 || c === 44) {
28
- i++;
29
- continue;
30
- }
31
- if (c === 35) {
32
- while (++i < n) {
33
- const x = s.charCodeAt(i);
34
- if (x === 10 || x === 13) break;
35
- }
36
- continue;
37
- }
38
- if (c === 34 && s.charCodeAt(i + 1) === 34 && s.charCodeAt(i + 2) === 34) {
39
- i += 3;
40
- while (i < n) {
41
- if (s.charCodeAt(i) === 34 && s.charCodeAt(i + 1) === 34 && s.charCodeAt(i + 2) === 34) {
42
- i += 3;
43
- break;
44
- }
45
- i += s.charCodeAt(i) === 92 ? 2 : 1;
46
- }
47
- continue;
48
- }
49
- if (c === 34) {
50
- i++;
51
- while (i < n) {
52
- const x = s.charCodeAt(i++);
53
- if (x === 92) i++;
54
- else if (x === 34) break;
55
- }
56
- return STR;
57
- }
58
- if (isNameStart(c)) {
59
- const p = i++;
60
- while (i < n && isNameChar(s.charCodeAt(i))) i++;
61
- return s.slice(p, i);
62
- }
63
- if (isDigit(c) || c === 45 && isDigit(s.charCodeAt(i + 1))) {
64
- i++;
65
- while (i < n) {
66
- const x = s.charCodeAt(i);
67
- if (isDigit(x) || x === 46 || x === 101 || x === 69 || x === 43 || x === 45) i++;
68
- else break;
69
- }
70
- return NUM;
71
- }
72
- if (c === 46 && s.charCodeAt(i + 1) === 46 && s.charCodeAt(i + 2) === 46) {
73
- i += 3;
74
- return "...";
75
- }
76
- if (c === 123 || c === 125 || c === 40 || c === 41 || c === 91 || c === 93 || c === 58 || c === 33 || c === 61 || c === 36 || c === 64 || c === 124 || c === 38) {
77
- i++;
78
- return String.fromCharCode(c);
79
- }
80
- i++;
81
- }
82
- }
83
- const peek = () => look ??= scan();
84
- const take = () => {
85
- const v = look ?? scan();
86
- look = void 0;
87
- return v;
88
- };
89
- const eat = (v) => {
90
- if (peek() !== v) return false;
91
- take();
92
- return true;
93
- };
94
- const need = (v) => {
95
- const x = take();
96
- if (x !== v) throw new Error(`parseSDL: expected "${v}", got "${x ?? "EOF"}"`);
97
- };
98
- const needName = () => {
99
- const x = take();
100
- if (!x || x === STR || x === NUM || !isNameStart(x.charCodeAt(0))) throw new Error(`parseSDL: expected NAME, got "${x ?? "EOF"}"`);
101
- return x;
102
- };
103
- function typeRef() {
104
- if (eat("[")) {
105
- let v = `[${typeRef()}]`;
106
- need("]");
107
- if (eat("!")) v += "!";
108
- return v;
109
- }
110
- let v = needName();
111
- if (eat("!")) v += "!";
112
- return v;
113
- }
114
- function skipValue() {
115
- if (eat("[")) {
116
- while (peek() && peek() !== "]") skipValue();
117
- need("]");
118
- return;
119
- }
120
- if (eat("{")) {
121
- while (peek() && peek() !== "}") {
122
- needName();
123
- need(":");
124
- skipValue();
125
- }
126
- need("}");
127
- return;
128
- }
129
- take();
130
- }
131
- function skipDirectives() {
132
- while (eat("@")) {
133
- needName();
134
- if (!eat("(")) continue;
135
- let d = 1;
136
- while (d && peek()) if (eat("(")) d++;
137
- else if (eat(")")) d--;
138
- else take();
139
- }
140
- }
141
- function args() {
142
- if (!eat("(")) return EMPTY_ARGS;
143
- const out = Object.create(null);
144
- while (peek() && peek() !== ")") {
145
- const k = needName();
146
- need(":");
147
- out[k] = typeRef();
148
- if (eat("=")) skipValue();
149
- skipDirectives();
150
- }
151
- need(")");
152
- return out;
153
- }
154
- function fields() {
155
- const out = Object.create(null);
156
- need("{");
157
- while (peek() && peek() !== "}") {
158
- const name = needName();
159
- const a = args();
160
- need(":");
161
- const type = typeRef();
162
- skipDirectives();
163
- out[name] = {
164
- name,
165
- args: a,
166
- type
167
- };
168
- }
169
- need("}");
170
- return out;
171
- }
172
- function objectType() {
173
- const name = needName();
174
- if (eat("implements")) {
175
- eat("&");
176
- while (peek() && peek() !== "{") take();
177
- }
178
- skipDirectives();
179
- if (peek() !== "{") return;
180
- types[name] = {
181
- name,
182
- fields: fields()
183
- };
184
- }
185
- function scalar() {
186
- scalars.add(needName());
187
- skipDirectives();
188
- }
189
- function enumType() {
190
- enums.add(needName());
191
- skipDirectives();
192
- if (!eat("{")) return;
193
- while (peek() && peek() !== "}") {
194
- take();
195
- skipDirectives();
196
- }
197
- need("}");
198
- }
199
- while (peek()) switch (take()) {
200
- case "type":
201
- objectType();
202
- break;
203
- case "scalar":
204
- scalar();
205
- break;
206
- case "enum": enumType();
207
- }
208
- return {
209
- query: types.Query?.fields || {},
210
- mutation: types.Mutation?.fields || {},
211
- types,
212
- scalars,
213
- enums
214
- };
215
- }
216
- function unwrapType(type) {
217
- let a = 0;
218
- let b = type.length;
219
- const first = type.charCodeAt(0);
220
- const last = type.charCodeAt(b - 1);
221
- if (first !== 91 && first !== 33 && last !== 93 && last !== 33) return type;
222
- const cached = TYPE_NAME_CACHE[type];
223
- if (cached) return cached;
224
- while (a < b) {
225
- const c = type.charCodeAt(a);
226
- if (c !== 91 && c !== 33) break;
227
- a++;
228
- }
229
- while (b > a) {
230
- const c = type.charCodeAt(b - 1);
231
- if (c !== 93 && c !== 33) break;
232
- b--;
233
- }
234
- const name = type.slice(a, b);
235
- TYPE_NAME_CACHE[type] = name;
236
- return name;
237
- }
238
- /**
239
- * 根据 SDL Schema 为一个 Query/Mutation 字段生成 selection-set。
240
- *
241
- * 普通 scalar/enum 字段默认包含;对象字段仅在 config 中显式声明时展开。
242
- * 该函数只生成字段树,不修改传入的 operation。
243
- */
244
- function buildSelectionFields(schema, operation, config = {}) {
245
- const schemaType = schema.types[unwrapType(operation.type)];
246
- if (!schemaType) return [];
247
- const result = [];
248
- const fields = schemaType.fields;
249
- for (const fieldName in fields) {
250
- const fieldInfo = fields[fieldName];
251
- const fieldConfig = config[fieldName];
252
- if (fieldConfig === false) continue;
253
- if (!schema.types[unwrapType(fieldInfo.type)]) {
254
- result.push(fieldName);
255
- continue;
256
- }
257
- if (fieldConfig === void 0) continue;
258
- result.push({
259
- name: fieldName,
260
- fields: buildSelectionFields(schema, fieldInfo, fieldConfig === true ? {} : fieldConfig)
261
- });
262
- }
263
- return result;
264
- }
265
- /** 将递归字段树渲染为 GraphQL selection-set 文本。 */
266
- function renderSelectionFields(fields, indent = " ") {
267
- let out = "";
268
- for (const field of fields) {
269
- if (out) out += "\n";
270
- if (typeof field === "string") {
271
- out += indent + field;
272
- continue;
273
- }
274
- const children = renderSelectionFields(field.fields, indent + " ");
275
- out += children ? `${indent}${field.name} {\n${children}\n${indent}}` : indent + field.name;
276
- }
277
- return out;
278
- }
279
- /**
280
- * 根据解析后的操作描述生成完整 GraphQL 文本。
281
- *
282
- * 参数定义全部来自 SDL,例如:
283
- * query Company($id: Int, $pageNo: Int) { ... }
284
- *
285
- * operation.fields 可通过 buildSelectionFields() 生成,也可以自行传入。
286
- */
287
- function buildGraphQLQuery(operation, operationType = "query") {
288
- const { name, args = EMPTY_ARGS, fields = [] } = operation;
289
- let variableDefinitions = "";
290
- let argumentsText = "";
291
- for (const argName in args) {
292
- if (variableDefinitions) variableDefinitions += ", ";
293
- variableDefinitions += `$${argName}: ${args[argName]}`;
294
- argumentsText += `${argumentsText ? "\n" : ""} ${argName}: $${argName}`;
295
- }
296
- const selectionText = renderSelectionFields(fields);
297
- let gql = `${operationType} ${name}`;
298
- if (variableDefinitions) gql += `(${variableDefinitions})`;
299
- gql += ` {\n ${name}`;
300
- if (argumentsText) gql += `(\n${argumentsText}\n )`;
301
- if (selectionText) gql += ` {\n${selectionText}\n }`;
302
- return gql + "\n}";
303
- }
304
- /**
305
- * 向后兼容旧名称。新代码建议使用 buildGraphQLQuery()。
306
- */
307
- const buildQuery = buildGraphQLQuery;
308
- //#endregion
309
- exports.buildGraphQLQuery = buildGraphQLQuery;
310
- exports.buildQuery = buildQuery;
311
- exports.buildSelectionFields = buildSelectionFields;
312
- exports.parseSDL = parseSDL;
313
- exports.unwrapType = unwrapType;
314
-
315
- //# sourceMappingURL=index.cjs.map
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=Object.freeze(Object.create(null)),t=e=>e===95||e>=65&&e<=90||e>=97&&e<=122,n=e=>t(e)||e>=48&&e<=57,r=e=>e>=48&&e<=57,i=Object.create(null);function a(i){let a=+(i.charCodeAt(0)===65279),o=i.length,s,c=Object.create(null),l=new Set([`String`,`Int`,`Float`,`Boolean`,`ID`]),u=new Set;function d(){for(;;){if(a>=o)return;let e=i.charCodeAt(a);if(e===32||e===9||e===10||e===13||e===44){a++;continue}if(e===35){for(;++a<o;){let e=i.charCodeAt(a);if(e===10||e===13)break}continue}if(e===34&&i.charCodeAt(a+1)===34&&i.charCodeAt(a+2)===34){for(a+=3;a<o;){if(i.charCodeAt(a)===34&&i.charCodeAt(a+1)===34&&i.charCodeAt(a+2)===34){a+=3;break}a+=i.charCodeAt(a)===92?2:1}continue}if(e===34){for(a++;a<o;){let e=i.charCodeAt(a++);if(e===92)a++;else if(e===34)break}return`\0s`}if(t(e)){let e=a++;for(;a<o&&n(i.charCodeAt(a));)a++;return i.slice(e,a)}if(r(e)||e===45&&r(i.charCodeAt(a+1))){for(a++;a<o;){let e=i.charCodeAt(a);if(r(e)||e===46||e===101||e===69||e===43||e===45)a++;else break}return`\0n`}if(e===46&&i.charCodeAt(a+1)===46&&i.charCodeAt(a+2)===46)return a+=3,`...`;if(e===123||e===125||e===40||e===41||e===91||e===93||e===58||e===33||e===61||e===36||e===64||e===124||e===38)return a++,String.fromCharCode(e);a++}}let f=()=>s??=d(),p=()=>{let e=s??d();return s=void 0,e},m=e=>f()===e&&(p(),!0),h=e=>{let t=p();if(t!==e)throw Error(`parseSDL: expected "${e}", got "${t??`EOF`}"`)},g=()=>{let e=p();if(!e||e===`\0s`||e===`\0n`||!t(e.charCodeAt(0)))throw Error(`parseSDL: expected NAME, got "${e??`EOF`}"`);return e};function _(){if(m(`[`)){let e=`[${_()}]`;return h(`]`),m(`!`)&&(e+=`!`),e}let e=g();return m(`!`)&&(e+=`!`),e}function v(){if(m(`[`)){for(;f()&&f()!==`]`;)v();h(`]`);return}if(m(`{`)){for(;f()&&f()!==`}`;)g(),h(`:`),v();h(`}`);return}p()}function y(){for(;m(`@`);){if(g(),!m(`(`))continue;let e=1;for(;e&&f();)m(`(`)?e++:m(`)`)?e--:p()}}function b(){if(!m(`(`))return e;let t=Object.create(null);for(;f()&&f()!==`)`;){let e=g();h(`:`),t[e]=_(),m(`=`)&&v(),y()}return h(`)`),t}function x(){let e=Object.create(null);for(h(`{`);f()&&f()!==`}`;){let t=g(),n=b();h(`:`);let r=_();y(),e[t]={name:t,args:n,type:r}}return h(`}`),e}function S(){let e=g();if(m(`implements`))for(m(`&`);f()&&f()!==`{`;)p();y(),f()===`{`&&(c[e]={name:e,fields:x()})}function C(){l.add(g()),y()}function w(){if(u.add(g()),y(),m(`{`)){for(;f()&&f()!==`}`;)p(),y();h(`}`)}}for(;f();)switch(p()){case`type`:S();break;case`scalar`:C();break;case`enum`:w()}return{query:c.Query?.fields||{},mutation:c.Mutation?.fields||{},types:c,scalars:l,enums:u}}function o(e){let t=0,n=e.length,r=e.charCodeAt(0),a=e.charCodeAt(n-1);if(r!==91&&r!==33&&a!==93&&a!==33)return e;let o=i[e];if(o)return o;for(;t<n;){let n=e.charCodeAt(t);if(n!==91&&n!==33)break;t++}for(;n>t;){let t=e.charCodeAt(n-1);if(t!==93&&t!==33)break;n--}let s=e.slice(t,n);return i[e]=s,s}function s(e,t,n={}){let r=e.types[o(t.type)];if(!r)return[];let i=[],a=r.fields;for(let t in a){let r=a[t],c=n[t];if(c!==!1){if(!e.types[o(r.type)]){i.push(t);continue}c!==void 0&&i.push({name:t,fields:s(e,r,c===!0?{}:c)})}}return i}function c(e,t=` `){let n=``;for(let r of e){if(n&&(n+=`
2
+ `),typeof r==`string`){n+=t+r;continue}let e=c(r.fields,t+` `);n+=e?`${t}${r.name} {\n${e}\n${t}}`:t+r.name}return n}function l(t,n=`query`){let{name:r,args:i=e,fields:a=[]}=t,o=``,s=``;for(let e in i)o&&(o+=`, `),o+=`$${e}: ${i[e]}`,s+=`${s?`
3
+ `:``} ${e}: $${e}`;let l=c(a),u=`${n} ${r}`;return o&&(u+=`(${o})`),u+=` {\n ${r}`,s&&(u+=`(\n${s}\n )`),l&&(u+=` {\n${l}\n }`),u+`
4
+ }`}const u=l;exports.buildGraphQLQuery=l,exports.buildQuery=u,exports.buildSelectionFields=s,exports.parseSDL=a,exports.unwrapType=o;
package/dist/index.mjs CHANGED
@@ -1,310 +1,4 @@
1
- //#region src/index.ts
2
- const STR = "\0s";
3
- const NUM = "\0n";
4
- const EMPTY_ARGS = Object.freeze(Object.create(null));
5
- const isNameStart = (c) => c === 95 || c >= 65 && c <= 90 || c >= 97 && c <= 122;
6
- const isNameChar = (c) => isNameStart(c) || c >= 48 && c <= 57;
7
- const isDigit = (c) => c >= 48 && c <= 57;
8
- const TYPE_NAME_CACHE = Object.create(null);
9
- function parseSDL(s) {
10
- let i = s.charCodeAt(0) === 65279 ? 1 : 0;
11
- const n = s.length;
12
- let look;
13
- const types = Object.create(null);
14
- const scalars = /* @__PURE__ */ new Set([
15
- "String",
16
- "Int",
17
- "Float",
18
- "Boolean",
19
- "ID"
20
- ]);
21
- const enums = /* @__PURE__ */ new Set();
22
- function scan() {
23
- for (;;) {
24
- if (i >= n) return;
25
- const c = s.charCodeAt(i);
26
- if (c === 32 || c === 9 || c === 10 || c === 13 || c === 44) {
27
- i++;
28
- continue;
29
- }
30
- if (c === 35) {
31
- while (++i < n) {
32
- const x = s.charCodeAt(i);
33
- if (x === 10 || x === 13) break;
34
- }
35
- continue;
36
- }
37
- if (c === 34 && s.charCodeAt(i + 1) === 34 && s.charCodeAt(i + 2) === 34) {
38
- i += 3;
39
- while (i < n) {
40
- if (s.charCodeAt(i) === 34 && s.charCodeAt(i + 1) === 34 && s.charCodeAt(i + 2) === 34) {
41
- i += 3;
42
- break;
43
- }
44
- i += s.charCodeAt(i) === 92 ? 2 : 1;
45
- }
46
- continue;
47
- }
48
- if (c === 34) {
49
- i++;
50
- while (i < n) {
51
- const x = s.charCodeAt(i++);
52
- if (x === 92) i++;
53
- else if (x === 34) break;
54
- }
55
- return STR;
56
- }
57
- if (isNameStart(c)) {
58
- const p = i++;
59
- while (i < n && isNameChar(s.charCodeAt(i))) i++;
60
- return s.slice(p, i);
61
- }
62
- if (isDigit(c) || c === 45 && isDigit(s.charCodeAt(i + 1))) {
63
- i++;
64
- while (i < n) {
65
- const x = s.charCodeAt(i);
66
- if (isDigit(x) || x === 46 || x === 101 || x === 69 || x === 43 || x === 45) i++;
67
- else break;
68
- }
69
- return NUM;
70
- }
71
- if (c === 46 && s.charCodeAt(i + 1) === 46 && s.charCodeAt(i + 2) === 46) {
72
- i += 3;
73
- return "...";
74
- }
75
- if (c === 123 || c === 125 || c === 40 || c === 41 || c === 91 || c === 93 || c === 58 || c === 33 || c === 61 || c === 36 || c === 64 || c === 124 || c === 38) {
76
- i++;
77
- return String.fromCharCode(c);
78
- }
79
- i++;
80
- }
81
- }
82
- const peek = () => look ??= scan();
83
- const take = () => {
84
- const v = look ?? scan();
85
- look = void 0;
86
- return v;
87
- };
88
- const eat = (v) => {
89
- if (peek() !== v) return false;
90
- take();
91
- return true;
92
- };
93
- const need = (v) => {
94
- const x = take();
95
- if (x !== v) throw new Error(`parseSDL: expected "${v}", got "${x ?? "EOF"}"`);
96
- };
97
- const needName = () => {
98
- const x = take();
99
- if (!x || x === STR || x === NUM || !isNameStart(x.charCodeAt(0))) throw new Error(`parseSDL: expected NAME, got "${x ?? "EOF"}"`);
100
- return x;
101
- };
102
- function typeRef() {
103
- if (eat("[")) {
104
- let v = `[${typeRef()}]`;
105
- need("]");
106
- if (eat("!")) v += "!";
107
- return v;
108
- }
109
- let v = needName();
110
- if (eat("!")) v += "!";
111
- return v;
112
- }
113
- function skipValue() {
114
- if (eat("[")) {
115
- while (peek() && peek() !== "]") skipValue();
116
- need("]");
117
- return;
118
- }
119
- if (eat("{")) {
120
- while (peek() && peek() !== "}") {
121
- needName();
122
- need(":");
123
- skipValue();
124
- }
125
- need("}");
126
- return;
127
- }
128
- take();
129
- }
130
- function skipDirectives() {
131
- while (eat("@")) {
132
- needName();
133
- if (!eat("(")) continue;
134
- let d = 1;
135
- while (d && peek()) if (eat("(")) d++;
136
- else if (eat(")")) d--;
137
- else take();
138
- }
139
- }
140
- function args() {
141
- if (!eat("(")) return EMPTY_ARGS;
142
- const out = Object.create(null);
143
- while (peek() && peek() !== ")") {
144
- const k = needName();
145
- need(":");
146
- out[k] = typeRef();
147
- if (eat("=")) skipValue();
148
- skipDirectives();
149
- }
150
- need(")");
151
- return out;
152
- }
153
- function fields() {
154
- const out = Object.create(null);
155
- need("{");
156
- while (peek() && peek() !== "}") {
157
- const name = needName();
158
- const a = args();
159
- need(":");
160
- const type = typeRef();
161
- skipDirectives();
162
- out[name] = {
163
- name,
164
- args: a,
165
- type
166
- };
167
- }
168
- need("}");
169
- return out;
170
- }
171
- function objectType() {
172
- const name = needName();
173
- if (eat("implements")) {
174
- eat("&");
175
- while (peek() && peek() !== "{") take();
176
- }
177
- skipDirectives();
178
- if (peek() !== "{") return;
179
- types[name] = {
180
- name,
181
- fields: fields()
182
- };
183
- }
184
- function scalar() {
185
- scalars.add(needName());
186
- skipDirectives();
187
- }
188
- function enumType() {
189
- enums.add(needName());
190
- skipDirectives();
191
- if (!eat("{")) return;
192
- while (peek() && peek() !== "}") {
193
- take();
194
- skipDirectives();
195
- }
196
- need("}");
197
- }
198
- while (peek()) switch (take()) {
199
- case "type":
200
- objectType();
201
- break;
202
- case "scalar":
203
- scalar();
204
- break;
205
- case "enum": enumType();
206
- }
207
- return {
208
- query: types.Query?.fields || {},
209
- mutation: types.Mutation?.fields || {},
210
- types,
211
- scalars,
212
- enums
213
- };
214
- }
215
- function unwrapType(type) {
216
- let a = 0;
217
- let b = type.length;
218
- const first = type.charCodeAt(0);
219
- const last = type.charCodeAt(b - 1);
220
- if (first !== 91 && first !== 33 && last !== 93 && last !== 33) return type;
221
- const cached = TYPE_NAME_CACHE[type];
222
- if (cached) return cached;
223
- while (a < b) {
224
- const c = type.charCodeAt(a);
225
- if (c !== 91 && c !== 33) break;
226
- a++;
227
- }
228
- while (b > a) {
229
- const c = type.charCodeAt(b - 1);
230
- if (c !== 93 && c !== 33) break;
231
- b--;
232
- }
233
- const name = type.slice(a, b);
234
- TYPE_NAME_CACHE[type] = name;
235
- return name;
236
- }
237
- /**
238
- * 根据 SDL Schema 为一个 Query/Mutation 字段生成 selection-set。
239
- *
240
- * 普通 scalar/enum 字段默认包含;对象字段仅在 config 中显式声明时展开。
241
- * 该函数只生成字段树,不修改传入的 operation。
242
- */
243
- function buildSelectionFields(schema, operation, config = {}) {
244
- const schemaType = schema.types[unwrapType(operation.type)];
245
- if (!schemaType) return [];
246
- const result = [];
247
- const fields = schemaType.fields;
248
- for (const fieldName in fields) {
249
- const fieldInfo = fields[fieldName];
250
- const fieldConfig = config[fieldName];
251
- if (fieldConfig === false) continue;
252
- if (!schema.types[unwrapType(fieldInfo.type)]) {
253
- result.push(fieldName);
254
- continue;
255
- }
256
- if (fieldConfig === void 0) continue;
257
- result.push({
258
- name: fieldName,
259
- fields: buildSelectionFields(schema, fieldInfo, fieldConfig === true ? {} : fieldConfig)
260
- });
261
- }
262
- return result;
263
- }
264
- /** 将递归字段树渲染为 GraphQL selection-set 文本。 */
265
- function renderSelectionFields(fields, indent = " ") {
266
- let out = "";
267
- for (const field of fields) {
268
- if (out) out += "\n";
269
- if (typeof field === "string") {
270
- out += indent + field;
271
- continue;
272
- }
273
- const children = renderSelectionFields(field.fields, indent + " ");
274
- out += children ? `${indent}${field.name} {\n${children}\n${indent}}` : indent + field.name;
275
- }
276
- return out;
277
- }
278
- /**
279
- * 根据解析后的操作描述生成完整 GraphQL 文本。
280
- *
281
- * 参数定义全部来自 SDL,例如:
282
- * query Company($id: Int, $pageNo: Int) { ... }
283
- *
284
- * operation.fields 可通过 buildSelectionFields() 生成,也可以自行传入。
285
- */
286
- function buildGraphQLQuery(operation, operationType = "query") {
287
- const { name, args = EMPTY_ARGS, fields = [] } = operation;
288
- let variableDefinitions = "";
289
- let argumentsText = "";
290
- for (const argName in args) {
291
- if (variableDefinitions) variableDefinitions += ", ";
292
- variableDefinitions += `$${argName}: ${args[argName]}`;
293
- argumentsText += `${argumentsText ? "\n" : ""} ${argName}: $${argName}`;
294
- }
295
- const selectionText = renderSelectionFields(fields);
296
- let gql = `${operationType} ${name}`;
297
- if (variableDefinitions) gql += `(${variableDefinitions})`;
298
- gql += ` {\n ${name}`;
299
- if (argumentsText) gql += `(\n${argumentsText}\n )`;
300
- if (selectionText) gql += ` {\n${selectionText}\n }`;
301
- return gql + "\n}";
302
- }
303
- /**
304
- * 向后兼容旧名称。新代码建议使用 buildGraphQLQuery()。
305
- */
306
- const buildQuery = buildGraphQLQuery;
307
- //#endregion
308
- export { buildGraphQLQuery, buildQuery, buildSelectionFields, parseSDL, unwrapType };
309
-
310
- //# sourceMappingURL=index.mjs.map
1
+ const e=Object.freeze(Object.create(null)),t=e=>e===95||e>=65&&e<=90||e>=97&&e<=122,n=e=>t(e)||e>=48&&e<=57,r=e=>e>=48&&e<=57,i=Object.create(null);function a(i){let a=+(i.charCodeAt(0)===65279),o=i.length,s,c=Object.create(null),l=new Set([`String`,`Int`,`Float`,`Boolean`,`ID`]),u=new Set;function d(){for(;;){if(a>=o)return;let e=i.charCodeAt(a);if(e===32||e===9||e===10||e===13||e===44){a++;continue}if(e===35){for(;++a<o;){let e=i.charCodeAt(a);if(e===10||e===13)break}continue}if(e===34&&i.charCodeAt(a+1)===34&&i.charCodeAt(a+2)===34){for(a+=3;a<o;){if(i.charCodeAt(a)===34&&i.charCodeAt(a+1)===34&&i.charCodeAt(a+2)===34){a+=3;break}a+=i.charCodeAt(a)===92?2:1}continue}if(e===34){for(a++;a<o;){let e=i.charCodeAt(a++);if(e===92)a++;else if(e===34)break}return`\0s`}if(t(e)){let e=a++;for(;a<o&&n(i.charCodeAt(a));)a++;return i.slice(e,a)}if(r(e)||e===45&&r(i.charCodeAt(a+1))){for(a++;a<o;){let e=i.charCodeAt(a);if(r(e)||e===46||e===101||e===69||e===43||e===45)a++;else break}return`\0n`}if(e===46&&i.charCodeAt(a+1)===46&&i.charCodeAt(a+2)===46)return a+=3,`...`;if(e===123||e===125||e===40||e===41||e===91||e===93||e===58||e===33||e===61||e===36||e===64||e===124||e===38)return a++,String.fromCharCode(e);a++}}let f=()=>s??=d(),p=()=>{let e=s??d();return s=void 0,e},m=e=>f()===e&&(p(),!0),h=e=>{let t=p();if(t!==e)throw Error(`parseSDL: expected "${e}", got "${t??`EOF`}"`)},g=()=>{let e=p();if(!e||e===`\0s`||e===`\0n`||!t(e.charCodeAt(0)))throw Error(`parseSDL: expected NAME, got "${e??`EOF`}"`);return e};function _(){if(m(`[`)){let e=`[${_()}]`;return h(`]`),m(`!`)&&(e+=`!`),e}let e=g();return m(`!`)&&(e+=`!`),e}function v(){if(m(`[`)){for(;f()&&f()!==`]`;)v();h(`]`);return}if(m(`{`)){for(;f()&&f()!==`}`;)g(),h(`:`),v();h(`}`);return}p()}function y(){for(;m(`@`);){if(g(),!m(`(`))continue;let e=1;for(;e&&f();)m(`(`)?e++:m(`)`)?e--:p()}}function b(){if(!m(`(`))return e;let t=Object.create(null);for(;f()&&f()!==`)`;){let e=g();h(`:`),t[e]=_(),m(`=`)&&v(),y()}return h(`)`),t}function x(){let e=Object.create(null);for(h(`{`);f()&&f()!==`}`;){let t=g(),n=b();h(`:`);let r=_();y(),e[t]={name:t,args:n,type:r}}return h(`}`),e}function S(){let e=g();if(m(`implements`))for(m(`&`);f()&&f()!==`{`;)p();y(),f()===`{`&&(c[e]={name:e,fields:x()})}function C(){l.add(g()),y()}function w(){if(u.add(g()),y(),m(`{`)){for(;f()&&f()!==`}`;)p(),y();h(`}`)}}for(;f();)switch(p()){case`type`:S();break;case`scalar`:C();break;case`enum`:w()}return{query:c.Query?.fields||{},mutation:c.Mutation?.fields||{},types:c,scalars:l,enums:u}}function o(e){let t=0,n=e.length,r=e.charCodeAt(0),a=e.charCodeAt(n-1);if(r!==91&&r!==33&&a!==93&&a!==33)return e;let o=i[e];if(o)return o;for(;t<n;){let n=e.charCodeAt(t);if(n!==91&&n!==33)break;t++}for(;n>t;){let t=e.charCodeAt(n-1);if(t!==93&&t!==33)break;n--}let s=e.slice(t,n);return i[e]=s,s}function s(e,t,n={}){let r=e.types[o(t.type)];if(!r)return[];let i=[],a=r.fields;for(let t in a){let r=a[t],c=n[t];if(c!==!1){if(!e.types[o(r.type)]){i.push(t);continue}c!==void 0&&i.push({name:t,fields:s(e,r,c===!0?{}:c)})}}return i}function c(e,t=` `){let n=``;for(let r of e){if(n&&(n+=`
2
+ `),typeof r==`string`){n+=t+r;continue}let e=c(r.fields,t+` `);n+=e?`${t}${r.name} {\n${e}\n${t}}`:t+r.name}return n}function l(t,n=`query`){let{name:r,args:i=e,fields:a=[]}=t,o=``,s=``;for(let e in i)o&&(o+=`, `),o+=`$${e}: ${i[e]}`,s+=`${s?`
3
+ `:``} ${e}: $${e}`;let l=c(a),u=`${n} ${r}`;return o&&(u+=`(${o})`),u+=` {\n ${r}`,s&&(u+=`(\n${s}\n )`),l&&(u+=` {\n${l}\n }`),u+`
4
+ }`}const u=l;export{l as buildGraphQLQuery,u as buildQuery,s as buildSelectionFields,a as parseSDL,o as unwrapType};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uni2c/graphqlapi4mp",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Parse GraphQL SDL and build GraphQL operation documents.",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -21,7 +21,9 @@
21
21
  "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
22
22
  "prebuild": "npm run clean",
23
23
  "build": "rolldown -c && tsc -p tsconfig.build.json",
24
+ "postbuild": "npm run size",
24
25
  "prepack": "npm run build",
26
+ "size": "node scripts/check-size.mjs",
25
27
  "test": "npm run build && node --import tsx --test test/**/*.test.ts",
26
28
  "test:demo": "tsx test.ts",
27
29
  "typecheck": "tsc --noEmit",
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export interface SchemaField {\n name: string;\n args: Record<string, string>;\n type: string;\n}\n\nexport interface SchemaType {\n name: string;\n fields: Record<string, SchemaField>;\n}\n\nexport interface SchemaMeta {\n query: Record<string, SchemaField>;\n mutation: Record<string, SchemaField>;\n types: Record<string, SchemaType>;\n scalars: Set<string>;\n enums: Set<string>;\n}\n\n/** GraphQL selection-set 中的字段;对象字段可继续递归包含子字段。 */\nexport type GraphQLSelectionField =\n | string\n | {\n name: string;\n fields: GraphQLSelectionField[];\n };\n\n/**\n * 对象字段展开配置:\n * - 未定义:普通字段默认显示,对象字段默认不展开;\n * - false:明确隐藏字段;\n * - true:展开对象,并显示其普通字段;\n * - object:按配置递归展开子对象。\n */\nexport type GraphQLSelectionConfig = {\n [fieldName: string]: boolean | GraphQLSelectionConfig;\n};\n\n/** 可直接交给查询生成器的操作描述。 */\nexport type GraphQLOperation = SchemaField & {\n fields?: GraphQLSelectionField[];\n};\n\n/** GraphQL 根操作类型。 */\nexport type GraphQLOperationType = 'query' | 'mutation';\n\nconst STR = '\\0s';\nconst NUM = '\\0n';\n// 无参数字段共用同一个只读空对象,避免大型 SDL 中产生大量空 args 对象。\nconst EMPTY_ARGS: Record<string, string> = Object.freeze(Object.create(null));\n\nconst isNameStart = (c: number) =>\n c === 95 || (c >= 65 && c <= 90) || (c >= 97 && c <= 122);\nconst isNameChar = (c: number) => isNameStart(c) || (c >= 48 && c <= 57);\nconst isDigit = (c: number) => c >= 48 && c <= 57;\n// 仅缓存 [Type] / Type! 等包装类型的解包结果;普通类型走 unwrapType 快路径。\nconst TYPE_NAME_CACHE: Record<string, string> = Object.create(null);\n\nexport function parseSDL(s: string): SchemaMeta {\n let i = s.charCodeAt(0) === 0xfeff ? 1 : 0;\n const n = s.length;\n let look: string | undefined;\n\n const types: Record<string, SchemaType> = Object.create(null);\n const scalars = new Set(['String', 'Int', 'Float', 'Boolean', 'ID']);\n const enums = new Set<string>();\n\n function scan(): string | undefined {\n for (;;) {\n if (i >= n) return;\n const c = s.charCodeAt(i);\n\n // whitespace + comma\n if (c === 32 || c === 9 || c === 10 || c === 13 || c === 44) {\n i++;\n continue;\n }\n\n // # comment\n if (c === 35) {\n while (++i < n) {\n const x = s.charCodeAt(i);\n if (x === 10 || x === 13) break;\n }\n continue;\n }\n\n // block string / description: content is irrelevant for runtime schema\n if (\n c === 34 &&\n s.charCodeAt(i + 1) === 34 &&\n s.charCodeAt(i + 2) === 34\n ) {\n i += 3;\n while (i < n) {\n if (\n s.charCodeAt(i) === 34 &&\n s.charCodeAt(i + 1) === 34 &&\n s.charCodeAt(i + 2) === 34\n ) {\n i += 3;\n break;\n }\n // escaped character\n i += s.charCodeAt(i) === 92 ? 2 : 1;\n }\n continue;\n }\n\n // normal string; its value is not needed\n if (c === 34) {\n i++;\n while (i < n) {\n const x = s.charCodeAt(i++);\n if (x === 92) i++;\n else if (x === 34) break;\n }\n return STR;\n }\n\n // name / keyword\n if (isNameStart(c)) {\n const p = i++;\n while (i < n && isNameChar(s.charCodeAt(i))) i++;\n return s.slice(p, i);\n }\n\n // number; exact value is not needed\n if (isDigit(c) || (c === 45 && isDigit(s.charCodeAt(i + 1)))) {\n i++;\n while (i < n) {\n const x = s.charCodeAt(i);\n if (\n isDigit(x) ||\n x === 46 ||\n x === 101 ||\n x === 69 ||\n x === 43 ||\n x === 45\n )\n i++;\n else break;\n }\n return NUM;\n }\n\n // spread\n if (\n c === 46 &&\n s.charCodeAt(i + 1) === 46 &&\n s.charCodeAt(i + 2) === 46\n ) {\n i += 3;\n return '...';\n }\n\n // punctuation used by SDL\n if (\n c === 123 ||\n c === 125 ||\n c === 40 ||\n c === 41 ||\n c === 91 ||\n c === 93 ||\n c === 58 ||\n c === 33 ||\n c === 61 ||\n c === 36 ||\n c === 64 ||\n c === 124 ||\n c === 38\n ) {\n i++;\n return String.fromCharCode(c);\n }\n\n i++;\n }\n }\n\n const peek = () => (look ??= scan());\n const take = () => {\n const v = look ?? scan();\n look = undefined;\n return v;\n };\n const eat = (v: string) => {\n if (peek() !== v) return false;\n take();\n return true;\n };\n const need = (v: string) => {\n const x = take();\n if (x !== v)\n throw new Error(`parseSDL: expected \"${v}\", got \"${x ?? 'EOF'}\"`);\n };\n const needName = () => {\n const x = take();\n if (!x || x === STR || x === NUM || !isNameStart(x.charCodeAt(0)))\n throw new Error(`parseSDL: expected NAME, got \"${x ?? 'EOF'}\"`);\n return x;\n };\n\n function typeRef(): string {\n if (eat('[')) {\n let v = `[${typeRef()}]`;\n need(']');\n if (eat('!')) v += '!';\n return v;\n }\n let v = needName();\n if (eat('!')) v += '!';\n return v;\n }\n\n function skipValue() {\n if (eat('[')) {\n while (peek() && peek() !== ']') skipValue();\n need(']');\n return;\n }\n if (eat('{')) {\n while (peek() && peek() !== '}') {\n needName();\n need(':');\n skipValue();\n }\n need('}');\n return;\n }\n take();\n }\n\n function skipDirectives() {\n while (eat('@')) {\n needName();\n if (!eat('(')) continue;\n let d = 1;\n while (d && peek()) {\n if (eat('(')) d++;\n else if (eat(')')) d--;\n else take();\n }\n }\n }\n\n function args(): Record<string, string> {\n if (!eat('(')) return EMPTY_ARGS;\n\n const out: Record<string, string> = Object.create(null);\n while (peek() && peek() !== ')') {\n const k = needName();\n need(':');\n out[k] = typeRef();\n if (eat('=')) skipValue();\n skipDirectives();\n }\n need(')');\n return out;\n }\n\n function fields() {\n const out: Record<string, SchemaField> = Object.create(null);\n need('{');\n while (peek() && peek() !== '}') {\n const name = needName();\n const a = args();\n need(':');\n const type = typeRef();\n skipDirectives();\n out[name] = { name, args: a, type };\n }\n need('}');\n return out;\n }\n\n function objectType() {\n const name = needName();\n if (eat('implements')) {\n eat('&');\n while (peek() && peek() !== '{') take();\n }\n skipDirectives();\n if (peek() !== '{') return;\n types[name] = { name, fields: fields() };\n }\n\n function scalar() {\n scalars.add(needName());\n skipDirectives();\n }\n\n function enumType() {\n enums.add(needName());\n skipDirectives();\n if (!eat('{')) return;\n while (peek() && peek() !== '}') {\n take();\n skipDirectives();\n }\n need('}');\n }\n\n while (peek()) {\n switch (take()) {\n case 'type':\n objectType();\n break;\n case 'scalar':\n scalar();\n break;\n case 'enum':\n enumType();\n break;\n }\n }\n\n return {\n query: types.Query?.fields || {},\n mutation: types.Mutation?.fields || {},\n types,\n scalars,\n enums,\n };\n}\n\nexport function unwrapType(type: string): string {\n let a = 0;\n let b = type.length;\n\n const first = type.charCodeAt(0);\n const last = type.charCodeAt(b - 1);\n if (first !== 91 && first !== 33 && last !== 93 && last !== 33) return type;\n\n const cached = TYPE_NAME_CACHE[type];\n if (cached) return cached;\n\n while (a < b) {\n const c = type.charCodeAt(a);\n if (c !== 91 && c !== 33) break;\n a++;\n }\n while (b > a) {\n const c = type.charCodeAt(b - 1);\n if (c !== 93 && c !== 33) break;\n b--;\n }\n const name = type.slice(a, b);\n TYPE_NAME_CACHE[type] = name;\n return name;\n}\n\n/**\n * 根据 SDL Schema 为一个 Query/Mutation 字段生成 selection-set。\n *\n * 普通 scalar/enum 字段默认包含;对象字段仅在 config 中显式声明时展开。\n * 该函数只生成字段树,不修改传入的 operation。\n */\nexport function buildSelectionFields(\n schema: SchemaMeta,\n operation: SchemaField,\n config: GraphQLSelectionConfig = {},\n): GraphQLSelectionField[] {\n const schemaType = schema.types[unwrapType(operation.type)];\n if (!schemaType) return [];\n\n const result: GraphQLSelectionField[] = [];\n const fields = schemaType.fields;\n\n for (const fieldName in fields) {\n const fieldInfo = fields[fieldName];\n const fieldConfig = config[fieldName];\n\n // false:显式隐藏;undefined:scalar 默认显示、object 默认不展开。\n if (fieldConfig === false) continue;\n\n const childType = schema.types[unwrapType(fieldInfo.type)];\n if (!childType) {\n result.push(fieldName);\n continue;\n }\n\n if (fieldConfig === undefined) continue;\n\n result.push({\n name: fieldName,\n fields: buildSelectionFields(\n schema,\n fieldInfo,\n fieldConfig === true ? {} : fieldConfig,\n ),\n });\n }\n\n return result;\n}\n\n/** 将递归字段树渲染为 GraphQL selection-set 文本。 */\nfunction renderSelectionFields(\n fields: GraphQLSelectionField[],\n indent = ' ',\n): string {\n let out = '';\n\n for (const field of fields) {\n if (out) out += '\\n';\n\n if (typeof field === 'string') {\n out += indent + field;\n continue;\n }\n\n const children = renderSelectionFields(field.fields, indent + ' ');\n out += children\n ? `${indent}${field.name} {\\n${children}\\n${indent}}`\n : indent + field.name;\n }\n\n return out;\n}\n\n/**\n * 根据解析后的操作描述生成完整 GraphQL 文本。\n *\n * 参数定义全部来自 SDL,例如:\n * query Company($id: Int, $pageNo: Int) { ... }\n *\n * operation.fields 可通过 buildSelectionFields() 生成,也可以自行传入。\n */\nexport function buildGraphQLQuery(\n operation: GraphQLOperation,\n operationType: GraphQLOperationType = 'query',\n): string {\n const { name, args = EMPTY_ARGS, fields = [] } = operation;\n\n let variableDefinitions = '';\n let argumentsText = '';\n\n for (const argName in args) {\n if (variableDefinitions) variableDefinitions += ', ';\n variableDefinitions += `$${argName}: ${args[argName]}`;\n argumentsText += `${argumentsText ? '\\n' : ''} ${argName}: $${argName}`;\n }\n\n const selectionText = renderSelectionFields(fields);\n\n let gql = `${operationType} ${name}`;\n if (variableDefinitions) gql += `(${variableDefinitions})`;\n\n gql += ` {\\n ${name}`;\n if (argumentsText) gql += `(\\n${argumentsText}\\n )`;\n if (selectionText) gql += ` {\\n${selectionText}\\n }`;\n return gql + '\\n}';\n}\n\n/**\n * 向后兼容旧名称。新代码建议使用 buildGraphQLQuery()。\n */\nexport const buildQuery = buildGraphQLQuery;\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export interface SchemaField {\n name: string;\n args: Record<string, string>;\n type: string;\n}\n\nexport interface SchemaType {\n name: string;\n fields: Record<string, SchemaField>;\n}\n\nexport interface SchemaMeta {\n query: Record<string, SchemaField>;\n mutation: Record<string, SchemaField>;\n types: Record<string, SchemaType>;\n scalars: Set<string>;\n enums: Set<string>;\n}\n\n/** GraphQL selection-set 中的字段;对象字段可继续递归包含子字段。 */\nexport type GraphQLSelectionField =\n | string\n | {\n name: string;\n fields: GraphQLSelectionField[];\n };\n\n/**\n * 对象字段展开配置:\n * - 未定义:普通字段默认显示,对象字段默认不展开;\n * - false:明确隐藏字段;\n * - true:展开对象,并显示其普通字段;\n * - object:按配置递归展开子对象。\n */\nexport type GraphQLSelectionConfig = {\n [fieldName: string]: boolean | GraphQLSelectionConfig;\n};\n\n/** 可直接交给查询生成器的操作描述。 */\nexport type GraphQLOperation = SchemaField & {\n fields?: GraphQLSelectionField[];\n};\n\n/** GraphQL 根操作类型。 */\nexport type GraphQLOperationType = 'query' | 'mutation';\n\nconst STR = '\\0s';\nconst NUM = '\\0n';\n// 无参数字段共用同一个只读空对象,避免大型 SDL 中产生大量空 args 对象。\nconst EMPTY_ARGS: Record<string, string> = Object.freeze(Object.create(null));\n\nconst isNameStart = (c: number) =>\n c === 95 || (c >= 65 && c <= 90) || (c >= 97 && c <= 122);\nconst isNameChar = (c: number) => isNameStart(c) || (c >= 48 && c <= 57);\nconst isDigit = (c: number) => c >= 48 && c <= 57;\n// 仅缓存 [Type] / Type! 等包装类型的解包结果;普通类型走 unwrapType 快路径。\nconst TYPE_NAME_CACHE: Record<string, string> = Object.create(null);\n\nexport function parseSDL(s: string): SchemaMeta {\n let i = s.charCodeAt(0) === 0xfeff ? 1 : 0;\n const n = s.length;\n let look: string | undefined;\n\n const types: Record<string, SchemaType> = Object.create(null);\n const scalars = new Set(['String', 'Int', 'Float', 'Boolean', 'ID']);\n const enums = new Set<string>();\n\n function scan(): string | undefined {\n for (;;) {\n if (i >= n) return;\n const c = s.charCodeAt(i);\n\n // whitespace + comma\n if (c === 32 || c === 9 || c === 10 || c === 13 || c === 44) {\n i++;\n continue;\n }\n\n // # comment\n if (c === 35) {\n while (++i < n) {\n const x = s.charCodeAt(i);\n if (x === 10 || x === 13) break;\n }\n continue;\n }\n\n // block string / description: content is irrelevant for runtime schema\n if (\n c === 34 &&\n s.charCodeAt(i + 1) === 34 &&\n s.charCodeAt(i + 2) === 34\n ) {\n i += 3;\n while (i < n) {\n if (\n s.charCodeAt(i) === 34 &&\n s.charCodeAt(i + 1) === 34 &&\n s.charCodeAt(i + 2) === 34\n ) {\n i += 3;\n break;\n }\n // escaped character\n i += s.charCodeAt(i) === 92 ? 2 : 1;\n }\n continue;\n }\n\n // normal string; its value is not needed\n if (c === 34) {\n i++;\n while (i < n) {\n const x = s.charCodeAt(i++);\n if (x === 92) i++;\n else if (x === 34) break;\n }\n return STR;\n }\n\n // name / keyword\n if (isNameStart(c)) {\n const p = i++;\n while (i < n && isNameChar(s.charCodeAt(i))) i++;\n return s.slice(p, i);\n }\n\n // number; exact value is not needed\n if (isDigit(c) || (c === 45 && isDigit(s.charCodeAt(i + 1)))) {\n i++;\n while (i < n) {\n const x = s.charCodeAt(i);\n if (\n isDigit(x) ||\n x === 46 ||\n x === 101 ||\n x === 69 ||\n x === 43 ||\n x === 45\n )\n i++;\n else break;\n }\n return NUM;\n }\n\n // spread\n if (\n c === 46 &&\n s.charCodeAt(i + 1) === 46 &&\n s.charCodeAt(i + 2) === 46\n ) {\n i += 3;\n return '...';\n }\n\n // punctuation used by SDL\n if (\n c === 123 ||\n c === 125 ||\n c === 40 ||\n c === 41 ||\n c === 91 ||\n c === 93 ||\n c === 58 ||\n c === 33 ||\n c === 61 ||\n c === 36 ||\n c === 64 ||\n c === 124 ||\n c === 38\n ) {\n i++;\n return String.fromCharCode(c);\n }\n\n i++;\n }\n }\n\n const peek = () => (look ??= scan());\n const take = () => {\n const v = look ?? scan();\n look = undefined;\n return v;\n };\n const eat = (v: string) => {\n if (peek() !== v) return false;\n take();\n return true;\n };\n const need = (v: string) => {\n const x = take();\n if (x !== v)\n throw new Error(`parseSDL: expected \"${v}\", got \"${x ?? 'EOF'}\"`);\n };\n const needName = () => {\n const x = take();\n if (!x || x === STR || x === NUM || !isNameStart(x.charCodeAt(0)))\n throw new Error(`parseSDL: expected NAME, got \"${x ?? 'EOF'}\"`);\n return x;\n };\n\n function typeRef(): string {\n if (eat('[')) {\n let v = `[${typeRef()}]`;\n need(']');\n if (eat('!')) v += '!';\n return v;\n }\n let v = needName();\n if (eat('!')) v += '!';\n return v;\n }\n\n function skipValue() {\n if (eat('[')) {\n while (peek() && peek() !== ']') skipValue();\n need(']');\n return;\n }\n if (eat('{')) {\n while (peek() && peek() !== '}') {\n needName();\n need(':');\n skipValue();\n }\n need('}');\n return;\n }\n take();\n }\n\n function skipDirectives() {\n while (eat('@')) {\n needName();\n if (!eat('(')) continue;\n let d = 1;\n while (d && peek()) {\n if (eat('(')) d++;\n else if (eat(')')) d--;\n else take();\n }\n }\n }\n\n function args(): Record<string, string> {\n if (!eat('(')) return EMPTY_ARGS;\n\n const out: Record<string, string> = Object.create(null);\n while (peek() && peek() !== ')') {\n const k = needName();\n need(':');\n out[k] = typeRef();\n if (eat('=')) skipValue();\n skipDirectives();\n }\n need(')');\n return out;\n }\n\n function fields() {\n const out: Record<string, SchemaField> = Object.create(null);\n need('{');\n while (peek() && peek() !== '}') {\n const name = needName();\n const a = args();\n need(':');\n const type = typeRef();\n skipDirectives();\n out[name] = { name, args: a, type };\n }\n need('}');\n return out;\n }\n\n function objectType() {\n const name = needName();\n if (eat('implements')) {\n eat('&');\n while (peek() && peek() !== '{') take();\n }\n skipDirectives();\n if (peek() !== '{') return;\n types[name] = { name, fields: fields() };\n }\n\n function scalar() {\n scalars.add(needName());\n skipDirectives();\n }\n\n function enumType() {\n enums.add(needName());\n skipDirectives();\n if (!eat('{')) return;\n while (peek() && peek() !== '}') {\n take();\n skipDirectives();\n }\n need('}');\n }\n\n while (peek()) {\n switch (take()) {\n case 'type':\n objectType();\n break;\n case 'scalar':\n scalar();\n break;\n case 'enum':\n enumType();\n break;\n }\n }\n\n return {\n query: types.Query?.fields || {},\n mutation: types.Mutation?.fields || {},\n types,\n scalars,\n enums,\n };\n}\n\nexport function unwrapType(type: string): string {\n let a = 0;\n let b = type.length;\n\n const first = type.charCodeAt(0);\n const last = type.charCodeAt(b - 1);\n if (first !== 91 && first !== 33 && last !== 93 && last !== 33) return type;\n\n const cached = TYPE_NAME_CACHE[type];\n if (cached) return cached;\n\n while (a < b) {\n const c = type.charCodeAt(a);\n if (c !== 91 && c !== 33) break;\n a++;\n }\n while (b > a) {\n const c = type.charCodeAt(b - 1);\n if (c !== 93 && c !== 33) break;\n b--;\n }\n const name = type.slice(a, b);\n TYPE_NAME_CACHE[type] = name;\n return name;\n}\n\n/**\n * 根据 SDL Schema 为一个 Query/Mutation 字段生成 selection-set。\n *\n * 普通 scalar/enum 字段默认包含;对象字段仅在 config 中显式声明时展开。\n * 该函数只生成字段树,不修改传入的 operation。\n */\nexport function buildSelectionFields(\n schema: SchemaMeta,\n operation: SchemaField,\n config: GraphQLSelectionConfig = {},\n): GraphQLSelectionField[] {\n const schemaType = schema.types[unwrapType(operation.type)];\n if (!schemaType) return [];\n\n const result: GraphQLSelectionField[] = [];\n const fields = schemaType.fields;\n\n for (const fieldName in fields) {\n const fieldInfo = fields[fieldName];\n const fieldConfig = config[fieldName];\n\n // false:显式隐藏;undefined:scalar 默认显示、object 默认不展开。\n if (fieldConfig === false) continue;\n\n const childType = schema.types[unwrapType(fieldInfo.type)];\n if (!childType) {\n result.push(fieldName);\n continue;\n }\n\n if (fieldConfig === undefined) continue;\n\n result.push({\n name: fieldName,\n fields: buildSelectionFields(\n schema,\n fieldInfo,\n fieldConfig === true ? {} : fieldConfig,\n ),\n });\n }\n\n return result;\n}\n\n/** 将递归字段树渲染为 GraphQL selection-set 文本。 */\nfunction renderSelectionFields(\n fields: GraphQLSelectionField[],\n indent = ' ',\n): string {\n let out = '';\n\n for (const field of fields) {\n if (out) out += '\\n';\n\n if (typeof field === 'string') {\n out += indent + field;\n continue;\n }\n\n const children = renderSelectionFields(field.fields, indent + ' ');\n out += children\n ? `${indent}${field.name} {\\n${children}\\n${indent}}`\n : indent + field.name;\n }\n\n return out;\n}\n\n/**\n * 根据解析后的操作描述生成完整 GraphQL 文本。\n *\n * 参数定义全部来自 SDL,例如:\n * query Company($id: Int, $pageNo: Int) { ... }\n *\n * operation.fields 可通过 buildSelectionFields() 生成,也可以自行传入。\n */\nexport function buildGraphQLQuery(\n operation: GraphQLOperation,\n operationType: GraphQLOperationType = 'query',\n): string {\n const { name, args = EMPTY_ARGS, fields = [] } = operation;\n\n let variableDefinitions = '';\n let argumentsText = '';\n\n for (const argName in args) {\n if (variableDefinitions) variableDefinitions += ', ';\n variableDefinitions += `$${argName}: ${args[argName]}`;\n argumentsText += `${argumentsText ? '\\n' : ''} ${argName}: $${argName}`;\n }\n\n const selectionText = renderSelectionFields(fields);\n\n let gql = `${operationType} ${name}`;\n if (variableDefinitions) gql += `(${variableDefinitions})`;\n\n gql += ` {\\n ${name}`;\n if (argumentsText) gql += `(\\n${argumentsText}\\n )`;\n if (selectionText) gql += ` {\\n${selectionText}\\n }`;\n return gql + '\\n}';\n}\n\n/**\n * 向后兼容旧名称。新代码建议使用 buildGraphQLQuery()。\n */\nexport const buildQuery = buildGraphQLQuery;\n"],"mappings":"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"}