@smartive/graphql-magic 7.0.1 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -3
- package/README.md +8 -15
- package/dist/bin/gqm.cjs +1812 -0
- package/dist/cjs/index.cjs +568 -30
- package/dist/esm/bin/gqm.d.ts +2 -0
- package/dist/esm/bin/gqm.js +121 -0
- package/dist/esm/bin/gqm.js.map +1 -0
- package/dist/esm/client/mutations.d.ts +2 -2
- package/dist/esm/client/mutations.js +2 -1
- package/dist/esm/client/mutations.js.map +1 -1
- package/dist/esm/db/generate.js +7 -7
- package/dist/esm/db/generate.js.map +1 -1
- package/dist/esm/gqm/codegen.d.ts +2 -0
- package/dist/esm/gqm/codegen.js +46 -0
- package/dist/esm/gqm/codegen.js.map +1 -0
- package/dist/esm/gqm/index.d.ts +9 -0
- package/dist/esm/gqm/index.js +11 -0
- package/dist/esm/gqm/index.js.map +1 -0
- package/dist/esm/gqm/parse-knexfile.d.ts +2 -0
- package/dist/esm/gqm/parse-knexfile.js +19 -0
- package/dist/esm/gqm/parse-knexfile.js.map +1 -0
- package/dist/esm/gqm/parse-models.d.ts +2 -0
- package/dist/esm/gqm/parse-models.js +19 -0
- package/dist/esm/gqm/parse-models.js.map +1 -0
- package/dist/esm/gqm/readline.d.ts +1 -0
- package/dist/esm/gqm/readline.js +14 -0
- package/dist/esm/gqm/readline.js.map +1 -0
- package/dist/esm/gqm/settings.d.ts +9 -0
- package/dist/esm/gqm/settings.js +98 -0
- package/dist/esm/gqm/settings.js.map +1 -0
- package/dist/esm/gqm/static-eval.d.ts +3 -0
- package/dist/esm/gqm/static-eval.js +188 -0
- package/dist/esm/gqm/static-eval.js.map +1 -0
- package/dist/esm/gqm/templates.d.ts +4 -0
- package/dist/esm/gqm/templates.js +62 -0
- package/dist/esm/gqm/templates.js.map +1 -0
- package/dist/esm/gqm/utils.d.ts +2 -0
- package/dist/esm/gqm/utils.js +22 -0
- package/dist/esm/gqm/utils.js.map +1 -0
- package/dist/esm/gqm/visitor.d.ts +8 -0
- package/dist/esm/gqm/visitor.js +15 -0
- package/dist/esm/gqm/visitor.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/migrations/generate.d.ts +1 -0
- package/dist/esm/migrations/generate.js +16 -5
- package/dist/esm/migrations/generate.js.map +1 -1
- package/dist/esm/models/models.d.ts +16 -10
- package/dist/esm/models/utils.d.ts +17 -9
- package/dist/esm/models/utils.js +6 -5
- package/dist/esm/models/utils.js.map +1 -1
- package/dist/esm/resolvers/node.js +2 -2
- package/dist/esm/resolvers/node.js.map +1 -1
- package/dist/esm/resolvers/resolver.js +1 -1
- package/dist/esm/resolvers/resolver.js.map +1 -1
- package/dist/esm/schema/generate.js +16 -4
- package/dist/esm/schema/generate.js.map +1 -1
- package/package.json +18 -5
- package/src/bin/gqm.ts +146 -0
- package/src/client/mutations.ts +4 -3
- package/src/db/generate.ts +7 -7
- package/src/gqm/codegen.ts +47 -0
- package/src/gqm/index.ts +11 -0
- package/src/gqm/parse-knexfile.ts +21 -0
- package/src/gqm/parse-models.ts +24 -0
- package/src/gqm/readline.ts +15 -0
- package/src/gqm/settings.ts +112 -0
- package/src/gqm/static-eval.ts +203 -0
- package/src/gqm/templates.ts +64 -0
- package/src/gqm/utils.ts +23 -0
- package/src/gqm/visitor.ts +29 -0
- package/src/index.ts +1 -0
- package/src/migrations/generate.ts +18 -5
- package/src/models/models.ts +12 -7
- package/src/models/utils.ts +11 -8
- package/src/resolvers/node.ts +2 -2
- package/src/resolvers/resolver.ts +1 -1
- package/src/schema/generate.ts +17 -4
- package/tests/utils/generate-migration.ts +2 -13
- package/tests/utils/models.ts +4 -4
package/dist/bin/gqm.cjs
ADDED
|
@@ -0,0 +1,1812 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// src/bin/gqm.ts
|
|
26
|
+
var import_commander = require("commander");
|
|
27
|
+
var import_dotenv = require("dotenv");
|
|
28
|
+
var import_knex = __toESM(require("knex"), 1);
|
|
29
|
+
var import_simple_git = require("simple-git");
|
|
30
|
+
|
|
31
|
+
// src/api/execute.ts
|
|
32
|
+
var import_schema = require("@graphql-tools/schema");
|
|
33
|
+
var import_graphql = require("graphql");
|
|
34
|
+
|
|
35
|
+
// src/client/mutations.ts
|
|
36
|
+
var import_upperCase = __toESM(require("lodash/upperCase"), 1);
|
|
37
|
+
var constantCase = (str) => (0, import_upperCase.default)(str).replace(/ /g, "_");
|
|
38
|
+
var generateMutations = (models) => {
|
|
39
|
+
const parts = [];
|
|
40
|
+
for (const { name: name2, creatable, updatable, deletable } of models.filter(isEntityModel)) {
|
|
41
|
+
if (creatable) {
|
|
42
|
+
parts.push(
|
|
43
|
+
`export const CREATE_${constantCase(
|
|
44
|
+
name2
|
|
45
|
+
)} = gql\`
|
|
46
|
+
mutation Create${name2}Mutation($data: Create${name2}!) {
|
|
47
|
+
create${name2}(data: $data) { id }
|
|
48
|
+
}
|
|
49
|
+
\`;`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
if (updatable) {
|
|
53
|
+
parts.push(
|
|
54
|
+
`export const UPDATE_${constantCase(
|
|
55
|
+
name2
|
|
56
|
+
)} = gql\`
|
|
57
|
+
mutation Update${name2}Mutation($id: ID!, $data: Update${name2}!) {
|
|
58
|
+
update${name2}(where: { id: $id }, data: $data) { id }
|
|
59
|
+
}
|
|
60
|
+
\`;`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (deletable) {
|
|
64
|
+
parts.push(
|
|
65
|
+
`export const DELETE_${constantCase(
|
|
66
|
+
name2
|
|
67
|
+
)} = gql\`
|
|
68
|
+
mutation Delete${name2}Mutation($id: ID!) {
|
|
69
|
+
delete${name2}(where: { id: $id })
|
|
70
|
+
}
|
|
71
|
+
\`;`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return `import { gql } from "@smartive/graphql-magic";
|
|
76
|
+
|
|
77
|
+
${parts.join("\n\n")}`;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/client/queries.ts
|
|
81
|
+
var import_upperFirst = __toESM(require("lodash/upperFirst"), 1);
|
|
82
|
+
|
|
83
|
+
// src/models/utils.ts
|
|
84
|
+
var import_inflection = require("inflection");
|
|
85
|
+
var import_camelCase = __toESM(require("lodash/camelCase"), 1);
|
|
86
|
+
var import_get = __toESM(require("lodash/get"), 1);
|
|
87
|
+
var import_kebabCase = __toESM(require("lodash/kebabCase"), 1);
|
|
88
|
+
var import_startCase = __toESM(require("lodash/startCase"), 1);
|
|
89
|
+
var typeToField = (type) => type.substr(0, 1).toLowerCase() + type.substr(1);
|
|
90
|
+
var getModelPlural = (model) => model.plural || (0, import_inflection.pluralize)(model.name);
|
|
91
|
+
var getModelPluralField = (model) => typeToField(getModelPlural(model));
|
|
92
|
+
var isEntityModel = (model) => model.kind === "entity";
|
|
93
|
+
var isEnumModel = (model) => model.kind === "enum";
|
|
94
|
+
var isRawEnumModel = (model) => model.kind === "raw-enum";
|
|
95
|
+
var isScalarModel = (model) => model.kind === "scalar";
|
|
96
|
+
var isObjectModel = (model) => model.kind === "object";
|
|
97
|
+
var isInputModel = (model) => model.kind === "input";
|
|
98
|
+
var and = (...predicates) => (field) => predicates.every((predicate) => predicate(field));
|
|
99
|
+
var not = (predicate) => (field) => !predicate(field);
|
|
100
|
+
var isRelation = (field) => field.kind === "relation";
|
|
101
|
+
var isQueriableField = ({ queriable }) => queriable !== false;
|
|
102
|
+
var isCustomField = (field) => field.kind === "custom";
|
|
103
|
+
var isSimpleField = and(not(isRelation), not(isCustomField));
|
|
104
|
+
var getModels = (rawModels) => {
|
|
105
|
+
const models = rawModels.filter(isEntityModel).map((model) => {
|
|
106
|
+
const objectModel = {
|
|
107
|
+
...model,
|
|
108
|
+
fieldsByName: {},
|
|
109
|
+
relations: [],
|
|
110
|
+
relationsByName: {},
|
|
111
|
+
reverseRelations: [],
|
|
112
|
+
reverseRelationsByName: {},
|
|
113
|
+
fields: [
|
|
114
|
+
{ name: "id", type: "ID", nonNull: true, unique: true, primary: true, generated: true },
|
|
115
|
+
...model.fields,
|
|
116
|
+
...model.creatable ? [
|
|
117
|
+
{
|
|
118
|
+
name: "createdAt",
|
|
119
|
+
type: "DateTime",
|
|
120
|
+
nonNull: true,
|
|
121
|
+
orderable: true,
|
|
122
|
+
generated: true,
|
|
123
|
+
...typeof model.creatable === "object" && model.creatable.createdAt
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "createdBy",
|
|
127
|
+
kind: "relation",
|
|
128
|
+
type: "User",
|
|
129
|
+
nonNull: true,
|
|
130
|
+
reverse: `created${getModelPlural(model)}`,
|
|
131
|
+
generated: true,
|
|
132
|
+
...typeof model.creatable === "object" && model.creatable.createdBy
|
|
133
|
+
}
|
|
134
|
+
] : [],
|
|
135
|
+
...model.updatable ? [
|
|
136
|
+
{
|
|
137
|
+
name: "updatedAt",
|
|
138
|
+
type: "DateTime",
|
|
139
|
+
nonNull: true,
|
|
140
|
+
orderable: true,
|
|
141
|
+
generated: true,
|
|
142
|
+
...typeof model.updatable === "object" && model.updatable.updatedAt
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: "updatedBy",
|
|
146
|
+
kind: "relation",
|
|
147
|
+
type: "User",
|
|
148
|
+
nonNull: true,
|
|
149
|
+
reverse: `updated${getModelPlural(model)}`,
|
|
150
|
+
generated: true,
|
|
151
|
+
...typeof model.updatable === "object" && model.updatable.updatedBy
|
|
152
|
+
}
|
|
153
|
+
] : [],
|
|
154
|
+
...model.deletable ? [
|
|
155
|
+
{
|
|
156
|
+
name: "deleted",
|
|
157
|
+
type: "Boolean",
|
|
158
|
+
nonNull: true,
|
|
159
|
+
defaultValue: false,
|
|
160
|
+
filterable: { default: false },
|
|
161
|
+
generated: true,
|
|
162
|
+
...typeof model.deletable === "object" && model.deletable.deleted
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "deletedAt",
|
|
166
|
+
type: "DateTime",
|
|
167
|
+
orderable: true,
|
|
168
|
+
generated: true,
|
|
169
|
+
...typeof model.deletable === "object" && model.deletable.deletedAt
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "deletedBy",
|
|
173
|
+
kind: "relation",
|
|
174
|
+
type: "User",
|
|
175
|
+
reverse: `deleted${getModelPlural(model)}`,
|
|
176
|
+
generated: true,
|
|
177
|
+
...typeof model.deletable === "object" && model.deletable.deletedBy
|
|
178
|
+
}
|
|
179
|
+
] : []
|
|
180
|
+
].map((field) => ({
|
|
181
|
+
...field,
|
|
182
|
+
...field.kind === "relation" && {
|
|
183
|
+
foreignKey: field.foreignKey || `${field.name}Id`
|
|
184
|
+
}
|
|
185
|
+
}))
|
|
186
|
+
};
|
|
187
|
+
for (const field of objectModel.fields) {
|
|
188
|
+
objectModel.fieldsByName[field.name] = field;
|
|
189
|
+
}
|
|
190
|
+
return objectModel;
|
|
191
|
+
});
|
|
192
|
+
for (const model of models) {
|
|
193
|
+
for (const field of model.fields) {
|
|
194
|
+
if (field.kind !== "relation") {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const fieldModel = summonByName(models, field.type);
|
|
198
|
+
const reverseRelation = {
|
|
199
|
+
kind: "relation",
|
|
200
|
+
name: field.reverse || (field.toOne ? typeToField(model.name) : getModelPluralField(model)),
|
|
201
|
+
foreignKey: get(field, "foreignKey"),
|
|
202
|
+
type: model.name,
|
|
203
|
+
toOne: !!field.toOne,
|
|
204
|
+
fieldModel,
|
|
205
|
+
field,
|
|
206
|
+
model
|
|
207
|
+
};
|
|
208
|
+
const relation = {
|
|
209
|
+
field,
|
|
210
|
+
model: fieldModel,
|
|
211
|
+
reverseRelation
|
|
212
|
+
};
|
|
213
|
+
model.relations.push(relation);
|
|
214
|
+
model.relationsByName[relation.field.name] = relation;
|
|
215
|
+
fieldModel.reverseRelations.push(reverseRelation);
|
|
216
|
+
fieldModel.reverseRelationsByName[reverseRelation.name] = reverseRelation;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return models;
|
|
220
|
+
};
|
|
221
|
+
var summonByName = (array, value2) => summonByKey(array, "name", value2);
|
|
222
|
+
var summonByKey = (array, key, value2) => summon(array, (element) => (0, import_get.default)(element, key) === value2, `No element found with ${key} ${value2}`);
|
|
223
|
+
var summon = (array, cb, errorMessage) => {
|
|
224
|
+
if (array === void 0) {
|
|
225
|
+
console.trace();
|
|
226
|
+
throw new Error("Base array is not defined.");
|
|
227
|
+
}
|
|
228
|
+
const result = array.find(cb);
|
|
229
|
+
if (result === void 0) {
|
|
230
|
+
console.trace();
|
|
231
|
+
throw new Error(errorMessage || "Element not found.");
|
|
232
|
+
}
|
|
233
|
+
return result;
|
|
234
|
+
};
|
|
235
|
+
var it = (object2) => {
|
|
236
|
+
if (object2 === void 0 || object2 === null) {
|
|
237
|
+
console.trace();
|
|
238
|
+
throw new Error("Base object is not defined.");
|
|
239
|
+
}
|
|
240
|
+
return object2;
|
|
241
|
+
};
|
|
242
|
+
var get = (object2, key) => {
|
|
243
|
+
const value2 = it(object2)[key];
|
|
244
|
+
if (value2 === void 0 || value2 === null) {
|
|
245
|
+
console.trace();
|
|
246
|
+
throw new Error(`Object doesn't have ${String(key)}`);
|
|
247
|
+
}
|
|
248
|
+
return value2;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// src/db/generate.ts
|
|
252
|
+
var import_code_block_writer = __toESM(require("code-block-writer"), 1);
|
|
253
|
+
var PRIMITIVE_TYPES = {
|
|
254
|
+
ID: "string",
|
|
255
|
+
Boolean: "boolean",
|
|
256
|
+
Upload: "string",
|
|
257
|
+
Int: "number",
|
|
258
|
+
Float: "number",
|
|
259
|
+
String: "string",
|
|
260
|
+
DateTime: "DateTime | string"
|
|
261
|
+
};
|
|
262
|
+
var OPTIONAL_SEED_FIELDS = ["createdAt", "createdById", "updatedAt", "updatedById", "deletedAt", "deletedById"];
|
|
263
|
+
var generateDBModels = (rawModels) => {
|
|
264
|
+
const writer = new import_code_block_writer.default["default"]({
|
|
265
|
+
useSingleQuote: true,
|
|
266
|
+
indentNumberOfSpaces: 2
|
|
267
|
+
});
|
|
268
|
+
writer.write(`import { DateTime } from 'luxon';`).blankLine();
|
|
269
|
+
for (const enm2 of rawModels.filter(isEnumModel)) {
|
|
270
|
+
writer.write(`export type ${enm2.name} = ${enm2.values.map((v) => `'${v}'`).join(" | ")};`).blankLine();
|
|
271
|
+
}
|
|
272
|
+
const models = getModels(rawModels);
|
|
273
|
+
for (const model of models) {
|
|
274
|
+
const fields2 = model.fields.some((field) => field.kind === "relation" && field.foreignKey === "id") ? model.fields.filter((field) => field.name !== "id") : model.fields;
|
|
275
|
+
writer.write(`export type ${model.name} = `).inlineBlock(() => {
|
|
276
|
+
for (const field of fields2.filter(not(isCustomField))) {
|
|
277
|
+
writer.write(`'${getFieldName(field)}': ${getFieldType(field)}${field.nonNull ? "" : " | null"},`).newLine();
|
|
278
|
+
}
|
|
279
|
+
}).blankLine();
|
|
280
|
+
writer.write(`export type ${model.name}Initializer = `).inlineBlock(() => {
|
|
281
|
+
for (const field of fields2.filter(not(isCustomField))) {
|
|
282
|
+
writer.write(
|
|
283
|
+
`'${getFieldName(field)}'${field.nonNull && field.defaultValue === void 0 ? "" : "?"}: ${getFieldType(
|
|
284
|
+
field
|
|
285
|
+
)}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"},`
|
|
286
|
+
).newLine();
|
|
287
|
+
}
|
|
288
|
+
}).blankLine();
|
|
289
|
+
writer.write(`export type ${model.name}Mutator = `).inlineBlock(() => {
|
|
290
|
+
for (const field of fields2.filter(not(isCustomField))) {
|
|
291
|
+
writer.write(
|
|
292
|
+
`'${getFieldName(field)}'?: ${getFieldType(field)}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"},`
|
|
293
|
+
).newLine();
|
|
294
|
+
}
|
|
295
|
+
}).blankLine();
|
|
296
|
+
writer.write(`export type ${model.name}Seed = `).inlineBlock(() => {
|
|
297
|
+
for (const field of fields2.filter(not(isCustomField))) {
|
|
298
|
+
const fieldName = getFieldName(field);
|
|
299
|
+
writer.write(
|
|
300
|
+
`'${getFieldName(field)}'${field.nonNull && field.defaultValue === void 0 && !OPTIONAL_SEED_FIELDS.includes(fieldName) ? "" : "?"}: ${field.kind === "enum" ? field.list ? "string[]" : "string" : getFieldType(field)}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"},`
|
|
301
|
+
).newLine();
|
|
302
|
+
}
|
|
303
|
+
}).blankLine();
|
|
304
|
+
}
|
|
305
|
+
writer.write(`export type SeedData = `).inlineBlock(() => {
|
|
306
|
+
for (const model of models) {
|
|
307
|
+
writer.write(`${model.name}: ${model.name}Seed[],`).newLine();
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
return writer.toString();
|
|
311
|
+
};
|
|
312
|
+
var getFieldName = (field) => field.kind === "relation" ? field.foreignKey || `${field.name}Id` : field.name;
|
|
313
|
+
var getFieldType = (field) => {
|
|
314
|
+
const kind = field.kind;
|
|
315
|
+
switch (kind) {
|
|
316
|
+
case "json":
|
|
317
|
+
return "string";
|
|
318
|
+
case "relation":
|
|
319
|
+
return "string";
|
|
320
|
+
case "enum":
|
|
321
|
+
return field.type + (field.list ? "[]" : "");
|
|
322
|
+
case "custom":
|
|
323
|
+
throw new Error(`Custom fields are not in the db.`);
|
|
324
|
+
case "primitive":
|
|
325
|
+
case void 0:
|
|
326
|
+
return get(PRIMITIVE_TYPES, field.type) + (field.list ? "[]" : "");
|
|
327
|
+
default: {
|
|
328
|
+
const exhaustiveCheck = kind;
|
|
329
|
+
throw new Error(exhaustiveCheck);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
var generateKnexTables = (rawModels) => {
|
|
334
|
+
const writer = new import_code_block_writer.default["default"]({
|
|
335
|
+
useSingleQuote: true,
|
|
336
|
+
indentNumberOfSpaces: 2
|
|
337
|
+
});
|
|
338
|
+
const models = getModels(rawModels);
|
|
339
|
+
writer.write(`import { Knex } from 'knex';`).newLine();
|
|
340
|
+
writer.write(
|
|
341
|
+
`import { ${models.map((model) => `${model.name}, ${model.name}Initializer, ${model.name}Mutator`).join(", ")} } from '.';`
|
|
342
|
+
).blankLine();
|
|
343
|
+
writer.write(`declare module 'knex/types/tables' `).inlineBlock(() => {
|
|
344
|
+
writer.write(`interface Tables `).inlineBlock(() => {
|
|
345
|
+
for (const model of models) {
|
|
346
|
+
writer.write(`'${model.name}': Knex.CompositeTableType<${model.name}, ${model.name}Initializer, ${model.name}Mutator>,`).newLine();
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
return writer.toString();
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// src/gqm/codegen.ts
|
|
354
|
+
var import_cli = require("@graphql-codegen/cli");
|
|
355
|
+
|
|
356
|
+
// src/gqm/settings.ts
|
|
357
|
+
var import_fs = require("fs");
|
|
358
|
+
var import_path = require("path");
|
|
359
|
+
|
|
360
|
+
// src/gqm/readline.ts
|
|
361
|
+
var import_readline = __toESM(require("readline"), 1);
|
|
362
|
+
var readLine = (prompt) => {
|
|
363
|
+
const rl = import_readline.default.createInterface({
|
|
364
|
+
input: process.stdin,
|
|
365
|
+
output: process.stdout
|
|
366
|
+
});
|
|
367
|
+
return new Promise((resolve2) => {
|
|
368
|
+
rl.question(prompt, (answer) => {
|
|
369
|
+
rl.close();
|
|
370
|
+
resolve2(answer);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// src/gqm/templates.ts
|
|
376
|
+
var EMPTY_MODELS = `
|
|
377
|
+
import { RawModels, getModels } from '@smartive/graphql-magic';
|
|
378
|
+
|
|
379
|
+
export const rawModels: RawModels = [
|
|
380
|
+
{
|
|
381
|
+
kind: 'entity',
|
|
382
|
+
name: 'User',
|
|
383
|
+
fields: []
|
|
384
|
+
},
|
|
385
|
+
]
|
|
386
|
+
|
|
387
|
+
export const models = getModels(rawModels);
|
|
388
|
+
`;
|
|
389
|
+
var GRAPHQL_CODEGEN = (path) => `
|
|
390
|
+
overwrite: true
|
|
391
|
+
schema: '${path}/schema.graphql'
|
|
392
|
+
documents: null
|
|
393
|
+
generates:
|
|
394
|
+
${path}/api/index.ts:
|
|
395
|
+
plugins:
|
|
396
|
+
- 'typescript'
|
|
397
|
+
- 'typescript-resolvers'
|
|
398
|
+
- add:
|
|
399
|
+
content: "import { DateTime } from 'luxon'"
|
|
400
|
+
config:
|
|
401
|
+
scalars:
|
|
402
|
+
DateTime: DateTime
|
|
403
|
+
`;
|
|
404
|
+
var CLIENT_CODEGEN = (path) => `
|
|
405
|
+
schema: ${path}/schema.graphql
|
|
406
|
+
documents: [ './src/**/*.ts', './src/**/*.tsx' ]
|
|
407
|
+
generates:
|
|
408
|
+
${path}/client/index.ts:
|
|
409
|
+
plugins:
|
|
410
|
+
- typescript
|
|
411
|
+
- typescript-operations
|
|
412
|
+
- typescript-compatibility
|
|
413
|
+
|
|
414
|
+
config:
|
|
415
|
+
preResolveTypes: true # Simplifies the generated types
|
|
416
|
+
namingConvention: keep # Keeps naming as-is
|
|
417
|
+
nonOptionalTypename: true # Forces \`__typename\` on all selection sets
|
|
418
|
+
skipTypeNameForRoot: true # Don't generate __typename for root types
|
|
419
|
+
avoidOptionals: # Avoids optionals on the level of the field
|
|
420
|
+
field: true
|
|
421
|
+
scalars:
|
|
422
|
+
DateTime: string
|
|
423
|
+
`;
|
|
424
|
+
var KNEXFILE = `
|
|
425
|
+
const config = {
|
|
426
|
+
client: 'postgresql',
|
|
427
|
+
connection: {
|
|
428
|
+
host: process.env.DATABASE_HOST,
|
|
429
|
+
database: process.env.DATABASE_NAME,
|
|
430
|
+
user: process.env.DATABASE_USER,
|
|
431
|
+
password: process.env.DATABASE_PASSWORD,
|
|
432
|
+
},
|
|
433
|
+
} as const;
|
|
434
|
+
|
|
435
|
+
export default config;
|
|
436
|
+
`;
|
|
437
|
+
|
|
438
|
+
// src/gqm/settings.ts
|
|
439
|
+
var SETTINGS_PATH = ".gqmrc.json";
|
|
440
|
+
var DEFAULTS = {
|
|
441
|
+
modelsPath: {
|
|
442
|
+
question: "What is the models path?",
|
|
443
|
+
defaultValue: "src/config/models.ts",
|
|
444
|
+
init: (path) => {
|
|
445
|
+
ensureFileExists(path, EMPTY_MODELS);
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
generatedFolderPath: {
|
|
449
|
+
question: "What is the path for generated stuff?",
|
|
450
|
+
defaultValue: "src/generated",
|
|
451
|
+
init: (path) => {
|
|
452
|
+
ensureFileExists(`${path}/.gitkeep`, "");
|
|
453
|
+
ensureFileExists(`${path}/db/.gitkeep`, "");
|
|
454
|
+
ensureFileExists(`${path}/api/.gitkeep`, "");
|
|
455
|
+
ensureFileExists(`${path}/client/.gitkeep`, "");
|
|
456
|
+
ensureFileExists(`graphql-codegen.yml`, GRAPHQL_CODEGEN(path));
|
|
457
|
+
ensureFileExists(`client-codegen.yml`, CLIENT_CODEGEN(path));
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
var initSetting = async (name2) => {
|
|
462
|
+
const { question, defaultValue, init } = DEFAULTS[name2];
|
|
463
|
+
const value2 = await readLine(`${question} (${defaultValue})`) || defaultValue;
|
|
464
|
+
init(value2);
|
|
465
|
+
return value2;
|
|
466
|
+
};
|
|
467
|
+
var initSettings = async () => {
|
|
468
|
+
const settings = {};
|
|
469
|
+
for (const name2 of Object.keys(DEFAULTS)) {
|
|
470
|
+
settings[name2] = await initSetting(name2);
|
|
471
|
+
}
|
|
472
|
+
saveSettings(settings);
|
|
473
|
+
};
|
|
474
|
+
var saveSettings = (settings) => {
|
|
475
|
+
writeToFile(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
476
|
+
};
|
|
477
|
+
var getSettings = async () => {
|
|
478
|
+
if (!(0, import_fs.existsSync)(SETTINGS_PATH)) {
|
|
479
|
+
await initSettings();
|
|
480
|
+
}
|
|
481
|
+
return JSON.parse((0, import_fs.readFileSync)(SETTINGS_PATH, "utf8"));
|
|
482
|
+
};
|
|
483
|
+
var getSetting = async (name2) => {
|
|
484
|
+
const settings = await getSettings();
|
|
485
|
+
if (!(name2 in settings)) {
|
|
486
|
+
settings[name2] = await initSetting(name2);
|
|
487
|
+
saveSettings(settings);
|
|
488
|
+
}
|
|
489
|
+
return settings[name2];
|
|
490
|
+
};
|
|
491
|
+
var ensureDirectoryExists = (filePath) => {
|
|
492
|
+
const dir = (0, import_path.dirname)(filePath);
|
|
493
|
+
if ((0, import_fs.existsSync)(dir)) {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
ensureDirectoryExists(dir);
|
|
497
|
+
try {
|
|
498
|
+
(0, import_fs.mkdirSync)(dir);
|
|
499
|
+
return true;
|
|
500
|
+
} catch (err) {
|
|
501
|
+
if (err.code === "EEXIST") {
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
throw err;
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
var ensureFileExists = (filePath, content) => {
|
|
508
|
+
if (!(0, import_fs.existsSync)(filePath)) {
|
|
509
|
+
console.info(`Creating ${filePath}`);
|
|
510
|
+
ensureDirectoryExists(filePath);
|
|
511
|
+
(0, import_fs.writeFileSync)(filePath, content);
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
var writeToFile = (filePath, content) => {
|
|
515
|
+
ensureDirectoryExists(filePath);
|
|
516
|
+
if ((0, import_fs.existsSync)(filePath)) {
|
|
517
|
+
const currentContent = (0, import_fs.readFileSync)(filePath, "utf-8");
|
|
518
|
+
if (content === currentContent) {
|
|
519
|
+
} else {
|
|
520
|
+
(0, import_fs.writeFileSync)(filePath, content);
|
|
521
|
+
console.info(`${filePath} updated`);
|
|
522
|
+
}
|
|
523
|
+
} else {
|
|
524
|
+
(0, import_fs.writeFileSync)(filePath, content);
|
|
525
|
+
console.info(`Created ${filePath}`);
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// src/gqm/codegen.ts
|
|
530
|
+
var generateGraphqlApiTypes = async () => {
|
|
531
|
+
const generatedFolderPath = await getSetting("generatedFolderPath");
|
|
532
|
+
await (0, import_cli.generate)({
|
|
533
|
+
overwrite: true,
|
|
534
|
+
schema: `${generatedFolderPath}/schema.graphql`,
|
|
535
|
+
documents: null,
|
|
536
|
+
generates: {
|
|
537
|
+
[`${generatedFolderPath}/api/index.ts`]: {
|
|
538
|
+
plugins: ["typescript", "typescript-resolvers", { add: { content: `import { DateTime } from 'luxon';` } }]
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
config: {
|
|
542
|
+
scalars: {
|
|
543
|
+
DateTime: "DateTime"
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
};
|
|
548
|
+
var generateGraphqlClientTypes = async () => {
|
|
549
|
+
const generatedFolderPath = await getSetting("generatedFolderPath");
|
|
550
|
+
await (0, import_cli.generate)({
|
|
551
|
+
schema: `${generatedFolderPath}/schema.graphql`,
|
|
552
|
+
documents: ["./src/**/*.ts", "./src/**/*.tsx"],
|
|
553
|
+
generates: {
|
|
554
|
+
[`${generatedFolderPath}/client/index.ts`]: {
|
|
555
|
+
plugins: ["typescript", "typescript-operations", "typescript-compatibility"]
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
config: {
|
|
559
|
+
preResolveTypes: true,
|
|
560
|
+
// Simplifies the generated types
|
|
561
|
+
namingConvention: "keep",
|
|
562
|
+
// Keeps naming as-is
|
|
563
|
+
nonOptionalTypename: true,
|
|
564
|
+
// Forces `__typename` on all selection sets
|
|
565
|
+
skipTypeNameForRoot: true,
|
|
566
|
+
// Don't generate __typename for root types
|
|
567
|
+
avoidOptionals: {
|
|
568
|
+
// Avoids optionals on the level of the field
|
|
569
|
+
field: true
|
|
570
|
+
},
|
|
571
|
+
scalars: {
|
|
572
|
+
DateTime: "string"
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
// src/gqm/parse-knexfile.ts
|
|
579
|
+
var import_ts_morph4 = require("ts-morph");
|
|
580
|
+
|
|
581
|
+
// src/gqm/static-eval.ts
|
|
582
|
+
var import_ts_morph2 = require("ts-morph");
|
|
583
|
+
|
|
584
|
+
// src/gqm/visitor.ts
|
|
585
|
+
var import_ts_morph = require("ts-morph");
|
|
586
|
+
var visit = (node, context, visitor2) => {
|
|
587
|
+
const kind = node?.getKind();
|
|
588
|
+
if (kind in visitor2) {
|
|
589
|
+
return visitor2[kind](node.asKindOrThrow(kind), context);
|
|
590
|
+
}
|
|
591
|
+
if ("unknown" in visitor2) {
|
|
592
|
+
return visitor2.unknown(node);
|
|
593
|
+
}
|
|
594
|
+
console.error(node.getText());
|
|
595
|
+
console.error(node.getParent().getText());
|
|
596
|
+
throw new Error(
|
|
597
|
+
`Cannot handle kind ${get(
|
|
598
|
+
Object.entries(import_ts_morph.SyntaxKind).find(([, val]) => val === kind),
|
|
599
|
+
0
|
|
600
|
+
)}`
|
|
601
|
+
);
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// src/gqm/static-eval.ts
|
|
605
|
+
var staticEval = (node, context) => visit(node, context, visitor);
|
|
606
|
+
var visitor = {
|
|
607
|
+
undefined: () => void 0,
|
|
608
|
+
[import_ts_morph2.SyntaxKind.VariableDeclaration]: (node, context) => staticEval(node.getInitializer(), context),
|
|
609
|
+
[import_ts_morph2.SyntaxKind.ArrayLiteralExpression]: (node, context) => {
|
|
610
|
+
const values = [];
|
|
611
|
+
for (const value2 of node.getElements()) {
|
|
612
|
+
if (value2.isKind(import_ts_morph2.SyntaxKind.SpreadElement)) {
|
|
613
|
+
values.push(...staticEval(value2, context));
|
|
614
|
+
} else {
|
|
615
|
+
values.push(staticEval(value2, context));
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return values;
|
|
619
|
+
},
|
|
620
|
+
[import_ts_morph2.SyntaxKind.ObjectLiteralExpression]: (node, context) => {
|
|
621
|
+
const result = {};
|
|
622
|
+
for (const property of node.getProperties()) {
|
|
623
|
+
Object.assign(result, staticEval(property, context));
|
|
624
|
+
}
|
|
625
|
+
return result;
|
|
626
|
+
},
|
|
627
|
+
[import_ts_morph2.SyntaxKind.StringLiteral]: (node) => node.getLiteralValue(),
|
|
628
|
+
[import_ts_morph2.SyntaxKind.PropertyAssignment]: (node, context) => ({
|
|
629
|
+
[node.getName()]: staticEval(node.getInitializer(), context)
|
|
630
|
+
}),
|
|
631
|
+
[import_ts_morph2.SyntaxKind.ShorthandPropertyAssignment]: (node, context) => ({
|
|
632
|
+
[node.getName()]: staticEval(node.getNameNode(), context)
|
|
633
|
+
}),
|
|
634
|
+
[import_ts_morph2.SyntaxKind.SpreadElement]: (node, context) => staticEval(node.getExpression(), context),
|
|
635
|
+
[import_ts_morph2.SyntaxKind.SpreadAssignment]: (node, context) => staticEval(node.getExpression(), context),
|
|
636
|
+
[import_ts_morph2.SyntaxKind.Identifier]: (node, context) => {
|
|
637
|
+
switch (node.getText()) {
|
|
638
|
+
case "undefined":
|
|
639
|
+
return void 0;
|
|
640
|
+
case "process":
|
|
641
|
+
return process;
|
|
642
|
+
}
|
|
643
|
+
const definitionNodes = node.getDefinitionNodes();
|
|
644
|
+
if (!definitionNodes.length) {
|
|
645
|
+
throw new Error(`No definition node found for identifier ${node.getText()}.`);
|
|
646
|
+
}
|
|
647
|
+
return staticEval(definitionNodes[0], context);
|
|
648
|
+
},
|
|
649
|
+
[import_ts_morph2.SyntaxKind.ParenthesizedExpression]: (node, context) => staticEval(node.getExpression(), context),
|
|
650
|
+
[import_ts_morph2.SyntaxKind.AsExpression]: (node, context) => staticEval(node.getExpression(), context),
|
|
651
|
+
[import_ts_morph2.SyntaxKind.ConditionalExpression]: (node, context) => staticEval(node.getCondition(), context) ? staticEval(node.getWhenTrue(), context) : staticEval(node.getWhenFalse(), context),
|
|
652
|
+
[import_ts_morph2.SyntaxKind.TrueKeyword]: () => true,
|
|
653
|
+
[import_ts_morph2.SyntaxKind.FalseKeyword]: () => false,
|
|
654
|
+
[import_ts_morph2.SyntaxKind.NumericLiteral]: (node) => node.getLiteralValue(),
|
|
655
|
+
[import_ts_morph2.SyntaxKind.CallExpression]: (node, context) => {
|
|
656
|
+
const method = staticEval(node.getExpression(), context);
|
|
657
|
+
const args2 = node.getArguments().map((arg) => staticEval(arg, context));
|
|
658
|
+
return method(...args2);
|
|
659
|
+
},
|
|
660
|
+
[import_ts_morph2.SyntaxKind.PropertyAccessExpression]: (node, context) => {
|
|
661
|
+
const target = staticEval(node.getExpression(), context);
|
|
662
|
+
const property = target[node.getName()];
|
|
663
|
+
if (typeof property === "function") {
|
|
664
|
+
if (Array.isArray(target)) {
|
|
665
|
+
switch (node.getName()) {
|
|
666
|
+
case "map":
|
|
667
|
+
case "flatMap":
|
|
668
|
+
case "includes":
|
|
669
|
+
case "some":
|
|
670
|
+
case "find":
|
|
671
|
+
case "filter":
|
|
672
|
+
return target[node.getName()].bind(target);
|
|
673
|
+
}
|
|
674
|
+
} else if (typeof target === "string") {
|
|
675
|
+
const name2 = node.getName();
|
|
676
|
+
switch (name2) {
|
|
677
|
+
case "slice":
|
|
678
|
+
case "toUpperCase":
|
|
679
|
+
case "toLowerCase":
|
|
680
|
+
return target[name2].bind(target);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
throw new Error(`Cannot handle method ${node.getName()} on type ${typeof target}`);
|
|
684
|
+
}
|
|
685
|
+
return property;
|
|
686
|
+
},
|
|
687
|
+
[import_ts_morph2.SyntaxKind.ArrowFunction]: (node, context) => {
|
|
688
|
+
return (...args2) => {
|
|
689
|
+
const parameters = {};
|
|
690
|
+
let i = 0;
|
|
691
|
+
for (const parameter of node.getParameters()) {
|
|
692
|
+
parameters[parameter.getName()] = args2[i];
|
|
693
|
+
i++;
|
|
694
|
+
}
|
|
695
|
+
return staticEval(node.getBody(), { ...context, ...parameters });
|
|
696
|
+
};
|
|
697
|
+
},
|
|
698
|
+
[import_ts_morph2.SyntaxKind.Block]: (node, context) => {
|
|
699
|
+
for (const statement of node.getStatements()) {
|
|
700
|
+
return staticEval(statement, context);
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
[import_ts_morph2.SyntaxKind.CaseClause]: (node, context) => {
|
|
704
|
+
const statements = node.getStatements();
|
|
705
|
+
if (statements.length !== 1) {
|
|
706
|
+
console.error(node.getText());
|
|
707
|
+
throw new Error(`Can only handle code blocks with 1 statement.`);
|
|
708
|
+
}
|
|
709
|
+
return staticEval(statements[0], context);
|
|
710
|
+
},
|
|
711
|
+
[import_ts_morph2.SyntaxKind.DefaultClause]: (node, context) => {
|
|
712
|
+
const statements = node.getStatements();
|
|
713
|
+
if (statements.length !== 1) {
|
|
714
|
+
console.error(node.getText());
|
|
715
|
+
throw new Error(`Can only handle code blocks with exactly 1 statement.`);
|
|
716
|
+
}
|
|
717
|
+
return staticEval(statements[0], context);
|
|
718
|
+
},
|
|
719
|
+
[import_ts_morph2.SyntaxKind.ReturnStatement]: (node, context) => {
|
|
720
|
+
return staticEval(node.getExpression(), context);
|
|
721
|
+
},
|
|
722
|
+
[import_ts_morph2.SyntaxKind.SwitchStatement]: (node, context) => {
|
|
723
|
+
const value2 = staticEval(node.getExpression(), context);
|
|
724
|
+
let active = false;
|
|
725
|
+
for (const clause of node.getCaseBlock().getClauses()) {
|
|
726
|
+
switch (clause.getKind()) {
|
|
727
|
+
case import_ts_morph2.SyntaxKind.DefaultClause:
|
|
728
|
+
return staticEval(clause, context);
|
|
729
|
+
case import_ts_morph2.SyntaxKind.CaseClause: {
|
|
730
|
+
const caseClause = clause.asKindOrThrow(import_ts_morph2.SyntaxKind.CaseClause);
|
|
731
|
+
if (caseClause.getStatements().length && active) {
|
|
732
|
+
return staticEval(clause, context);
|
|
733
|
+
}
|
|
734
|
+
const caseValue = staticEval(caseClause.getExpression(), context);
|
|
735
|
+
if (value2 === caseValue) {
|
|
736
|
+
active = true;
|
|
737
|
+
if (caseClause.getStatements().length) {
|
|
738
|
+
return staticEval(clause, context);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
[import_ts_morph2.SyntaxKind.Parameter]: (node, context) => context[node.getName()],
|
|
746
|
+
[import_ts_morph2.SyntaxKind.BinaryExpression]: (node, context) => {
|
|
747
|
+
switch (node.getOperatorToken().getKind()) {
|
|
748
|
+
case import_ts_morph2.SyntaxKind.EqualsEqualsEqualsToken:
|
|
749
|
+
return staticEval(node.getLeft(), context) === staticEval(node.getRight(), context);
|
|
750
|
+
case import_ts_morph2.SyntaxKind.BarBarToken:
|
|
751
|
+
return staticEval(node.getLeft(), context) || staticEval(node.getRight(), context);
|
|
752
|
+
default:
|
|
753
|
+
throw new Error(`Cannot handle operator of kind ${node.getOperatorToken().getKindName()}`);
|
|
754
|
+
}
|
|
755
|
+
},
|
|
756
|
+
[import_ts_morph2.SyntaxKind.SatisfiesExpression]: (node, context) => staticEval(node.getExpression(), context),
|
|
757
|
+
[import_ts_morph2.SyntaxKind.TemplateExpression]: (node, context) => node.getHead().getLiteralText() + node.getTemplateSpans().map((span) => staticEval(span.getExpression(), context) + staticEval(span.getLiteral(), context)).join(""),
|
|
758
|
+
[import_ts_morph2.SyntaxKind.TemplateTail]: (node) => node.getLiteralText(),
|
|
759
|
+
[import_ts_morph2.SyntaxKind.TemplateMiddle]: (node) => node.getLiteralText(),
|
|
760
|
+
[import_ts_morph2.SyntaxKind.PrefixUnaryExpression]: (node, context) => {
|
|
761
|
+
switch (node.getOperatorToken()) {
|
|
762
|
+
case import_ts_morph2.SyntaxKind.PlusToken:
|
|
763
|
+
return +staticEval(node.getOperand(), context);
|
|
764
|
+
case import_ts_morph2.SyntaxKind.MinusToken:
|
|
765
|
+
return -staticEval(node.getOperand(), context);
|
|
766
|
+
case import_ts_morph2.SyntaxKind.TildeToken:
|
|
767
|
+
return ~staticEval(node.getOperand(), context);
|
|
768
|
+
case import_ts_morph2.SyntaxKind.ExclamationToken:
|
|
769
|
+
return !staticEval(node.getOperand(), context);
|
|
770
|
+
case import_ts_morph2.SyntaxKind.PlusPlusToken:
|
|
771
|
+
case import_ts_morph2.SyntaxKind.MinusMinusToken:
|
|
772
|
+
throw new Error(`Cannot handle assignments.`);
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
[import_ts_morph2.SyntaxKind.ElementAccessExpression]: (node, context) => {
|
|
776
|
+
const target = staticEval(node.getExpression(), context);
|
|
777
|
+
const argument = staticEval(node.getArgumentExpression(), context);
|
|
778
|
+
return target[argument];
|
|
779
|
+
},
|
|
780
|
+
[import_ts_morph2.SyntaxKind.NoSubstitutionTemplateLiteral]: (node) => node.getLiteralValue()
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
// src/gqm/utils.ts
|
|
784
|
+
var import_ts_morph3 = require("ts-morph");
|
|
785
|
+
var findDeclarationInFile = (sourceFile, name2) => {
|
|
786
|
+
const syntaxList = sourceFile.getChildrenOfKind(import_ts_morph3.SyntaxKind.SyntaxList)[0];
|
|
787
|
+
if (!syntaxList) {
|
|
788
|
+
throw new Error("No SyntaxList");
|
|
789
|
+
}
|
|
790
|
+
const declaration = findDeclaration(syntaxList, name2);
|
|
791
|
+
if (!declaration) {
|
|
792
|
+
throw new Error(`No ${name2} declaration`);
|
|
793
|
+
}
|
|
794
|
+
return declaration;
|
|
795
|
+
};
|
|
796
|
+
var findDeclaration = (syntaxList, name2) => {
|
|
797
|
+
for (const variableStatement of syntaxList.getChildrenOfKind(import_ts_morph3.SyntaxKind.VariableStatement)) {
|
|
798
|
+
for (const declaration of variableStatement.getDeclarationList().getDeclarations()) {
|
|
799
|
+
if (declaration.getName() === name2) {
|
|
800
|
+
return declaration;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
// src/gqm/parse-knexfile.ts
|
|
807
|
+
var KNEXFILE_PATH = `knexfile.ts`;
|
|
808
|
+
var parseKnexfile = async () => {
|
|
809
|
+
const project = new import_ts_morph4.Project({
|
|
810
|
+
manipulationSettings: {
|
|
811
|
+
indentationText: import_ts_morph4.IndentationText.TwoSpaces
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
ensureFileExists(KNEXFILE_PATH, KNEXFILE);
|
|
815
|
+
const sourceFile = project.addSourceFileAtPath(KNEXFILE_PATH);
|
|
816
|
+
const configDeclaration = findDeclarationInFile(sourceFile, "config");
|
|
817
|
+
const config2 = staticEval(configDeclaration, {});
|
|
818
|
+
return config2;
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
// src/gqm/parse-models.ts
|
|
822
|
+
var import_ts_morph5 = require("ts-morph");
|
|
823
|
+
var parseModels = async () => {
|
|
824
|
+
const project = new import_ts_morph5.Project({
|
|
825
|
+
manipulationSettings: {
|
|
826
|
+
indentationText: import_ts_morph5.IndentationText.TwoSpaces
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
const modelsPath = await getSetting("modelsPath");
|
|
830
|
+
const sourceFile = project.addSourceFileAtPath(modelsPath);
|
|
831
|
+
const modelsDeclaration = findDeclarationInFile(sourceFile, "rawModels");
|
|
832
|
+
const rawModels = staticEval(modelsDeclaration, {});
|
|
833
|
+
const generatedFolderPath = await getSetting("generatedFolderPath");
|
|
834
|
+
writeToFile(`${generatedFolderPath}/models.json`, JSON.stringify(rawModels, null, 2));
|
|
835
|
+
return rawModels;
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
// src/migrations/generate.ts
|
|
839
|
+
var import_code_block_writer2 = __toESM(require("code-block-writer"), 1);
|
|
840
|
+
var import_knex_schema_inspector = require("knex-schema-inspector");
|
|
841
|
+
var import_lowerFirst = __toESM(require("lodash/lowerFirst"), 1);
|
|
842
|
+
var MigrationGenerator = class {
|
|
843
|
+
constructor(knex2, rawModels) {
|
|
844
|
+
this.rawModels = rawModels;
|
|
845
|
+
this.schema = (0, import_knex_schema_inspector.SchemaInspector)(knex2);
|
|
846
|
+
this.models = getModels(rawModels);
|
|
847
|
+
}
|
|
848
|
+
writer = new import_code_block_writer2.default["default"]({
|
|
849
|
+
useSingleQuote: true,
|
|
850
|
+
indentNumberOfSpaces: 2
|
|
851
|
+
});
|
|
852
|
+
schema;
|
|
853
|
+
columns = {};
|
|
854
|
+
uuidUsed;
|
|
855
|
+
nowUsed;
|
|
856
|
+
models;
|
|
857
|
+
async generate() {
|
|
858
|
+
const { writer, schema, rawModels, models } = this;
|
|
859
|
+
const enums = (await schema.knex("pg_type").where({ typtype: "e" }).select("typname")).map(({ typname }) => typname);
|
|
860
|
+
const tables = await schema.tables();
|
|
861
|
+
for (const table of tables) {
|
|
862
|
+
this.columns[table] = await schema.columnInfo(table);
|
|
863
|
+
}
|
|
864
|
+
const up = [];
|
|
865
|
+
const down = [];
|
|
866
|
+
this.createEnums(
|
|
867
|
+
rawModels.filter(isEnumModel).filter((enm2) => !enums.includes((0, import_lowerFirst.default)(enm2.name))),
|
|
868
|
+
up,
|
|
869
|
+
down
|
|
870
|
+
);
|
|
871
|
+
for (const model of models) {
|
|
872
|
+
if (model.deleted) {
|
|
873
|
+
up.push(() => {
|
|
874
|
+
this.dropTable(model.name);
|
|
875
|
+
});
|
|
876
|
+
down.push(() => {
|
|
877
|
+
this.createTable(model.name, () => {
|
|
878
|
+
for (const field of model.fields) {
|
|
879
|
+
this.column(field);
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
});
|
|
883
|
+
if (model.updatable) {
|
|
884
|
+
up.push(() => {
|
|
885
|
+
this.dropTable(`${model.name}Revision`);
|
|
886
|
+
});
|
|
887
|
+
down.push(() => {
|
|
888
|
+
this.createRevisionTable(model);
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
if (model.oldName) {
|
|
893
|
+
up.push(() => {
|
|
894
|
+
this.renameTable(model.oldName, model.name);
|
|
895
|
+
});
|
|
896
|
+
down.push(() => {
|
|
897
|
+
this.renameTable(model.name, model.oldName);
|
|
898
|
+
});
|
|
899
|
+
tables[tables.indexOf(model.oldName)] = model.name;
|
|
900
|
+
this.columns[model.name] = this.columns[model.oldName];
|
|
901
|
+
delete this.columns[model.oldName];
|
|
902
|
+
if (model.updatable) {
|
|
903
|
+
up.push(() => {
|
|
904
|
+
this.renameTable(`${model.oldName}Revision`, `${model.name}Revision`);
|
|
905
|
+
this.alterTable(`${model.name}Revision`, () => {
|
|
906
|
+
this.renameColumn(`${typeToField(get(model, "oldName"))}Id`, `${typeToField(model.name)}Id`);
|
|
907
|
+
});
|
|
908
|
+
});
|
|
909
|
+
down.push(() => {
|
|
910
|
+
this.renameTable(`${model.name}Revision`, `${model.oldName}Revision`);
|
|
911
|
+
this.alterTable(`${model.oldName}Revision`, () => {
|
|
912
|
+
this.renameColumn(`${typeToField(model.name)}Id`, `${typeToField(get(model, "oldName"))}Id`);
|
|
913
|
+
});
|
|
914
|
+
});
|
|
915
|
+
tables[tables.indexOf(`${model.oldName}Revision`)] = `${model.name}Revision`;
|
|
916
|
+
this.columns[`${model.name}Revision`] = this.columns[`${model.oldName}Revision`];
|
|
917
|
+
delete this.columns[`${model.oldName}Revision`];
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (!tables.includes(model.name)) {
|
|
921
|
+
up.push(() => {
|
|
922
|
+
this.createTable(model.name, () => {
|
|
923
|
+
for (const field of model.fields) {
|
|
924
|
+
this.column(field);
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
});
|
|
928
|
+
down.push(() => {
|
|
929
|
+
this.dropTable(model.name);
|
|
930
|
+
});
|
|
931
|
+
} else {
|
|
932
|
+
this.renameFields(
|
|
933
|
+
model,
|
|
934
|
+
model.fields.filter(({ oldName }) => oldName),
|
|
935
|
+
up,
|
|
936
|
+
down
|
|
937
|
+
);
|
|
938
|
+
this.createFields(
|
|
939
|
+
model,
|
|
940
|
+
model.fields.filter(
|
|
941
|
+
({ name: name2, ...field }) => field.kind !== "custom" && !this.columns[model.name].some(
|
|
942
|
+
(col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
|
|
943
|
+
)
|
|
944
|
+
),
|
|
945
|
+
up,
|
|
946
|
+
down
|
|
947
|
+
);
|
|
948
|
+
const existingFields = model.fields.filter(({ name: name2, kind, nonNull: nonNull2 }) => {
|
|
949
|
+
const col = this.columns[model.name].find((col2) => col2.name === (kind === "relation" ? `${name2}Id` : name2));
|
|
950
|
+
if (!col) {
|
|
951
|
+
return false;
|
|
952
|
+
}
|
|
953
|
+
return !nonNull2 && !col.is_nullable;
|
|
954
|
+
});
|
|
955
|
+
this.updateFields(model, existingFields, up, down);
|
|
956
|
+
}
|
|
957
|
+
if (model.updatable) {
|
|
958
|
+
if (!tables.includes(`${model.name}Revision`)) {
|
|
959
|
+
up.push(() => {
|
|
960
|
+
this.createRevisionTable(model);
|
|
961
|
+
});
|
|
962
|
+
if (tables.includes(model.name)) {
|
|
963
|
+
up.push(() => {
|
|
964
|
+
writer.block(() => {
|
|
965
|
+
writer.writeLine(`const data = await knex('${model.name}');`);
|
|
966
|
+
writer.write(`if (data.length)`).block(() => {
|
|
967
|
+
writer.write(`await knex.batchInsert('${model.name}Revision', data.map((row) => (`).inlineBlock(() => {
|
|
968
|
+
writer.writeLine(`id: uuid(),`);
|
|
969
|
+
writer.writeLine(`${typeToField(model.name)}Id: row.id,`);
|
|
970
|
+
this.nowUsed = true;
|
|
971
|
+
writer.writeLine(`createdAt: row.updatedAt || row.createdAt || now,`);
|
|
972
|
+
writer.writeLine(`createdById: row.updatedById || row.createdById,`);
|
|
973
|
+
if (model.deletable) {
|
|
974
|
+
writer.writeLine(`deleted: row.deleted,`);
|
|
975
|
+
}
|
|
976
|
+
for (const { name: name2, kind } of model.fields.filter(({ updatable }) => updatable)) {
|
|
977
|
+
const col = kind === "relation" ? `${name2}Id` : name2;
|
|
978
|
+
writer.writeLine(`${col}: row.${col},`);
|
|
979
|
+
}
|
|
980
|
+
}).write(")));").newLine();
|
|
981
|
+
});
|
|
982
|
+
}).blankLine();
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
down.push(() => {
|
|
986
|
+
this.dropTable(`${model.name}Revision`);
|
|
987
|
+
});
|
|
988
|
+
} else {
|
|
989
|
+
const revisionTable = `${model.name}Revision`;
|
|
990
|
+
const missingRevisionFields = model.fields.filter(
|
|
991
|
+
({ name: name2, updatable, ...field }) => field.kind !== "custom" && updatable && !this.columns[revisionTable].some(
|
|
992
|
+
(col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
|
|
993
|
+
)
|
|
994
|
+
);
|
|
995
|
+
this.createRevisionFields(model, missingRevisionFields, up, down);
|
|
996
|
+
const revisionFieldsToRemove = model.fields.filter(
|
|
997
|
+
({ name: name2, updatable, generated, ...field }) => !generated && field.kind !== "custom" && !updatable && !(field.kind === "relation" && field.foreignKey === "id") && this.columns[revisionTable].some(
|
|
998
|
+
(col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
|
|
999
|
+
)
|
|
1000
|
+
);
|
|
1001
|
+
this.createRevisionFields(model, revisionFieldsToRemove, down, up);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
for (const model of getModels(rawModels)) {
|
|
1006
|
+
if (tables.includes(model.name)) {
|
|
1007
|
+
this.createFields(
|
|
1008
|
+
model,
|
|
1009
|
+
model.fields.filter(({ name: name2, deleted }) => deleted && this.columns[model.name].some((col) => col.name === name2)),
|
|
1010
|
+
down,
|
|
1011
|
+
up
|
|
1012
|
+
);
|
|
1013
|
+
if (model.updatable) {
|
|
1014
|
+
this.createRevisionFields(
|
|
1015
|
+
model,
|
|
1016
|
+
model.fields.filter(({ deleted, updatable }) => updatable && deleted),
|
|
1017
|
+
down,
|
|
1018
|
+
up
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
this.createEnums(
|
|
1024
|
+
rawModels.filter(isEnumModel).filter((enm2) => enm2.deleted),
|
|
1025
|
+
down,
|
|
1026
|
+
up
|
|
1027
|
+
);
|
|
1028
|
+
writer.writeLine(`import { Knex } from 'knex';`);
|
|
1029
|
+
if (this.uuidUsed) {
|
|
1030
|
+
writer.writeLine(`import { v4 as uuid } from 'uuid';`);
|
|
1031
|
+
}
|
|
1032
|
+
if (this.nowUsed) {
|
|
1033
|
+
writer.writeLine(`import { date } from '../src/utils/dates';`);
|
|
1034
|
+
}
|
|
1035
|
+
writer.blankLine();
|
|
1036
|
+
if (this.nowUsed) {
|
|
1037
|
+
writer.writeLine(`const now = date();`).blankLine();
|
|
1038
|
+
}
|
|
1039
|
+
this.migration("up", up);
|
|
1040
|
+
this.migration("down", down.reverse());
|
|
1041
|
+
return writer.toString();
|
|
1042
|
+
}
|
|
1043
|
+
renameFields(model, fields2, up, down) {
|
|
1044
|
+
if (!fields2.length) {
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
up.push(() => {
|
|
1048
|
+
for (const field of fields2) {
|
|
1049
|
+
this.alterTable(model.name, () => {
|
|
1050
|
+
this.renameColumn(
|
|
1051
|
+
field.kind === "relation" ? `${field.oldName}Id` : get(field, "oldName"),
|
|
1052
|
+
field.kind === "relation" ? `${field.name}Id` : field.name
|
|
1053
|
+
);
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
down.push(() => {
|
|
1058
|
+
for (const field of fields2) {
|
|
1059
|
+
this.alterTable(model.name, () => {
|
|
1060
|
+
this.renameColumn(
|
|
1061
|
+
field.kind === "relation" ? `${field.name}Id` : field.name,
|
|
1062
|
+
field.kind === "relation" ? `${field.oldName}Id` : get(field, "oldName")
|
|
1063
|
+
);
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
for (const field of fields2) {
|
|
1068
|
+
summonByName(this.columns[model.name], field.kind === "relation" ? `${field.oldName}Id` : field.oldName).name = field.kind === "relation" ? `${field.name}Id` : field.name;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
createFields(model, fields2, up, down) {
|
|
1072
|
+
if (!fields2.length) {
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
up.push(() => {
|
|
1076
|
+
const alter = [];
|
|
1077
|
+
const updates = [];
|
|
1078
|
+
const postAlter = [];
|
|
1079
|
+
for (const field of fields2) {
|
|
1080
|
+
alter.push(() => this.column(field, { setNonNull: field.defaultValue !== void 0 }));
|
|
1081
|
+
if (field.nonNull && field.defaultValue === void 0) {
|
|
1082
|
+
updates.push(() => this.writer.write(`${field.name}: 'TODO',`).newLine());
|
|
1083
|
+
postAlter.push(() => this.column(field, { alter: true, foreign: false }));
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
if (alter.length) {
|
|
1087
|
+
this.alterTable(model.name, () => {
|
|
1088
|
+
alter.map((cb) => cb());
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
if (updates.length) {
|
|
1092
|
+
this.writer.write(`await knex('${model.name}').update(`).inlineBlock(() => {
|
|
1093
|
+
updates.map((cb) => cb());
|
|
1094
|
+
}).write(");").newLine().blankLine();
|
|
1095
|
+
}
|
|
1096
|
+
if (postAlter.length) {
|
|
1097
|
+
this.alterTable(model.name, () => {
|
|
1098
|
+
postAlter.map((cb) => cb());
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
down.push(() => {
|
|
1103
|
+
this.alterTable(model.name, () => {
|
|
1104
|
+
for (const { kind, name: name2 } of fields2) {
|
|
1105
|
+
this.dropColumn(kind === "relation" ? `${name2}Id` : name2);
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
updateFields(model, fields2, up, down) {
|
|
1111
|
+
if (!fields2.length) {
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
up.push(() => {
|
|
1115
|
+
this.alterTable(model.name, () => {
|
|
1116
|
+
for (const field of fields2) {
|
|
1117
|
+
this.column(field, { alter: true });
|
|
1118
|
+
}
|
|
1119
|
+
});
|
|
1120
|
+
});
|
|
1121
|
+
down.push(() => {
|
|
1122
|
+
this.alterTable(model.name, () => {
|
|
1123
|
+
for (const field of fields2) {
|
|
1124
|
+
this.column(
|
|
1125
|
+
field,
|
|
1126
|
+
{ alter: true },
|
|
1127
|
+
summonByName(this.columns[model.name], field.kind === "relation" ? `${field.name}Id` : field.name)
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
});
|
|
1132
|
+
if (model.updatable) {
|
|
1133
|
+
const updatableFields = fields2.filter(({ updatable }) => updatable);
|
|
1134
|
+
if (!updatableFields.length) {
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
up.push(() => {
|
|
1138
|
+
this.alterTable(`${model.name}Revision`, () => {
|
|
1139
|
+
for (const field of updatableFields) {
|
|
1140
|
+
this.column(field, { alter: true });
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
});
|
|
1144
|
+
down.push(() => {
|
|
1145
|
+
this.alterTable(`${model.name}Revision`, () => {
|
|
1146
|
+
for (const field of updatableFields) {
|
|
1147
|
+
this.column(
|
|
1148
|
+
field,
|
|
1149
|
+
{ alter: true },
|
|
1150
|
+
summonByName(this.columns[model.name], field.kind === "relation" ? `${field.name}Id` : field.name)
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
});
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
createRevisionTable(model) {
|
|
1158
|
+
const writer = this.writer;
|
|
1159
|
+
this.createTable(`${model.name}Revision`, () => {
|
|
1160
|
+
writer.writeLine(`table.uuid('id').notNullable().primary();`);
|
|
1161
|
+
writer.writeLine(`table.uuid('${typeToField(model.name)}Id').notNullable();`);
|
|
1162
|
+
writer.write(`table.uuid('createdById')`);
|
|
1163
|
+
writer.write(".notNullable()");
|
|
1164
|
+
writer.write(";").newLine();
|
|
1165
|
+
writer.writeLine(`table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now(0));`);
|
|
1166
|
+
if (model.deletable) {
|
|
1167
|
+
writer.writeLine(`table.boolean('deleted').notNullable();`);
|
|
1168
|
+
}
|
|
1169
|
+
for (const field of model.fields.filter((field2) => field2.updatable)) {
|
|
1170
|
+
this.column(field, { setUnique: false, setDefault: false });
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
createRevisionFields(model, missingRevisionFields, up, down) {
|
|
1175
|
+
const revisionTable = `${model.name}Revision`;
|
|
1176
|
+
if (missingRevisionFields.length) {
|
|
1177
|
+
up.push(() => {
|
|
1178
|
+
this.alterTable(revisionTable, () => {
|
|
1179
|
+
for (const field of missingRevisionFields) {
|
|
1180
|
+
this.column(field, { setUnique: false, setNonNull: false, setDefault: false });
|
|
1181
|
+
}
|
|
1182
|
+
});
|
|
1183
|
+
this.writer.write(`await knex('${model.name}Revision').update(`).inlineBlock(() => {
|
|
1184
|
+
for (const { name: name2, kind: type } of missingRevisionFields) {
|
|
1185
|
+
const col = type === "relation" ? `${name2}Id` : name2;
|
|
1186
|
+
this.writer.write(
|
|
1187
|
+
`${col}: knex.raw('(select "${col}" from "${model.name}" where "${model.name}".id = "${model.name}Revision"."${typeToField(model.name)}Id")'),`
|
|
1188
|
+
).newLine();
|
|
1189
|
+
}
|
|
1190
|
+
}).write(");").newLine().blankLine();
|
|
1191
|
+
const nonNullableMissingRevisionFields = missingRevisionFields.filter(({ nonNull: nonNull2 }) => nonNull2);
|
|
1192
|
+
if (nonNullableMissingRevisionFields.length) {
|
|
1193
|
+
this.alterTable(revisionTable, () => {
|
|
1194
|
+
for (const field of nonNullableMissingRevisionFields) {
|
|
1195
|
+
this.column(field, { setUnique: false, setDefault: false, alter: true });
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
down.push(() => {
|
|
1201
|
+
this.alterTable(revisionTable, () => {
|
|
1202
|
+
for (const field of missingRevisionFields) {
|
|
1203
|
+
this.dropColumn(field.kind === "relation" ? `${field.name}Id` : field.name);
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
createEnums(enums, up, down) {
|
|
1210
|
+
for (const enm2 of enums) {
|
|
1211
|
+
const name2 = (0, import_lowerFirst.default)(enm2.name);
|
|
1212
|
+
up.push(
|
|
1213
|
+
() => this.writer.writeLine(
|
|
1214
|
+
`await knex.raw(\`CREATE TYPE "${name2}" AS ENUM (${enm2.values.map((value2) => `'${value2}'`).join(",")})\`);`
|
|
1215
|
+
).newLine()
|
|
1216
|
+
);
|
|
1217
|
+
down.push(() => this.writer.writeLine(`await knex.raw('DROP TYPE "${name2}"')`));
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
migration(name2, cbs) {
|
|
1221
|
+
return this.writer.write(`export const ${name2} = async (knex: Knex) => `).inlineBlock(() => {
|
|
1222
|
+
cbs.map((cb) => cb());
|
|
1223
|
+
}).write(";").newLine().blankLine();
|
|
1224
|
+
}
|
|
1225
|
+
createTable(table, block) {
|
|
1226
|
+
return this.writer.write(`await knex.schema.createTable('${table}', (table) => `).inlineBlock(block).write(");").newLine().blankLine();
|
|
1227
|
+
}
|
|
1228
|
+
alterTable(table, block) {
|
|
1229
|
+
return this.writer.write(`await knex.schema.alterTable('${table}', (table) => `).inlineBlock(block).write(");").newLine().blankLine();
|
|
1230
|
+
}
|
|
1231
|
+
dropColumn(col) {
|
|
1232
|
+
return this.writer.writeLine(`table.dropColumn('${col}');`);
|
|
1233
|
+
}
|
|
1234
|
+
dropTable(table) {
|
|
1235
|
+
return this.writer.writeLine(`await knex.schema.dropTable('${table}');`).blankLine();
|
|
1236
|
+
}
|
|
1237
|
+
renameTable(from, to) {
|
|
1238
|
+
return this.writer.writeLine(`await knex.schema.renameTable('${from}', '${to}');`).blankLine();
|
|
1239
|
+
}
|
|
1240
|
+
renameColumn(from, to) {
|
|
1241
|
+
this.writer.writeLine(`table.renameColumn('${from}', '${to}')`);
|
|
1242
|
+
}
|
|
1243
|
+
value(value2) {
|
|
1244
|
+
if (typeof value2 === "string") {
|
|
1245
|
+
return `'${value2}'`;
|
|
1246
|
+
}
|
|
1247
|
+
return value2;
|
|
1248
|
+
}
|
|
1249
|
+
column({ name: name2, primary, list: list2, ...field }, { setUnique = true, setNonNull = true, alter = false, foreign = true, setDefault = true } = {}, toColumn) {
|
|
1250
|
+
const col = (what) => {
|
|
1251
|
+
if (what) {
|
|
1252
|
+
this.writer.write(what);
|
|
1253
|
+
}
|
|
1254
|
+
if (setNonNull) {
|
|
1255
|
+
if (toColumn) {
|
|
1256
|
+
if (toColumn.is_nullable) {
|
|
1257
|
+
this.writer.write(`.nullable()`);
|
|
1258
|
+
} else {
|
|
1259
|
+
this.writer.write(".notNullable()");
|
|
1260
|
+
}
|
|
1261
|
+
} else {
|
|
1262
|
+
if (field.nonNull) {
|
|
1263
|
+
this.writer.write(`.notNullable()`);
|
|
1264
|
+
} else {
|
|
1265
|
+
this.writer.write(".nullable()");
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
if (setDefault && field.defaultValue !== void 0) {
|
|
1270
|
+
this.writer.write(`.defaultTo(${this.value(field.defaultValue)})`);
|
|
1271
|
+
}
|
|
1272
|
+
if (primary) {
|
|
1273
|
+
this.writer.write(".primary()");
|
|
1274
|
+
} else if (setUnique && field.unique) {
|
|
1275
|
+
this.writer.write(".unique()");
|
|
1276
|
+
}
|
|
1277
|
+
if (alter) {
|
|
1278
|
+
this.writer.write(".alter()");
|
|
1279
|
+
}
|
|
1280
|
+
this.writer.write(";").newLine();
|
|
1281
|
+
};
|
|
1282
|
+
const kind = field.kind;
|
|
1283
|
+
switch (kind) {
|
|
1284
|
+
case void 0:
|
|
1285
|
+
case "primitive":
|
|
1286
|
+
switch (field.type) {
|
|
1287
|
+
case "Boolean":
|
|
1288
|
+
col(`table.boolean('${name2}')`);
|
|
1289
|
+
break;
|
|
1290
|
+
case "Int":
|
|
1291
|
+
col(`table.integer('${name2}')`);
|
|
1292
|
+
break;
|
|
1293
|
+
case "Float":
|
|
1294
|
+
if (field.double) {
|
|
1295
|
+
col(`table.double('${name2}')`);
|
|
1296
|
+
} else {
|
|
1297
|
+
col(`table.decimal('${name2}', ${get(field, "precision")}, ${get(field, "scale")})`);
|
|
1298
|
+
}
|
|
1299
|
+
break;
|
|
1300
|
+
case "String":
|
|
1301
|
+
if (field.large) {
|
|
1302
|
+
col(`table.text('${name2}')`);
|
|
1303
|
+
} else {
|
|
1304
|
+
col(`table.string('${name2}', ${field.maxLength})`);
|
|
1305
|
+
}
|
|
1306
|
+
break;
|
|
1307
|
+
case "DateTime":
|
|
1308
|
+
col(`table.timestamp('${name2}')`);
|
|
1309
|
+
break;
|
|
1310
|
+
case "ID":
|
|
1311
|
+
col(`table.uuid('${name2}')`);
|
|
1312
|
+
break;
|
|
1313
|
+
case "Upload":
|
|
1314
|
+
break;
|
|
1315
|
+
}
|
|
1316
|
+
break;
|
|
1317
|
+
case "relation":
|
|
1318
|
+
col(`table.uuid('${name2}Id')`);
|
|
1319
|
+
if (foreign && !alter) {
|
|
1320
|
+
this.writer.writeLine(`table.foreign('${name2}Id').references('id').inTable('${field.type}');`);
|
|
1321
|
+
}
|
|
1322
|
+
break;
|
|
1323
|
+
case "enum":
|
|
1324
|
+
if (list2) {
|
|
1325
|
+
this.writer.write(`table.specificType('${name2}', '"${typeToField(field.type)}"[]');`);
|
|
1326
|
+
} else {
|
|
1327
|
+
this.writer.write(`table.enum('${name2}', null as any, `).inlineBlock(() => {
|
|
1328
|
+
this.writer.writeLine(`useNative: true,`);
|
|
1329
|
+
this.writer.writeLine(`existingType: true,`);
|
|
1330
|
+
this.writer.writeLine(`enumName: '${typeToField(field.type)}',`);
|
|
1331
|
+
}).write(")");
|
|
1332
|
+
}
|
|
1333
|
+
col();
|
|
1334
|
+
break;
|
|
1335
|
+
case "json":
|
|
1336
|
+
this.writer.write(`table.json('${typeToField(field.type)}')`);
|
|
1337
|
+
break;
|
|
1338
|
+
case "custom":
|
|
1339
|
+
throw new Error("Custom fields aren't stored in the database");
|
|
1340
|
+
default: {
|
|
1341
|
+
const exhaustiveCheck = kind;
|
|
1342
|
+
throw new Error(exhaustiveCheck);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
var getMigrationDate = () => {
|
|
1348
|
+
const date = /* @__PURE__ */ new Date();
|
|
1349
|
+
const year = date.getFullYear();
|
|
1350
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
1351
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
1352
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
1353
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1354
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
1355
|
+
return `${year}${month}${day}${hours}${minutes}${seconds}`;
|
|
1356
|
+
};
|
|
1357
|
+
|
|
1358
|
+
// src/errors.ts
|
|
1359
|
+
var import_graphql2 = require("graphql");
|
|
1360
|
+
|
|
1361
|
+
// src/resolvers/utils.ts
|
|
1362
|
+
var import_graphql3 = require("graphql");
|
|
1363
|
+
|
|
1364
|
+
// src/resolvers/arguments.ts
|
|
1365
|
+
var import_graphql4 = require("graphql");
|
|
1366
|
+
|
|
1367
|
+
// src/resolvers/mutations.ts
|
|
1368
|
+
var import_uuid = require("uuid");
|
|
1369
|
+
|
|
1370
|
+
// src/resolvers/resolver.ts
|
|
1371
|
+
var import_cloneDeep = __toESM(require("lodash/cloneDeep"), 1);
|
|
1372
|
+
var import_flatMap = __toESM(require("lodash/flatMap"), 1);
|
|
1373
|
+
|
|
1374
|
+
// src/schema/generate.ts
|
|
1375
|
+
var import_graphql5 = require("graphql");
|
|
1376
|
+
var import_flatMap2 = __toESM(require("lodash/flatMap"), 1);
|
|
1377
|
+
|
|
1378
|
+
// src/schema/utils.ts
|
|
1379
|
+
var import_luxon = require("luxon");
|
|
1380
|
+
|
|
1381
|
+
// src/values.ts
|
|
1382
|
+
var Enum = class {
|
|
1383
|
+
constructor(value2) {
|
|
1384
|
+
this.value = value2;
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
// src/schema/utils.ts
|
|
1389
|
+
var document = (definitions) => ({
|
|
1390
|
+
kind: "Document",
|
|
1391
|
+
definitions
|
|
1392
|
+
});
|
|
1393
|
+
var scalar = (nme) => ({
|
|
1394
|
+
name: name(nme),
|
|
1395
|
+
kind: "ScalarTypeDefinition"
|
|
1396
|
+
});
|
|
1397
|
+
var input = (nme, fields2, dvs) => ({
|
|
1398
|
+
name: name(nme),
|
|
1399
|
+
fields: inputValues(fields2),
|
|
1400
|
+
kind: "InputObjectTypeDefinition",
|
|
1401
|
+
directives: directives(dvs)
|
|
1402
|
+
});
|
|
1403
|
+
var object = (nme, fds, interfaces, dvs) => ({
|
|
1404
|
+
name: name(nme),
|
|
1405
|
+
fields: fields(fds),
|
|
1406
|
+
kind: "ObjectTypeDefinition",
|
|
1407
|
+
interfaces: interfaces && interfaces.map((i) => namedType(i)),
|
|
1408
|
+
directives: directives(dvs)
|
|
1409
|
+
});
|
|
1410
|
+
var inputValues = (fields2) => fields2.map(
|
|
1411
|
+
(field) => ({
|
|
1412
|
+
kind: "InputValueDefinition",
|
|
1413
|
+
name: name(field.name),
|
|
1414
|
+
type: fieldType(field),
|
|
1415
|
+
defaultValue: field.defaultValue === void 0 ? void 0 : value(field.defaultValue),
|
|
1416
|
+
directives: directives(field.directives)
|
|
1417
|
+
})
|
|
1418
|
+
);
|
|
1419
|
+
var fields = (fields2) => fields2.map(
|
|
1420
|
+
(field) => ({
|
|
1421
|
+
kind: "FieldDefinition",
|
|
1422
|
+
name: name(field.name),
|
|
1423
|
+
type: fieldType(field),
|
|
1424
|
+
arguments: field.args?.map((arg) => ({
|
|
1425
|
+
kind: "InputValueDefinition",
|
|
1426
|
+
name: name(arg.name),
|
|
1427
|
+
type: fieldType(arg),
|
|
1428
|
+
defaultValue: arg.defaultValue === void 0 ? void 0 : value(arg.defaultValue)
|
|
1429
|
+
})),
|
|
1430
|
+
directives: directives(field.directives)
|
|
1431
|
+
})
|
|
1432
|
+
);
|
|
1433
|
+
var directives = (directives2) => directives2?.map(
|
|
1434
|
+
(directive) => ({
|
|
1435
|
+
kind: "Directive",
|
|
1436
|
+
name: name(directive.name),
|
|
1437
|
+
arguments: args(directive.values)
|
|
1438
|
+
})
|
|
1439
|
+
);
|
|
1440
|
+
var args = (ags) => ags?.map(
|
|
1441
|
+
(argument) => ({
|
|
1442
|
+
kind: "Argument",
|
|
1443
|
+
name: name(argument.name),
|
|
1444
|
+
value: value(argument.values)
|
|
1445
|
+
})
|
|
1446
|
+
);
|
|
1447
|
+
var enm = (nme, values) => ({
|
|
1448
|
+
name: name(nme),
|
|
1449
|
+
kind: "EnumTypeDefinition",
|
|
1450
|
+
values: values.map(
|
|
1451
|
+
(v) => ({
|
|
1452
|
+
kind: "EnumValueDefinition",
|
|
1453
|
+
name: name(v)
|
|
1454
|
+
})
|
|
1455
|
+
)
|
|
1456
|
+
});
|
|
1457
|
+
var nonNull = (type) => ({
|
|
1458
|
+
type,
|
|
1459
|
+
kind: "NonNullType"
|
|
1460
|
+
});
|
|
1461
|
+
var namedType = (nme) => ({
|
|
1462
|
+
kind: "NamedType",
|
|
1463
|
+
name: name(nme)
|
|
1464
|
+
});
|
|
1465
|
+
var list = (type) => ({
|
|
1466
|
+
type,
|
|
1467
|
+
kind: "ListType"
|
|
1468
|
+
});
|
|
1469
|
+
var name = (name2) => ({
|
|
1470
|
+
kind: "Name",
|
|
1471
|
+
value: name2
|
|
1472
|
+
});
|
|
1473
|
+
var fieldType = (field) => {
|
|
1474
|
+
let type = namedType(field.type);
|
|
1475
|
+
if (field.list) {
|
|
1476
|
+
type = list(nonNull(type));
|
|
1477
|
+
}
|
|
1478
|
+
if (field.nonNull) {
|
|
1479
|
+
type = nonNull(type);
|
|
1480
|
+
}
|
|
1481
|
+
return type;
|
|
1482
|
+
};
|
|
1483
|
+
var value = (val = null) => val === null ? {
|
|
1484
|
+
kind: "NullValue"
|
|
1485
|
+
} : typeof val === "boolean" ? {
|
|
1486
|
+
kind: "BooleanValue",
|
|
1487
|
+
value: val
|
|
1488
|
+
} : typeof val === "number" ? {
|
|
1489
|
+
kind: "IntValue",
|
|
1490
|
+
value: `${val}`
|
|
1491
|
+
} : typeof val === "string" ? {
|
|
1492
|
+
kind: "StringValue",
|
|
1493
|
+
value: val
|
|
1494
|
+
} : val instanceof Enum ? {
|
|
1495
|
+
kind: "EnumValue",
|
|
1496
|
+
value: val.value
|
|
1497
|
+
} : Array.isArray(val) ? {
|
|
1498
|
+
kind: "ListValue",
|
|
1499
|
+
values: val.map(value)
|
|
1500
|
+
} : val instanceof import_luxon.DateTime ? {
|
|
1501
|
+
kind: "StringValue",
|
|
1502
|
+
value: val.toString()
|
|
1503
|
+
} : {
|
|
1504
|
+
kind: "ObjectValue",
|
|
1505
|
+
fields: Object.keys(val).map(
|
|
1506
|
+
(nme) => ({
|
|
1507
|
+
kind: "ObjectField",
|
|
1508
|
+
name: name(nme),
|
|
1509
|
+
value: value(val[nme])
|
|
1510
|
+
})
|
|
1511
|
+
)
|
|
1512
|
+
};
|
|
1513
|
+
|
|
1514
|
+
// src/schema/generate.ts
|
|
1515
|
+
var generateDefinitions = (rawModels) => {
|
|
1516
|
+
const models = getModels(rawModels);
|
|
1517
|
+
return [
|
|
1518
|
+
// Predefined types
|
|
1519
|
+
enm("Order", ["ASC", "DESC"]),
|
|
1520
|
+
scalar("DateTime"),
|
|
1521
|
+
scalar("Upload"),
|
|
1522
|
+
...rawModels.filter(isEnumModel).map((model) => enm(model.name, model.values)),
|
|
1523
|
+
...rawModels.filter(isRawEnumModel).map((model) => enm(model.name, model.values)),
|
|
1524
|
+
...rawModels.filter(isScalarModel).map((model) => scalar(model.name)),
|
|
1525
|
+
...rawModels.filter(isObjectModel).filter(({ name: name2 }) => !["Query", "Mutation"].includes(name2)).map((model) => object(model.name, model.fields)),
|
|
1526
|
+
...rawModels.filter(isInputModel).map((model) => input(model.name, model.fields)),
|
|
1527
|
+
...rawModels.filter(isObjectModel).filter(
|
|
1528
|
+
(model) => models.some((m) => m.creatable && m.fields.some((f) => f.creatable && f.kind === "json" && f.type === model.name))
|
|
1529
|
+
).map((model) => input(`Create${model.name}`, model.fields)),
|
|
1530
|
+
...rawModels.filter(isObjectModel).filter(
|
|
1531
|
+
(model) => models.some((m) => m.creatable && m.fields.some((f) => f.creatable && f.kind === "json" && f.type === model.name))
|
|
1532
|
+
).map((model) => input(`Update${model.name}`, model.fields)),
|
|
1533
|
+
...(0, import_flatMap2.default)(
|
|
1534
|
+
models.map((model) => {
|
|
1535
|
+
const types = [
|
|
1536
|
+
object(
|
|
1537
|
+
model.name,
|
|
1538
|
+
[
|
|
1539
|
+
...model.fields.filter(isQueriableField).map((field) => ({
|
|
1540
|
+
...field,
|
|
1541
|
+
type: field.type,
|
|
1542
|
+
args: [...field.args || []],
|
|
1543
|
+
directives: field.directives
|
|
1544
|
+
})),
|
|
1545
|
+
...model.reverseRelations.map(({ name: name2, field, model: model2 }) => ({
|
|
1546
|
+
name: name2,
|
|
1547
|
+
type: model2.name,
|
|
1548
|
+
list: !field.toOne,
|
|
1549
|
+
nonNull: !field.toOne,
|
|
1550
|
+
args: [
|
|
1551
|
+
{ name: "where", type: `${model2.name}Where` },
|
|
1552
|
+
...model2.fields.some(({ searchable }) => searchable) ? [{ name: "search", type: "String" }] : [],
|
|
1553
|
+
...model2.fields.some(({ orderable }) => orderable) ? [{ name: "orderBy", type: `${model2.name}OrderBy`, list: true }] : [],
|
|
1554
|
+
{ name: "limit", type: "Int" },
|
|
1555
|
+
{ name: "offset", type: "Int" }
|
|
1556
|
+
]
|
|
1557
|
+
}))
|
|
1558
|
+
],
|
|
1559
|
+
model.interfaces
|
|
1560
|
+
),
|
|
1561
|
+
input(`${model.name}Where`, [
|
|
1562
|
+
...model.fields.filter(({ kind, unique, filterable }) => (unique || filterable) && kind !== "relation").map((field) => ({
|
|
1563
|
+
name: field.name,
|
|
1564
|
+
type: field.type,
|
|
1565
|
+
list: true,
|
|
1566
|
+
default: typeof field.filterable === "object" ? field.filterable.default : void 0
|
|
1567
|
+
})),
|
|
1568
|
+
...(0, import_flatMap2.default)(
|
|
1569
|
+
model.fields.filter(({ comparable }) => comparable),
|
|
1570
|
+
(field) => [
|
|
1571
|
+
{ name: `${field.name}_GT`, type: field.type },
|
|
1572
|
+
{ name: `${field.name}_GTE`, type: field.type },
|
|
1573
|
+
{ name: `${field.name}_LT`, type: field.type },
|
|
1574
|
+
{ name: `${field.name}_LTE`, type: field.type }
|
|
1575
|
+
]
|
|
1576
|
+
),
|
|
1577
|
+
...model.fields.filter(isRelation).filter(({ filterable }) => filterable).map(({ name: name2, type }) => ({
|
|
1578
|
+
name: name2,
|
|
1579
|
+
type: `${type}Where`
|
|
1580
|
+
}))
|
|
1581
|
+
]),
|
|
1582
|
+
input(
|
|
1583
|
+
`${model.name}WhereUnique`,
|
|
1584
|
+
model.fields.filter(({ unique }) => unique).map((field) => ({ name: field.name, type: field.type }))
|
|
1585
|
+
),
|
|
1586
|
+
...model.fields.some(({ orderable }) => orderable) ? [
|
|
1587
|
+
input(
|
|
1588
|
+
`${model.name}OrderBy`,
|
|
1589
|
+
model.fields.filter(({ orderable }) => orderable).map(({ name: name2 }) => ({ name: name2, type: "Order" }))
|
|
1590
|
+
)
|
|
1591
|
+
] : []
|
|
1592
|
+
];
|
|
1593
|
+
if (model.creatable) {
|
|
1594
|
+
types.push(
|
|
1595
|
+
input(
|
|
1596
|
+
`Create${model.name}`,
|
|
1597
|
+
model.fields.filter(({ creatable }) => creatable).map(
|
|
1598
|
+
(field) => field.kind === "relation" ? { name: `${field.name}Id`, type: "ID", nonNull: field.nonNull } : {
|
|
1599
|
+
name: field.name,
|
|
1600
|
+
type: field.kind === "json" ? `Create${field.type}` : field.type,
|
|
1601
|
+
list: field.list,
|
|
1602
|
+
nonNull: field.nonNull && field.defaultValue === void 0
|
|
1603
|
+
}
|
|
1604
|
+
)
|
|
1605
|
+
)
|
|
1606
|
+
);
|
|
1607
|
+
}
|
|
1608
|
+
if (model.updatable) {
|
|
1609
|
+
types.push(
|
|
1610
|
+
input(
|
|
1611
|
+
`Update${model.name}`,
|
|
1612
|
+
model.fields.filter(({ updatable }) => updatable).map(
|
|
1613
|
+
(field) => field.kind === "relation" ? { name: `${field.name}Id`, type: "ID" } : {
|
|
1614
|
+
name: field.name,
|
|
1615
|
+
type: field.kind === "json" ? `Update${field.type}` : field.type,
|
|
1616
|
+
list: field.list
|
|
1617
|
+
}
|
|
1618
|
+
)
|
|
1619
|
+
)
|
|
1620
|
+
);
|
|
1621
|
+
}
|
|
1622
|
+
return types;
|
|
1623
|
+
})
|
|
1624
|
+
),
|
|
1625
|
+
object("Query", [
|
|
1626
|
+
{
|
|
1627
|
+
name: "me",
|
|
1628
|
+
type: "User"
|
|
1629
|
+
},
|
|
1630
|
+
...models.filter(({ queriable }) => queriable).map(({ name: name2 }) => ({
|
|
1631
|
+
name: typeToField(name2),
|
|
1632
|
+
type: name2,
|
|
1633
|
+
nonNull: true,
|
|
1634
|
+
args: [
|
|
1635
|
+
{
|
|
1636
|
+
name: "where",
|
|
1637
|
+
type: `${name2}WhereUnique`,
|
|
1638
|
+
nonNull: true
|
|
1639
|
+
}
|
|
1640
|
+
]
|
|
1641
|
+
})),
|
|
1642
|
+
...models.filter(({ listQueriable }) => listQueriable).map((model) => ({
|
|
1643
|
+
name: getModelPluralField(model),
|
|
1644
|
+
type: model.name,
|
|
1645
|
+
list: true,
|
|
1646
|
+
nonNull: true,
|
|
1647
|
+
args: [
|
|
1648
|
+
{ name: "where", type: `${model.name}Where` },
|
|
1649
|
+
...model.fields.some(({ searchable }) => searchable) ? [{ name: "search", type: "String" }] : [],
|
|
1650
|
+
...model.fields.some(({ orderable }) => orderable) ? [{ name: "orderBy", type: `${model.name}OrderBy`, list: true }] : [],
|
|
1651
|
+
{ name: "limit", type: "Int" },
|
|
1652
|
+
{ name: "offset", type: "Int" }
|
|
1653
|
+
]
|
|
1654
|
+
})),
|
|
1655
|
+
...rawModels.filter(isObjectModel).filter((model) => model.name === "Query").flatMap((model) => model.fields)
|
|
1656
|
+
]),
|
|
1657
|
+
object("Mutation", [
|
|
1658
|
+
...(0, import_flatMap2.default)(
|
|
1659
|
+
models.map((model) => {
|
|
1660
|
+
const mutations = [];
|
|
1661
|
+
if (model.creatable) {
|
|
1662
|
+
mutations.push({
|
|
1663
|
+
name: `create${model.name}`,
|
|
1664
|
+
type: model.name,
|
|
1665
|
+
nonNull: true,
|
|
1666
|
+
args: [
|
|
1667
|
+
{
|
|
1668
|
+
name: "data",
|
|
1669
|
+
type: `Create${model.name}`,
|
|
1670
|
+
nonNull: true
|
|
1671
|
+
}
|
|
1672
|
+
]
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
if (model.updatable) {
|
|
1676
|
+
mutations.push({
|
|
1677
|
+
name: `update${model.name}`,
|
|
1678
|
+
type: model.name,
|
|
1679
|
+
nonNull: true,
|
|
1680
|
+
args: [
|
|
1681
|
+
{
|
|
1682
|
+
name: "where",
|
|
1683
|
+
type: `${model.name}WhereUnique`,
|
|
1684
|
+
nonNull: true
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
name: "data",
|
|
1688
|
+
type: `Update${model.name}`,
|
|
1689
|
+
nonNull: true
|
|
1690
|
+
}
|
|
1691
|
+
]
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
if (model.deletable) {
|
|
1695
|
+
mutations.push({
|
|
1696
|
+
name: `delete${model.name}`,
|
|
1697
|
+
type: "ID",
|
|
1698
|
+
nonNull: true,
|
|
1699
|
+
args: [
|
|
1700
|
+
{
|
|
1701
|
+
name: "where",
|
|
1702
|
+
type: `${model.name}WhereUnique`,
|
|
1703
|
+
nonNull: true
|
|
1704
|
+
},
|
|
1705
|
+
{
|
|
1706
|
+
name: "dryRun",
|
|
1707
|
+
type: "Boolean"
|
|
1708
|
+
}
|
|
1709
|
+
]
|
|
1710
|
+
});
|
|
1711
|
+
mutations.push({
|
|
1712
|
+
name: `restore${model.name}`,
|
|
1713
|
+
type: "ID",
|
|
1714
|
+
nonNull: true,
|
|
1715
|
+
args: [
|
|
1716
|
+
{
|
|
1717
|
+
name: "where",
|
|
1718
|
+
type: `${model.name}WhereUnique`,
|
|
1719
|
+
nonNull: true
|
|
1720
|
+
}
|
|
1721
|
+
]
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
return mutations;
|
|
1725
|
+
})
|
|
1726
|
+
),
|
|
1727
|
+
...rawModels.filter(isObjectModel).filter((model) => model.name === "Mutation").flatMap((model) => model.fields)
|
|
1728
|
+
])
|
|
1729
|
+
];
|
|
1730
|
+
};
|
|
1731
|
+
var generate = (rawModels) => document(generateDefinitions(rawModels));
|
|
1732
|
+
var printSchema = (schema) => [
|
|
1733
|
+
...schema.getDirectives().map((d) => d.astNode && (0, import_graphql5.print)(d.astNode)),
|
|
1734
|
+
...Object.values(schema.getTypeMap()).filter((t) => !t.name.match(/^__/)).sort((a, b) => a.name > b.name ? 1 : -1).map((t) => t.astNode && (0, import_graphql5.print)(t.astNode))
|
|
1735
|
+
].filter(Boolean).map((s) => `${s}
|
|
1736
|
+
`).join("\n");
|
|
1737
|
+
var printSchemaFromModels = (models) => printSchema((0, import_graphql5.buildASTSchema)(generate(models)));
|
|
1738
|
+
|
|
1739
|
+
// src/bin/gqm.ts
|
|
1740
|
+
(0, import_dotenv.config)({
|
|
1741
|
+
path: ".env"
|
|
1742
|
+
});
|
|
1743
|
+
(0, import_dotenv.config)({
|
|
1744
|
+
path: ".env.local"
|
|
1745
|
+
});
|
|
1746
|
+
import_commander.program.description("The graphql-magic cli.");
|
|
1747
|
+
import_commander.program.command("setup").description("Set up the project").action(async () => {
|
|
1748
|
+
await getSettings();
|
|
1749
|
+
ensureFileExists(KNEXFILE_PATH, KNEXFILE);
|
|
1750
|
+
});
|
|
1751
|
+
import_commander.program.command("generate").description("Generate all the things").action(async () => {
|
|
1752
|
+
const rawModels = await parseModels();
|
|
1753
|
+
const generatedFolderPath = await getSetting("generatedFolderPath");
|
|
1754
|
+
writeToFile(`${generatedFolderPath}/schema.graphql`, printSchemaFromModels(rawModels));
|
|
1755
|
+
writeToFile(`${generatedFolderPath}/client/mutations.ts`, generateMutations(rawModels));
|
|
1756
|
+
writeToFile(`${generatedFolderPath}/db/index.ts`, generateDBModels(rawModels));
|
|
1757
|
+
writeToFile(`${generatedFolderPath}/db/knex.ts`, generateKnexTables(rawModels));
|
|
1758
|
+
await generateGraphqlApiTypes();
|
|
1759
|
+
await generateGraphqlClientTypes();
|
|
1760
|
+
});
|
|
1761
|
+
import_commander.program.command("generate-models").description("Generate models.json").action(async () => {
|
|
1762
|
+
const rawModels = await parseModels();
|
|
1763
|
+
const generatedFolderPath = await getSetting("generatedFolderPath");
|
|
1764
|
+
writeToFile(`${generatedFolderPath}/models.json`, JSON.stringify(rawModels, null, 2));
|
|
1765
|
+
});
|
|
1766
|
+
import_commander.program.command("generate-schema").description("Generate schema").action(async () => {
|
|
1767
|
+
const rawModels = await parseModels();
|
|
1768
|
+
const generatedFolderPath = await getSetting("generatedFolderPath");
|
|
1769
|
+
writeToFile(`${generatedFolderPath}/schema.graphql`, printSchemaFromModels(rawModels));
|
|
1770
|
+
});
|
|
1771
|
+
import_commander.program.command("generate-mutation-queries").description("Generate mutation-queries").action(async () => {
|
|
1772
|
+
const rawModels = await parseModels();
|
|
1773
|
+
const generatedFolderPath = await getSetting("generatedFolderPath");
|
|
1774
|
+
writeToFile(`${generatedFolderPath}/client/mutations.ts`, generateMutations(rawModels));
|
|
1775
|
+
});
|
|
1776
|
+
import_commander.program.command("generate-db-types").description("Generate DB types").action(async () => {
|
|
1777
|
+
const rawModels = await parseModels();
|
|
1778
|
+
const generatedFolderPath = await getSetting("generatedFolderPath");
|
|
1779
|
+
writeToFile(`${generatedFolderPath}/db/index.ts`, generateMutations(rawModels));
|
|
1780
|
+
});
|
|
1781
|
+
import_commander.program.command("generate-knex-types").description("Generate Knex types").action(async () => {
|
|
1782
|
+
const rawModels = await parseModels();
|
|
1783
|
+
const generatedFolderPath = await getSetting("generatedFolderPath");
|
|
1784
|
+
writeToFile(`${generatedFolderPath}/db/knex.ts`, generateKnexTables(rawModels));
|
|
1785
|
+
});
|
|
1786
|
+
import_commander.program.command("generate-graphql-api-types").description("Generate Graphql API types").action(async () => {
|
|
1787
|
+
await generateGraphqlApiTypes();
|
|
1788
|
+
});
|
|
1789
|
+
import_commander.program.command("generate-graphql-client-types").description("Generate Graphql client types").action(async () => {
|
|
1790
|
+
await generateGraphqlClientTypes();
|
|
1791
|
+
});
|
|
1792
|
+
import_commander.program.command("generate-migration").description("Generate Migration").action(async () => {
|
|
1793
|
+
const git = (0, import_simple_git.simpleGit)();
|
|
1794
|
+
let name2 = process.argv[2] || (await git.branch()).current.split("/").pop();
|
|
1795
|
+
if (name2 && ["staging", "production"].includes(name2)) {
|
|
1796
|
+
name2 = await readLine("Migration name:");
|
|
1797
|
+
}
|
|
1798
|
+
const knexfile = await parseKnexfile();
|
|
1799
|
+
const db = (0, import_knex.default)(knexfile);
|
|
1800
|
+
try {
|
|
1801
|
+
const rawModels = await parseModels();
|
|
1802
|
+
const migrations = await new MigrationGenerator(db, rawModels).generate();
|
|
1803
|
+
writeToFile(`migrations/${getMigrationDate()}_${name2}.ts`, migrations);
|
|
1804
|
+
} finally {
|
|
1805
|
+
await db.destroy();
|
|
1806
|
+
}
|
|
1807
|
+
});
|
|
1808
|
+
import_commander.program.command("*", { noHelp: true }).description("Invalid command").action(() => {
|
|
1809
|
+
console.error("Invalid command: %s\nSee --help for a list of available commands.", import_commander.program.args.join(" "));
|
|
1810
|
+
process.exit(1);
|
|
1811
|
+
});
|
|
1812
|
+
import_commander.program.parse(process.argv);
|