pastoria 1.0.8 → 1.0.9
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 +7 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +44 -17
- package/dist/build.js.map +1 -1
- package/dist/generate.d.ts +1 -0
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +254 -117
- package/dist/generate.js.map +1 -1
- package/package.json +3 -3
- package/src/build.ts +55 -20
- package/src/generate.ts +408 -168
package/CHANGELOG.md
CHANGED
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,YAAY,EACZ,KAAK,uBAAuB,EAE7B,MAAM,MAAM,CAAC;AA8Id,eAAO,MAAM,YAAY,EAAE,uBAK1B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,uBAM1B,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,uBAAuB,GAChC,YAAY,CAyBd;AAED,wBAAsB,WAAW,kBAUhC"}
|
package/dist/build.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import react from '@vitejs/plugin-react';
|
|
2
1
|
import tailwindcss from '@tailwindcss/vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
3
|
import { access } from 'node:fs/promises';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
|
-
import { cjsInterop } from 'vite-plugin-cjs-interop';
|
|
6
4
|
import { build, } from 'vite';
|
|
7
|
-
|
|
5
|
+
import { cjsInterop } from 'vite-plugin-cjs-interop';
|
|
6
|
+
function generateClientEntry({ hasAppRoot }) {
|
|
8
7
|
const appImport = hasAppRoot
|
|
9
8
|
? `import {App} from '#genfiles/router/app_root';`
|
|
10
9
|
: '';
|
|
@@ -22,11 +21,17 @@ async function main() {
|
|
|
22
21
|
main();
|
|
23
22
|
`;
|
|
24
23
|
}
|
|
25
|
-
function generateServerEntry(hasAppRoot) {
|
|
24
|
+
function generateServerEntry({ hasAppRoot, hasServerHandler, }) {
|
|
26
25
|
const appImport = hasAppRoot
|
|
27
26
|
? `import {App} from '#genfiles/router/app_root';`
|
|
28
27
|
: '';
|
|
29
28
|
const appValue = hasAppRoot ? 'App' : 'null';
|
|
29
|
+
const serverHandlerImport = hasServerHandler
|
|
30
|
+
? `import {router as serverHandler} from '#genfiles/router/server_handler';`
|
|
31
|
+
: '';
|
|
32
|
+
const serverHandlerUse = hasServerHandler
|
|
33
|
+
? ' router.use(serverHandler)'
|
|
34
|
+
: '';
|
|
30
35
|
return `// Generated by Pastoria.
|
|
31
36
|
import {JSResource} from '#genfiles/router/js_resource';
|
|
32
37
|
import {
|
|
@@ -37,6 +42,8 @@ import {
|
|
|
37
42
|
import {getSchema} from '#genfiles/schema/schema';
|
|
38
43
|
import {Context} from '#genfiles/router/context';
|
|
39
44
|
${appImport}
|
|
45
|
+
${serverHandlerImport}
|
|
46
|
+
import express from 'express';
|
|
40
47
|
import {GraphQLSchema, specifiedDirectives} from 'graphql';
|
|
41
48
|
import {PastoriaConfig} from 'pastoria-config';
|
|
42
49
|
import {createRouterHandler} from 'pastoria-runtime/server';
|
|
@@ -53,7 +60,7 @@ export function createHandler(
|
|
|
53
60
|
config: Required<PastoriaConfig>,
|
|
54
61
|
manifest?: Manifest,
|
|
55
62
|
) {
|
|
56
|
-
|
|
63
|
+
const routeHandler = createRouterHandler(
|
|
57
64
|
listRoutes(),
|
|
58
65
|
JSResource.srcOfModuleId,
|
|
59
66
|
router__loadEntryPoint,
|
|
@@ -65,9 +72,37 @@ export function createHandler(
|
|
|
65
72
|
config,
|
|
66
73
|
manifest,
|
|
67
74
|
);
|
|
75
|
+
|
|
76
|
+
const router = express.Router();
|
|
77
|
+
router.use(routeHandler);
|
|
78
|
+
${serverHandlerUse}
|
|
79
|
+
|
|
80
|
+
return router;
|
|
68
81
|
}
|
|
69
82
|
`;
|
|
70
83
|
}
|
|
84
|
+
async function determineCapabilities() {
|
|
85
|
+
const capabilities = {
|
|
86
|
+
hasAppRoot: false,
|
|
87
|
+
hasServerHandler: false,
|
|
88
|
+
};
|
|
89
|
+
async function hasAppRoot() {
|
|
90
|
+
try {
|
|
91
|
+
await access('__generated__/router/app_root.ts');
|
|
92
|
+
capabilities.hasAppRoot = true;
|
|
93
|
+
}
|
|
94
|
+
catch { }
|
|
95
|
+
}
|
|
96
|
+
async function hasServerHandler() {
|
|
97
|
+
try {
|
|
98
|
+
await access('__generated__/router/server_handler.ts');
|
|
99
|
+
capabilities.hasServerHandler = true;
|
|
100
|
+
}
|
|
101
|
+
catch { }
|
|
102
|
+
}
|
|
103
|
+
await Promise.all([hasAppRoot(), hasServerHandler()]);
|
|
104
|
+
return capabilities;
|
|
105
|
+
}
|
|
71
106
|
function pastoriaEntryPlugin() {
|
|
72
107
|
const clientEntryModuleId = 'virtual:pastoria-entry-client.tsx';
|
|
73
108
|
const serverEntryModuleId = 'virtual:pastoria-entry-server.tsx';
|
|
@@ -82,20 +117,12 @@ function pastoriaEntryPlugin() {
|
|
|
82
117
|
}
|
|
83
118
|
},
|
|
84
119
|
async load(id) {
|
|
85
|
-
const
|
|
86
|
-
let hasAppRoot = false;
|
|
87
|
-
try {
|
|
88
|
-
await access(appRootPath);
|
|
89
|
-
hasAppRoot = true;
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
hasAppRoot = false;
|
|
93
|
-
}
|
|
120
|
+
const capabilities = await determineCapabilities();
|
|
94
121
|
if (id === clientEntryModuleId) {
|
|
95
|
-
return generateClientEntry(
|
|
122
|
+
return generateClientEntry(capabilities);
|
|
96
123
|
}
|
|
97
124
|
else if (id === serverEntryModuleId) {
|
|
98
|
-
return generateServerEntry(
|
|
125
|
+
return generateServerEntry(capabilities);
|
|
99
126
|
}
|
|
100
127
|
},
|
|
101
128
|
};
|
package/dist/build.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
|
package/dist/generate.d.ts
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* - Add @resource <resource-name> to exports for lazy loading
|
|
21
21
|
* - Add @appRoot to a component to designate it as the application root wrapper
|
|
22
22
|
* - Add @gqlContext to a class extending PastoriaRootContext to provide a custom GraphQL context
|
|
23
|
+
* - Add @serverRoute to functions to add an express handler
|
|
23
24
|
*
|
|
24
25
|
* The generator automatically creates Zod schemas for route parameters based on
|
|
25
26
|
* TypeScript types, enabling runtime validation and type safety.
|
package/dist/generate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAyoBH,wBAAsB,yBAAyB,kBAgB9C"}
|
package/dist/generate.js
CHANGED
|
@@ -20,21 +20,16 @@
|
|
|
20
20
|
* - Add @resource <resource-name> to exports for lazy loading
|
|
21
21
|
* - Add @appRoot to a component to designate it as the application root wrapper
|
|
22
22
|
* - Add @gqlContext to a class extending PastoriaRootContext to provide a custom GraphQL context
|
|
23
|
+
* - Add @serverRoute to functions to add an express handler
|
|
23
24
|
*
|
|
24
25
|
* The generator automatically creates Zod schemas for route parameters based on
|
|
25
26
|
* TypeScript types, enabling runtime validation and type safety.
|
|
26
27
|
*/
|
|
27
28
|
import { readFile } from 'node:fs/promises';
|
|
28
29
|
import * as path from 'node:path';
|
|
29
|
-
import
|
|
30
|
-
import { Project, SyntaxKind, ts, TypeFlags } from 'ts-morph';
|
|
31
|
-
|
|
32
|
-
const JS_RESOURCE_TEMPLATE = path.join(import.meta.dirname, '../templates/js_resource.ts');
|
|
33
|
-
const ROUTER_FILENAME = '__generated__/router/router.tsx';
|
|
34
|
-
const ROUTER_TEMPLATE = path.join(import.meta.dirname, '../templates/router.tsx');
|
|
35
|
-
const APP_ROOT_FILENAME = '__generated__/router/app_root.ts';
|
|
36
|
-
const CONTEXT_FILENAME = '__generated__/router/context.ts';
|
|
37
|
-
async function loadRouterFiles(project) {
|
|
30
|
+
import pc from 'picocolors';
|
|
31
|
+
import { IndentationText, Project, SyntaxKind, ts, TypeFlags, } from 'ts-morph';
|
|
32
|
+
async function loadRouterTemplates(project, filename) {
|
|
38
33
|
async function loadSourceFile(fileName, templateFileName) {
|
|
39
34
|
const template = await readFile(templateFileName, 'utf-8');
|
|
40
35
|
const warningComment = `/*
|
|
@@ -47,17 +42,20 @@ async function loadRouterFiles(project) {
|
|
|
47
42
|
overwrite: true,
|
|
48
43
|
});
|
|
49
44
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
]);
|
|
54
|
-
return { jsResource, router };
|
|
45
|
+
const template = path.join(import.meta.dirname, '../templates', filename);
|
|
46
|
+
const output = path.join('__generated__/router', filename);
|
|
47
|
+
return loadSourceFile(output, template);
|
|
55
48
|
}
|
|
56
49
|
// Regex to quickly check if a file contains any Pastoria JSDoc tags
|
|
57
|
-
const PASTORIA_TAG_REGEX = /@(route|resource|appRoot|param|gqlContext)\b/;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
const PASTORIA_TAG_REGEX = /@(route|resource|appRoot|param|gqlContext|serverRoute)\b/;
|
|
51
|
+
/**
|
|
52
|
+
*
|
|
53
|
+
* @query {abc} 123
|
|
54
|
+
*/
|
|
55
|
+
function collectPastoriaMetadata(project) {
|
|
56
|
+
const resources = new Map();
|
|
57
|
+
const routes = new Map();
|
|
58
|
+
const serverHandlers = new Map();
|
|
61
59
|
let appRoot = null;
|
|
62
60
|
let gqlContext = null;
|
|
63
61
|
function visitRouterNodes(sourceFile) {
|
|
@@ -72,6 +70,8 @@ function collectRouterNodes(project) {
|
|
|
72
70
|
}
|
|
73
71
|
sourceFile.getExportSymbols().forEach((symbol) => {
|
|
74
72
|
let routerResource = null;
|
|
73
|
+
const resourceQueries = new Map();
|
|
74
|
+
const resourceEntryPoints = new Map();
|
|
75
75
|
let routerRoute = null;
|
|
76
76
|
const routeParams = new Map();
|
|
77
77
|
function visitJSDocTags(tag) {
|
|
@@ -89,26 +89,46 @@ function collectRouterNodes(project) {
|
|
|
89
89
|
else if (typeof tag.comment === 'string') {
|
|
90
90
|
switch (tag.tagName.getText()) {
|
|
91
91
|
case 'route': {
|
|
92
|
-
routerRoute =
|
|
93
|
-
|
|
94
|
-
sourceFile,
|
|
95
|
-
|
|
96
|
-
params: routeParams,
|
|
97
|
-
};
|
|
92
|
+
routerRoute = [
|
|
93
|
+
tag.comment,
|
|
94
|
+
{ sourceFile, symbol, params: routeParams },
|
|
95
|
+
];
|
|
98
96
|
break;
|
|
99
97
|
}
|
|
100
98
|
case 'resource': {
|
|
101
|
-
routerResource =
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
routerResource = [
|
|
100
|
+
tag.comment,
|
|
101
|
+
{
|
|
102
|
+
sourceFile,
|
|
103
|
+
symbol,
|
|
104
|
+
queries: resourceQueries,
|
|
105
|
+
entryPoints: resourceEntryPoints,
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'serverRoute': {
|
|
111
|
+
serverHandlers.set(tag.comment, { sourceFile, symbol });
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
case 'query': {
|
|
115
|
+
const match = tag.comment.match(/^\s*\{\s*(?<query>\w+)\s*\}\s+(?<name>\w+)\s*$/)?.groups;
|
|
116
|
+
if (match && match.query && match.name) {
|
|
117
|
+
resourceQueries.set(match.name, match.query);
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case 'entrypoint': {
|
|
122
|
+
const match = tag.comment.match(/^\s*\{\s*(?<resource>[\w#]+)\s*\}\s+(?<name>\w+)\s*$/)?.groups;
|
|
123
|
+
if (match && match.resource && match.name) {
|
|
124
|
+
resourceEntryPoints.set(match.name, match.resource);
|
|
125
|
+
}
|
|
106
126
|
break;
|
|
107
127
|
}
|
|
108
128
|
}
|
|
109
129
|
}
|
|
110
130
|
else {
|
|
111
|
-
// Handle tags without comments (like @
|
|
131
|
+
// Handle tags without comments (like @ExportedSymbol, @gqlContext)
|
|
112
132
|
switch (tag.tagName.getText()) {
|
|
113
133
|
case 'appRoot': {
|
|
114
134
|
if (appRoot != null) {
|
|
@@ -161,14 +181,16 @@ function collectRouterNodes(project) {
|
|
|
161
181
|
.getDeclarations()
|
|
162
182
|
.flatMap((decl) => ts.getJSDocCommentsAndTags(decl.compilerNode))
|
|
163
183
|
.forEach(visitJSDocTags);
|
|
164
|
-
if (routerRoute != null)
|
|
165
|
-
routes.
|
|
166
|
-
|
|
167
|
-
|
|
184
|
+
if (routerRoute != null) {
|
|
185
|
+
routes.set(routerRoute[0], routerRoute[1]);
|
|
186
|
+
}
|
|
187
|
+
if (routerResource != null) {
|
|
188
|
+
resources.set(routerResource[0], routerResource[1]);
|
|
189
|
+
}
|
|
168
190
|
});
|
|
169
191
|
}
|
|
170
192
|
project.getSourceFiles().forEach(visitRouterNodes);
|
|
171
|
-
return { resources, routes, appRoot, gqlContext };
|
|
193
|
+
return { resources, routes, appRoot, gqlContext, serverHandlers };
|
|
172
194
|
}
|
|
173
195
|
function zodSchemaOfType(tc, t) {
|
|
174
196
|
if (t.getFlags() & TypeFlags.String) {
|
|
@@ -201,39 +223,177 @@ function zodSchemaOfType(tc, t) {
|
|
|
201
223
|
return `z.any()`;
|
|
202
224
|
}
|
|
203
225
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
226
|
+
function writeEntryPoint(writer, metadata, consumedQueries, resourceName, resource, parseVars = true) {
|
|
227
|
+
writer.writeLine(`root: JSResource.fromModuleId('${resourceName}'),`);
|
|
228
|
+
writer
|
|
229
|
+
.write(`getPreloadProps(${parseVars ? '{params, schema}' : ''})`)
|
|
230
|
+
.block(() => {
|
|
231
|
+
if (parseVars) {
|
|
232
|
+
writer.writeLine('const variables = schema.parse(params);');
|
|
233
|
+
}
|
|
234
|
+
writer.write('return').block(() => {
|
|
235
|
+
writer
|
|
236
|
+
.write('queries:')
|
|
237
|
+
.block(() => {
|
|
238
|
+
for (const [queryRef, query] of resource.queries.entries()) {
|
|
239
|
+
consumedQueries.add(query);
|
|
240
|
+
writer
|
|
241
|
+
.write(`${queryRef}:`)
|
|
242
|
+
.block(() => {
|
|
243
|
+
writer.writeLine(`parameters: ${query}Parameters,`);
|
|
244
|
+
writer.writeLine(`variables`);
|
|
245
|
+
})
|
|
246
|
+
.write(',');
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
.writeLine(',');
|
|
250
|
+
writer.write('entryPoints:').block(() => {
|
|
251
|
+
for (const [epRef, subresourceName,] of resource.entryPoints.entries()) {
|
|
252
|
+
const subresource = metadata.resources.get(subresourceName);
|
|
253
|
+
if (subresource) {
|
|
254
|
+
writer.write(`${epRef}:`).block(() => {
|
|
255
|
+
writer.writeLine(`entryPointParams: {},`);
|
|
256
|
+
writer.write('entryPoint:').block(() => {
|
|
257
|
+
writeEntryPoint(writer, metadata, consumedQueries, subresourceName, subresource, false);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
});
|
|
208
264
|
});
|
|
265
|
+
}
|
|
266
|
+
async function generateRouter(project, metadata) {
|
|
267
|
+
const routerTemplate = await loadRouterTemplates(project, 'router.tsx');
|
|
209
268
|
const tc = project.getTypeChecker().compilerObject;
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const filePath = path.relative(
|
|
218
|
-
|
|
219
|
-
|
|
269
|
+
const routerConf = routerTemplate
|
|
270
|
+
.getVariableDeclarationOrThrow('ROUTER_CONF')
|
|
271
|
+
.getInitializerIfKindOrThrow(SyntaxKind.AsExpression)
|
|
272
|
+
.getExpressionIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
|
|
273
|
+
routerConf.getPropertyOrThrow('noop').remove();
|
|
274
|
+
let entryPointImportIndex = 0;
|
|
275
|
+
for (const [routeName, { sourceFile, symbol, params },] of metadata.routes.entries()) {
|
|
276
|
+
const filePath = path.relative(process.cwd(), sourceFile.getFilePath());
|
|
277
|
+
let entryPointExpression;
|
|
278
|
+
// Resource-routes are combined declarations of a resource and a route
|
|
279
|
+
// where we should generate the entrypoint for the route.
|
|
280
|
+
const isResourceRoute = Array.from(metadata.resources.entries()).find(([, { symbol: resourceSymbol }]) => symbol === resourceSymbol);
|
|
281
|
+
if (isResourceRoute) {
|
|
282
|
+
const [resourceName, resource] = isResourceRoute;
|
|
283
|
+
const entryPointFunctionName = `entrypoint_${resourceName.replace(/\W/g, '__')}`;
|
|
284
|
+
routerTemplate.addImportDeclaration({
|
|
285
|
+
moduleSpecifier: './js_resource',
|
|
286
|
+
namedImports: ['JSResource', 'ModuleType'],
|
|
287
|
+
});
|
|
288
|
+
const consumedQueries = new Set();
|
|
289
|
+
routerTemplate.addFunction({
|
|
290
|
+
name: entryPointFunctionName,
|
|
291
|
+
returnType: `EntryPoint<ModuleType<'${resourceName}'>, EntryPointParams<'${routeName}'>>`,
|
|
292
|
+
statements: (writer) => {
|
|
293
|
+
writer.write('return ').block(() => {
|
|
294
|
+
writeEntryPoint(writer, metadata, consumedQueries, resourceName, resource);
|
|
295
|
+
});
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
for (const query of consumedQueries) {
|
|
299
|
+
routerTemplate.addImportDeclaration({
|
|
300
|
+
moduleSpecifier: `#genfiles/queries/${query}$parameters`,
|
|
301
|
+
defaultImport: `${query}Parameters`,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
entryPointExpression = entryPointFunctionName + '()';
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
const importAlias = `e${entryPointImportIndex++}`;
|
|
308
|
+
const moduleSpecifier = routerTemplate.getRelativePathAsModuleSpecifierTo(sourceFile.getFilePath());
|
|
309
|
+
routerTemplate.addImportDeclaration({
|
|
310
|
+
moduleSpecifier,
|
|
311
|
+
namedImports: [
|
|
312
|
+
{
|
|
313
|
+
name: symbol.getName(),
|
|
314
|
+
alias: importAlias,
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
});
|
|
318
|
+
entryPointExpression = importAlias;
|
|
319
|
+
}
|
|
320
|
+
routerConf.addPropertyAssignment({
|
|
321
|
+
name: `"${routeName}"`,
|
|
322
|
+
initializer: (writer) => {
|
|
323
|
+
writer
|
|
324
|
+
.write('{')
|
|
325
|
+
.indent(() => {
|
|
326
|
+
writer.writeLine(`entrypoint: ${entryPointExpression},`);
|
|
327
|
+
if (params.size === 0) {
|
|
328
|
+
writer.writeLine(`schema: z.object({})`);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
writer.writeLine(`schema: z.object({`);
|
|
332
|
+
for (const [paramName, paramType] of Array.from(params)) {
|
|
333
|
+
writer.writeLine(` ${paramName}: ${zodSchemaOfType(tc, paramType)},`);
|
|
334
|
+
}
|
|
335
|
+
writer.writeLine('})');
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
.write('} as const');
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
console.log('Created route', pc.cyan(routeName), 'for', pc.green(symbol.getName()), 'exported from', pc.yellow(filePath));
|
|
342
|
+
}
|
|
343
|
+
await routerTemplate.save();
|
|
344
|
+
}
|
|
345
|
+
async function generateJsResource(project, metadata) {
|
|
346
|
+
const jsResourceTemplate = await loadRouterTemplates(project, 'js_resource.ts');
|
|
347
|
+
const resourceConf = jsResourceTemplate
|
|
348
|
+
.getVariableDeclarationOrThrow('RESOURCE_CONF')
|
|
349
|
+
.getInitializerIfKindOrThrow(SyntaxKind.AsExpression)
|
|
350
|
+
.getExpressionIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
|
|
351
|
+
resourceConf.getPropertyOrThrow('noop').remove();
|
|
352
|
+
for (const [resourceName, { sourceFile, symbol },] of metadata.resources.entries()) {
|
|
353
|
+
const filePath = path.relative(process.cwd(), sourceFile.getFilePath());
|
|
354
|
+
const moduleSpecifier = jsResourceTemplate.getRelativePathAsModuleSpecifierTo(sourceFile.getFilePath());
|
|
355
|
+
resourceConf.addPropertyAssignment({
|
|
356
|
+
name: `"${resourceName}"`,
|
|
357
|
+
initializer: (writer) => {
|
|
358
|
+
writer.block(() => {
|
|
359
|
+
writer
|
|
360
|
+
.writeLine(`src: "${filePath}",`)
|
|
361
|
+
.writeLine(`loader: () => import("${moduleSpecifier}").then(m => m.${symbol.getName()})`);
|
|
362
|
+
});
|
|
363
|
+
},
|
|
220
364
|
});
|
|
221
|
-
|
|
222
|
-
|
|
365
|
+
console.log('Created resource', pc.cyan(resourceName), 'for', pc.green(symbol.getName()), 'exported from', pc.yellow(filePath));
|
|
366
|
+
}
|
|
367
|
+
await jsResourceTemplate.save();
|
|
368
|
+
}
|
|
369
|
+
async function generateAppRoot(project, metadata) {
|
|
370
|
+
const targetDir = process.cwd();
|
|
371
|
+
const appRoot = metadata.appRoot;
|
|
372
|
+
if (appRoot == null) {
|
|
373
|
+
await project
|
|
374
|
+
.getSourceFile('__generated__/router/app_root.ts')
|
|
375
|
+
?.deleteImmediately();
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const appRootSourceFile = appRoot.sourceFile;
|
|
379
|
+
const appRootSymbol = appRoot.symbol;
|
|
380
|
+
const filePath = path.relative(targetDir, appRootSourceFile.getFilePath());
|
|
381
|
+
const appRootFile = project.createSourceFile('__generated__/router/app_root.ts', '', { overwrite: true });
|
|
382
|
+
const moduleSpecifier = appRootFile.getRelativePathAsModuleSpecifierTo(appRootSourceFile.getFilePath());
|
|
383
|
+
appRootFile.addStatements(`/*
|
|
223
384
|
* This file was generated by \`pastoria\`.
|
|
224
385
|
* Do not modify this file directly.
|
|
225
386
|
*/
|
|
226
387
|
|
|
227
388
|
export {${appRootSymbol.getName()} as App} from '${moduleSpecifier}';
|
|
228
389
|
`);
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
});
|
|
390
|
+
await appRootFile.save();
|
|
391
|
+
console.log('Created app root for', pc.green(appRootSymbol.getName()), 'exported from', pc.yellow(filePath));
|
|
392
|
+
}
|
|
393
|
+
async function generateGraphqlContext(project, metadata) {
|
|
394
|
+
const targetDir = process.cwd();
|
|
395
|
+
const gqlContext = metadata.gqlContext;
|
|
396
|
+
const contextFile = project.createSourceFile('__generated__/router/context.ts', '', { overwrite: true });
|
|
237
397
|
if (gqlContext != null) {
|
|
238
398
|
const contextSourceFile = gqlContext.sourceFile;
|
|
239
399
|
const contextSymbol = gqlContext.symbol;
|
|
@@ -264,68 +424,45 @@ export class Context extends PastoriaRootContext {}
|
|
|
264
424
|
console.log('No @gqlContext found, generating default', pc.green('Context'));
|
|
265
425
|
}
|
|
266
426
|
await contextFile.save();
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const moduleSpecifier = routerFiles.jsResource.getRelativePathAsModuleSpecifierTo(sourceFile.getFilePath());
|
|
275
|
-
resourceConf.addPropertyAssignment({
|
|
276
|
-
name: `"${resourceName}"`,
|
|
277
|
-
initializer: (writer) => {
|
|
278
|
-
writer.block(() => {
|
|
279
|
-
writer
|
|
280
|
-
.writeLine(`src: "${filePath}",`)
|
|
281
|
-
.writeLine(`loader: () => import("${moduleSpecifier}").then(m => m.${symbol.getName()})`);
|
|
282
|
-
});
|
|
283
|
-
},
|
|
284
|
-
});
|
|
285
|
-
console.log('Created resource', pc.cyan(resourceName), 'for', pc.green(symbol.getName()), 'exported from', pc.yellow(filePath));
|
|
427
|
+
}
|
|
428
|
+
async function generateServerHandler(project, metadata) {
|
|
429
|
+
if (metadata.serverHandlers.size === 0) {
|
|
430
|
+
await project
|
|
431
|
+
.getSourceFile('__generated__/router/server_handler.ts')
|
|
432
|
+
?.deleteImmediately();
|
|
433
|
+
return;
|
|
286
434
|
}
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const importAlias = `e${entryPointImportIndex++}`;
|
|
435
|
+
const sourceText = `import express from 'express';
|
|
436
|
+
export const router = express.Router();
|
|
437
|
+
`;
|
|
438
|
+
const serverHandlerTemplate = project.createSourceFile('__generated__/router/server_handler.ts', sourceText, { overwrite: true });
|
|
439
|
+
let serverHandlerImportIndex = 0;
|
|
440
|
+
for (const [routeName, { symbol, sourceFile },] of metadata.serverHandlers.entries()) {
|
|
441
|
+
const importAlias = `e${serverHandlerImportIndex++}`;
|
|
295
442
|
const filePath = path.relative(process.cwd(), sourceFile.getFilePath());
|
|
296
|
-
const moduleSpecifier =
|
|
297
|
-
|
|
443
|
+
const moduleSpecifier = serverHandlerTemplate.getRelativePathAsModuleSpecifierTo(sourceFile.getFilePath());
|
|
444
|
+
serverHandlerTemplate.addImportDeclaration({
|
|
298
445
|
moduleSpecifier,
|
|
299
|
-
namedImports: [
|
|
300
|
-
{
|
|
301
|
-
name: symbol.getName(),
|
|
302
|
-
alias: importAlias,
|
|
303
|
-
},
|
|
304
|
-
],
|
|
305
|
-
});
|
|
306
|
-
routerConf.addPropertyAssignment({
|
|
307
|
-
name: `"${routeName}"`,
|
|
308
|
-
initializer: (writer) => {
|
|
309
|
-
writer
|
|
310
|
-
.write('{')
|
|
311
|
-
.indent(() => {
|
|
312
|
-
writer.writeLine(`entrypoint: ${importAlias},`);
|
|
313
|
-
if (params.size === 0) {
|
|
314
|
-
writer.writeLine(`schema: z.object({})`);
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
writer.writeLine(`schema: z.object({`);
|
|
318
|
-
for (const [paramName, paramType] of Array.from(params)) {
|
|
319
|
-
writer.writeLine(` ${paramName}: ${zodSchemaOfType(tc, paramType)},`);
|
|
320
|
-
}
|
|
321
|
-
writer.writeLine('})');
|
|
322
|
-
}
|
|
323
|
-
})
|
|
324
|
-
.write('} as const');
|
|
325
|
-
},
|
|
446
|
+
namedImports: [{ name: symbol.getName(), alias: importAlias }],
|
|
326
447
|
});
|
|
327
|
-
|
|
448
|
+
serverHandlerTemplate.addStatements(`router.use('${routeName}', ${importAlias})`);
|
|
449
|
+
console.log('Created server handler', pc.cyan(routeName), 'for', pc.green(symbol.getName()), 'exported from', pc.yellow(filePath));
|
|
328
450
|
}
|
|
329
|
-
await
|
|
451
|
+
await serverHandlerTemplate.save();
|
|
452
|
+
}
|
|
453
|
+
export async function generatePastoriaArtifacts() {
|
|
454
|
+
const targetDir = process.cwd();
|
|
455
|
+
const project = new Project({
|
|
456
|
+
tsConfigFilePath: path.join(targetDir, 'tsconfig.json'),
|
|
457
|
+
manipulationSettings: {
|
|
458
|
+
indentationText: IndentationText.TwoSpaces,
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
const metadata = collectPastoriaMetadata(project);
|
|
462
|
+
await generateAppRoot(project, metadata);
|
|
463
|
+
await generateGraphqlContext(project, metadata);
|
|
464
|
+
await generateRouter(project, metadata);
|
|
465
|
+
await generateJsResource(project, metadata);
|
|
466
|
+
await generateServerHandler(project, metadata);
|
|
330
467
|
}
|
|
331
468
|
//# sourceMappingURL=generate.js.map
|