@uni2c/graphqlapi4mp 1.0.3 → 1.0.5
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 +360 -0
- package/package.json +24 -5
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/package.json
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uni2c/graphqlapi4mp",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "Lightweight GraphQL SDL parser and query builder for TypeScript, Node.js, browsers, and mini programs.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"graphql",
|
|
7
|
+
"graphql-sdl",
|
|
8
|
+
"sdl-parser",
|
|
9
|
+
"graphql-query-builder",
|
|
10
|
+
"query-builder",
|
|
11
|
+
"graphql-query",
|
|
12
|
+
"graphql-mutation",
|
|
13
|
+
"typescript",
|
|
14
|
+
"mini-program",
|
|
15
|
+
"wechat-miniprogram",
|
|
16
|
+
"esm",
|
|
17
|
+
"commonjs"
|
|
18
|
+
],
|
|
19
|
+
"author": "momoca",
|
|
20
|
+
"license": "ISC",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=16.0.0"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
5
27
|
"main": "./dist/index.cjs",
|
|
6
28
|
"module": "./dist/index.mjs",
|
|
7
29
|
"types": "./dist/index.d.ts",
|
|
@@ -32,9 +54,6 @@
|
|
|
32
54
|
"version:minor": "npm version minor",
|
|
33
55
|
"version:major": "npm version major"
|
|
34
56
|
},
|
|
35
|
-
"keywords": [],
|
|
36
|
-
"author": "",
|
|
37
|
-
"license": "ISC",
|
|
38
57
|
"type": "commonjs",
|
|
39
58
|
"devDependencies": {
|
|
40
59
|
"@types/node": "^26.4.0",
|