zenstack 0.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.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/bin/cli +3 -0
  3. package/out/cli/cli-util.js +64 -0
  4. package/out/cli/cli-util.js.map +1 -0
  5. package/out/cli/generator.js +1 -0
  6. package/out/cli/generator.js.map +1 -0
  7. package/out/cli/index.js +90 -0
  8. package/out/cli/index.js.map +1 -0
  9. package/out/extension.js +81 -0
  10. package/out/extension.js.map +1 -0
  11. package/out/generator/data-server/index.js +1 -0
  12. package/out/generator/data-server/index.js.map +1 -0
  13. package/out/generator/next-auth/index.js +196 -0
  14. package/out/generator/next-auth/index.js.map +1 -0
  15. package/out/generator/prisma/index.js +212 -0
  16. package/out/generator/prisma/index.js.map +1 -0
  17. package/out/generator/prisma/prisma-builder.js +307 -0
  18. package/out/generator/prisma/prisma-builder.js.map +1 -0
  19. package/out/generator/react-hooks/index.js +258 -0
  20. package/out/generator/react-hooks/index.js.map +1 -0
  21. package/out/generator/server/data/data-generator.js +376 -0
  22. package/out/generator/server/data/data-generator.js.map +1 -0
  23. package/out/generator/server/data/expression-writer.js +281 -0
  24. package/out/generator/server/data/expression-writer.js.map +1 -0
  25. package/out/generator/server/data/plain-expression-builder.js +53 -0
  26. package/out/generator/server/data/plain-expression-builder.js.map +1 -0
  27. package/out/generator/server/data-generator.js +82 -0
  28. package/out/generator/server/data-generator.js.map +1 -0
  29. package/out/generator/server/expression-writer.js +1 -0
  30. package/out/generator/server/expression-writer.js.map +1 -0
  31. package/out/generator/server/function/function-generator.js +50 -0
  32. package/out/generator/server/function/function-generator.js.map +1 -0
  33. package/out/generator/server/function-generator.js +13 -0
  34. package/out/generator/server/function-generator.js.map +1 -0
  35. package/out/generator/server/index.js +88 -0
  36. package/out/generator/server/index.js.map +1 -0
  37. package/out/generator/server/js-expression-builder.js +1 -0
  38. package/out/generator/server/js-expression-builder.js.map +1 -0
  39. package/out/generator/server/plain-expression-builder.js +1 -0
  40. package/out/generator/server/plain-expression-builder.js.map +1 -0
  41. package/out/generator/server/server-code-generator.js +3 -0
  42. package/out/generator/server/server-code-generator.js.map +1 -0
  43. package/out/generator/server/server-code-writer.js +1 -0
  44. package/out/generator/server/server-code-writer.js.map +1 -0
  45. package/out/generator/service/index.js +72 -0
  46. package/out/generator/service/index.js.map +1 -0
  47. package/out/generator/types.js +10 -0
  48. package/out/generator/types.js.map +1 -0
  49. package/out/generator/utils.js +10 -0
  50. package/out/generator/utils.js.map +1 -0
  51. package/out/language-server/generated/ast.js +386 -0
  52. package/out/language-server/generated/ast.js.map +1 -0
  53. package/out/language-server/generated/grammar.js +2193 -0
  54. package/out/language-server/generated/grammar.js.map +1 -0
  55. package/out/language-server/generated/module.js +23 -0
  56. package/out/language-server/generated/module.js.map +1 -0
  57. package/out/language-server/main.js +12 -0
  58. package/out/language-server/main.js.map +1 -0
  59. package/out/language-server/stdlib.zmodel +21 -0
  60. package/out/language-server/types.js +3 -0
  61. package/out/language-server/types.js.map +1 -0
  62. package/out/language-server/zmodel-index.js +38 -0
  63. package/out/language-server/zmodel-index.js.map +1 -0
  64. package/out/language-server/zmodel-linker.js +239 -0
  65. package/out/language-server/zmodel-linker.js.map +1 -0
  66. package/out/language-server/zmodel-module.js +51 -0
  67. package/out/language-server/zmodel-module.js.map +1 -0
  68. package/out/language-server/zmodel-scope.js +30 -0
  69. package/out/language-server/zmodel-scope.js.map +1 -0
  70. package/out/language-server/zmodel-validator.js +25 -0
  71. package/out/language-server/zmodel-validator.js.map +1 -0
  72. package/out/utils/indent-string.js +25 -0
  73. package/out/utils/indent-string.js.map +1 -0
  74. package/package.json +94 -0
  75. package/src/cli/cli-util.ts +80 -0
  76. package/src/cli/index.ts +80 -0
  77. package/src/extension.ts +76 -0
  78. package/src/generator/next-auth/index.ts +183 -0
  79. package/src/generator/prisma/index.ts +323 -0
  80. package/src/generator/prisma/prisma-builder.ts +366 -0
  81. package/src/generator/react-hooks/index.ts +267 -0
  82. package/src/generator/server/data/data-generator.ts +483 -0
  83. package/src/generator/server/data/expression-writer.ts +350 -0
  84. package/src/generator/server/data/plain-expression-builder.ts +72 -0
  85. package/src/generator/server/function/function-generator.ts +32 -0
  86. package/src/generator/server/index.ts +57 -0
  87. package/src/generator/server/server-code-generator.ts +6 -0
  88. package/src/generator/service/index.ts +43 -0
  89. package/src/generator/types.ts +16 -0
  90. package/src/generator/utils.ts +9 -0
  91. package/src/language-server/generated/ast.ts +603 -0
  92. package/src/language-server/generated/grammar.ts +2190 -0
  93. package/src/language-server/generated/module.ts +24 -0
  94. package/src/language-server/main.ts +12 -0
  95. package/src/language-server/stdlib.zmodel +21 -0
  96. package/src/language-server/types.ts +9 -0
  97. package/src/language-server/zmodel-index.ts +33 -0
  98. package/src/language-server/zmodel-linker.ts +407 -0
  99. package/src/language-server/zmodel-module.ts +90 -0
  100. package/src/language-server/zmodel-scope.ts +21 -0
  101. package/src/language-server/zmodel-validator.ts +35 -0
  102. package/src/language-server/zmodel.langium +186 -0
  103. package/src/utils/indent-string.ts +41 -0
@@ -0,0 +1,267 @@
1
+ import { Context, Generator } from '../types';
2
+ import { Project } from 'ts-morph';
3
+ import * as path from 'path';
4
+ import { camelCase, paramCase } from 'change-case';
5
+ import { DataModel } from '@lang/generated/ast';
6
+ import colors from 'colors';
7
+ import { extractDataModelsWithAllowRules } from '../utils';
8
+
9
+ export default class ReactHooksGenerator implements Generator {
10
+ async generate(context: Context) {
11
+ const project = new Project();
12
+
13
+ const models = extractDataModelsWithAllowRules(context.schema);
14
+
15
+ this.generateIndex(project, context, models);
16
+ this.generateRequestRuntime(project, context);
17
+
18
+ models.forEach((d) => this.generateModelHooks(project, context, d));
19
+
20
+ await project.save();
21
+
22
+ console.log(colors.blue(' ✔️ React hooks generated'));
23
+ }
24
+
25
+ private generateRequestRuntime(project: Project, context: Context) {
26
+ const content = `
27
+ import useSWR, { useSWRConfig } from 'swr';
28
+ import type { ScopedMutator } from 'swr/dist/types';
29
+
30
+ const fetcher = async (url: string, options?: RequestInit) => {
31
+ const res = await fetch(url, options);
32
+ if (!res.ok) {
33
+ const error: Error & { info?: any; status?: number } = new Error(
34
+ 'An error occurred while fetching the data.'
35
+ );
36
+ error.info = await res.json();
37
+ error.status = res.status;
38
+ throw error;
39
+ }
40
+ return res.json();
41
+ };
42
+
43
+ function makeUrl(url: string, args: unknown) {
44
+ return args ? url + \`q=\${encodeURIComponent(JSON.stringify(args))}\` : url;
45
+ }
46
+
47
+ export function get<Data, Error = any>(url: string, args?: unknown) {
48
+ return useSWR<Data, Error>(makeUrl(url, args), fetcher);
49
+ }
50
+
51
+ export async function post<Data, Result>(
52
+ url: string,
53
+ data: Data,
54
+ mutate: ScopedMutator<any>
55
+ ) {
56
+ const r: Result = await fetcher(url, {
57
+ method: 'POST',
58
+ headers: {
59
+ 'content-type': 'application/json',
60
+ },
61
+ body: JSON.stringify(data),
62
+ });
63
+ mutate(url);
64
+ return r;
65
+ }
66
+
67
+ export async function put<Data, Result>(
68
+ url: string,
69
+ data: Data,
70
+ mutate: ScopedMutator<any>
71
+ ) {
72
+ const r: Result = await fetcher(url, {
73
+ method: 'PUT',
74
+ headers: {
75
+ 'content-type': 'application/json',
76
+ },
77
+ body: JSON.stringify(data),
78
+ });
79
+ mutate(url, r);
80
+ return r;
81
+ }
82
+
83
+ export async function del<Result>(
84
+ url: string,
85
+ args: unknown,
86
+ mutate: ScopedMutator<any>
87
+ ) {
88
+ const reqUrl = makeUrl(url, args);
89
+ const r: Result = await fetcher(reqUrl, {
90
+ method: 'DELETE',
91
+ });
92
+ const path = url.split('/');
93
+ path.pop();
94
+ mutate(path.join('/'));
95
+ return r;
96
+ }
97
+
98
+ export function getMutate() {
99
+ const { mutate } = useSWRConfig();
100
+ return mutate;
101
+ }
102
+ `;
103
+
104
+ const sf = project.createSourceFile(
105
+ path.join(context.outDir, `hooks/request.ts`),
106
+ content,
107
+ { overwrite: true }
108
+ );
109
+ sf.formatText();
110
+ }
111
+
112
+ private generateModelHooks(
113
+ project: Project,
114
+ context: Context,
115
+ model: DataModel
116
+ ) {
117
+ const fileName = paramCase(model.name);
118
+ const sf = project.createSourceFile(
119
+ path.join(context.outDir, `hooks/${fileName}.ts`),
120
+ undefined,
121
+ { overwrite: true }
122
+ );
123
+
124
+ sf.addImportDeclaration({
125
+ namedImports: [{ name: 'Prisma', alias: 'P' }, model.name],
126
+ isTypeOnly: true,
127
+ moduleSpecifier: '../.prisma',
128
+ });
129
+
130
+ sf.addStatements([`import * as request from './request';`]);
131
+
132
+ sf.addStatements(
133
+ `const endpoint = '/api/zen/data/${camelCase(model.name)}';`
134
+ );
135
+
136
+ const useFuncBody = sf
137
+ .addFunction({
138
+ name: `use${model.name}`,
139
+ isExported: true,
140
+ })
141
+ .addBody();
142
+
143
+ useFuncBody.addStatements(['const mutate = request.getMutate();']);
144
+
145
+ // create
146
+ useFuncBody
147
+ .addFunction({
148
+ name: 'create',
149
+ isAsync: true,
150
+ typeParameters: [`T extends P.${model.name}CreateArgs`],
151
+ parameters: [
152
+ { name: 'args', type: `P.${model.name}CreateArgs` },
153
+ ],
154
+ })
155
+ .addBody()
156
+ .addStatements([
157
+ `return request.post<P.${model.name}CreateArgs, P.CheckSelect<T, ${model.name}, P.${model.name}GetPayload<T>>>(endpoint, args, mutate);`,
158
+ ]);
159
+
160
+ // find
161
+ useFuncBody
162
+ .addFunction({
163
+ name: 'find',
164
+ typeParameters: [`T extends P.${model.name}FindManyArgs`],
165
+ parameters: [
166
+ {
167
+ name: 'args?',
168
+ type: `P.SelectSubset<T, P.${model.name}FindManyArgs>`,
169
+ },
170
+ ],
171
+ })
172
+ .addBody()
173
+ .addStatements([
174
+ `return request.get<P.CheckSelect<T, Array<${model.name}>, Array<P.${model.name}GetPayload<T>>>>(endpoint, args);`,
175
+ ]);
176
+
177
+ // get
178
+ useFuncBody
179
+ .addFunction({
180
+ name: 'get',
181
+ isAsync: true,
182
+ typeParameters: [
183
+ `T extends P.Subset<P.${model.name}FindFirstArgs, 'select' | 'include'>`,
184
+ ],
185
+ parameters: [
186
+ {
187
+ name: 'id',
188
+ type: 'String',
189
+ },
190
+ {
191
+ name: 'args?',
192
+ type: `P.SelectSubset<T, P.Subset<P.${model.name}FindFirstArgs, 'select' | 'include'>>`,
193
+ },
194
+ ],
195
+ })
196
+ .addBody()
197
+ .addStatements([
198
+ `return request.get<P.CheckSelect<T, ${model.name}, P.${model.name}GetPayload<T>>>(\`\${endpoint}/\${id}\`, args);`,
199
+ ]);
200
+
201
+ // update
202
+ useFuncBody
203
+ .addFunction({
204
+ name: 'update',
205
+ isAsync: true,
206
+ typeParameters: [
207
+ `T extends Omit<P.${model.name}UpdateArgs, 'where'>`,
208
+ ],
209
+ parameters: [
210
+ { name: 'id', type: 'String' },
211
+ {
212
+ name: 'args',
213
+ type: `Omit<P.${model.name}UpdateArgs, 'where'>`,
214
+ },
215
+ ],
216
+ })
217
+ .addBody()
218
+ .addStatements([
219
+ `return request.put<Omit<P.${model.name}UpdateArgs, 'where'>, P.CheckSelect<T, ${model.name}, P.${model.name}GetPayload<T>>>(\`\${endpoint}/\${id}\`, args, mutate);`,
220
+ ]);
221
+
222
+ // del
223
+ useFuncBody
224
+ .addFunction({
225
+ name: 'del',
226
+ isAsync: true,
227
+ typeParameters: [
228
+ `T extends Omit<P.${model.name}DeleteArgs, 'where'>`,
229
+ ],
230
+ parameters: [
231
+ { name: 'id', type: 'String' },
232
+ {
233
+ name: 'args?',
234
+ type: `Omit<P.${model.name}DeleteArgs, 'where'>`,
235
+ },
236
+ ],
237
+ })
238
+ .addBody()
239
+ .addStatements([
240
+ `return request.del<P.CheckSelect<T, ${model.name}, P.${model.name}GetPayload<T>>>(\`\${endpoint}/\${id}\`, args, mutate);`,
241
+ ]);
242
+
243
+ useFuncBody.addStatements([
244
+ 'return { create, find, get, update, del };',
245
+ ]);
246
+
247
+ sf.formatText();
248
+ }
249
+
250
+ private generateIndex(
251
+ project: Project,
252
+ context: Context,
253
+ models: DataModel[]
254
+ ) {
255
+ const sf = project.createSourceFile(
256
+ path.join(context.outDir, 'hooks/index.ts'),
257
+ undefined,
258
+ { overwrite: true }
259
+ );
260
+
261
+ sf.addStatements(
262
+ models.map((d) => `export * from './${paramCase(d.name)}';`)
263
+ );
264
+
265
+ sf.formatText();
266
+ }
267
+ }