pastoria 1.0.12 → 1.0.14
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 +13 -0
- package/dist/build.d.ts +0 -4
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +80 -272
- package/dist/build.js.map +1 -1
- package/dist/devserver.d.ts.map +1 -1
- package/dist/devserver.js +4 -3
- package/dist/devserver.js.map +1 -1
- package/dist/generate.d.ts +6 -0
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +187 -39
- package/dist/generate.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +10 -0
- package/dist/logger.js.map +1 -0
- package/dist/vite_plugin.d.ts +5 -0
- package/dist/vite_plugin.d.ts.map +1 -0
- package/dist/vite_plugin.js +93 -0
- package/dist/vite_plugin.js.map +1 -0
- package/package.json +2 -2
- package/src/build.ts +109 -336
- package/src/devserver.ts +4 -3
- package/src/generate.ts +233 -61
- package/src/index.ts +1 -1
- package/src/logger.ts +12 -0
- package/src/vite_plugin.ts +109 -0
- package/templates/router.tsx +1 -14
- package/tsconfig.json +1 -1
package/src/generate.ts
CHANGED
|
@@ -31,15 +31,14 @@ import * as path from 'node:path';
|
|
|
31
31
|
import pc from 'picocolors';
|
|
32
32
|
import {
|
|
33
33
|
CodeBlockWriter,
|
|
34
|
-
IndentationText,
|
|
35
34
|
Project,
|
|
36
35
|
SourceFile,
|
|
37
36
|
Symbol,
|
|
38
37
|
SyntaxKind,
|
|
39
38
|
ts,
|
|
40
39
|
TypeFlags,
|
|
41
|
-
WriterFunction,
|
|
42
40
|
} from 'ts-morph';
|
|
41
|
+
import {logInfo, logWarn} from './logger.js';
|
|
43
42
|
|
|
44
43
|
async function loadRouterTemplates(project: Project, filename: string) {
|
|
45
44
|
async function loadSourceFile(fileName: string, templateFileName: string) {
|
|
@@ -90,6 +89,36 @@ export interface PastoriaMetadata {
|
|
|
90
89
|
export const PASTORIA_TAG_REGEX =
|
|
91
90
|
/@(route|resource|appRoot|param|gqlContext|serverRoute)\b/;
|
|
92
91
|
|
|
92
|
+
function collectQueryParameters(
|
|
93
|
+
project: Project,
|
|
94
|
+
queries: string[],
|
|
95
|
+
): Map<string, ts.Type> {
|
|
96
|
+
const vars = new Map<string, ts.Type>();
|
|
97
|
+
|
|
98
|
+
for (const query of queries) {
|
|
99
|
+
const variablesType = project
|
|
100
|
+
.getSourceFile(`__generated__/queries/${query}.graphql.ts`)
|
|
101
|
+
?.getExportedDeclarations()
|
|
102
|
+
.get(`${query}$variables`)
|
|
103
|
+
?.at(0)
|
|
104
|
+
?.getType();
|
|
105
|
+
|
|
106
|
+
if (variablesType == null) continue;
|
|
107
|
+
|
|
108
|
+
for (const property of variablesType.getProperties()) {
|
|
109
|
+
// TODO: Detect conflicting types among properties declared.
|
|
110
|
+
const propertyName = property.getName();
|
|
111
|
+
const propertyType = property.getValueDeclaration()?.getType();
|
|
112
|
+
|
|
113
|
+
if (propertyType) {
|
|
114
|
+
vars.set(propertyName, propertyType.compilerType);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return vars;
|
|
120
|
+
}
|
|
121
|
+
|
|
93
122
|
function collectPastoriaMetadata(project: Project): PastoriaMetadata {
|
|
94
123
|
const resources = new Map<string, RouterResource>();
|
|
95
124
|
const routes = new Map<string, RouterRoute>();
|
|
@@ -158,10 +187,7 @@ function collectPastoriaMetadata(project: Project): PastoriaMetadata {
|
|
|
158
187
|
switch (tag.tagName.getText()) {
|
|
159
188
|
case 'appRoot': {
|
|
160
189
|
if (appRoot != null) {
|
|
161
|
-
|
|
162
|
-
pc.yellow('Warning:'),
|
|
163
|
-
'Multiple @appRoot tags found. Using the first one.',
|
|
164
|
-
);
|
|
190
|
+
logWarn('Multiple @appRoot tags found. Using the first one.');
|
|
165
191
|
} else {
|
|
166
192
|
appRoot = {
|
|
167
193
|
sourceFile,
|
|
@@ -195,8 +221,7 @@ function collectPastoriaMetadata(project: Project): PastoriaMetadata {
|
|
|
195
221
|
|
|
196
222
|
if (extendsPastoriaRootContext) {
|
|
197
223
|
if (gqlContext != null) {
|
|
198
|
-
|
|
199
|
-
pc.yellow('Warning:'),
|
|
224
|
+
logWarn(
|
|
200
225
|
'Multiple classes with @gqlContext extending PastoriaRootContext found. Using the first one.',
|
|
201
226
|
);
|
|
202
227
|
} else {
|
|
@@ -284,23 +309,32 @@ function getResourceQueriesAndEntryPoints(symbol: Symbol): {
|
|
|
284
309
|
|
|
285
310
|
entryPoints?.getProperties().forEach((prop) => {
|
|
286
311
|
const epRef = prop.getName();
|
|
287
|
-
const
|
|
312
|
+
const entryPointTypeRef = prop
|
|
288
313
|
.getValueDeclaration()
|
|
289
314
|
?.asKind(SyntaxKind.PropertySignature)
|
|
290
315
|
?.getTypeNode()
|
|
291
|
-
?.asKind(SyntaxKind.TypeReference)
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
316
|
+
?.asKind(SyntaxKind.TypeReference);
|
|
317
|
+
|
|
318
|
+
const entryPointTypeName = entryPointTypeRef?.getTypeName().getText();
|
|
319
|
+
if (entryPointTypeName !== 'EntryPoint') {
|
|
320
|
+
// TODO: Warn about found types not named EntryPoint
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const entryPointInner = entryPointTypeRef?.getTypeArguments().at(0);
|
|
325
|
+
const moduleTypeRef = entryPointInner?.asKind(SyntaxKind.TypeReference);
|
|
326
|
+
if (moduleTypeRef != null) {
|
|
327
|
+
const resourceName = moduleTypeRef
|
|
328
|
+
?.getTypeArguments()
|
|
329
|
+
.at(0)
|
|
330
|
+
?.asKind(SyntaxKind.LiteralType)
|
|
331
|
+
?.getLiteral()
|
|
332
|
+
.asKind(SyntaxKind.StringLiteral)
|
|
333
|
+
?.getLiteralText();
|
|
334
|
+
|
|
335
|
+
if (resourceName) {
|
|
336
|
+
resource.entryPoints.set(epRef, resourceName);
|
|
337
|
+
}
|
|
304
338
|
}
|
|
305
339
|
});
|
|
306
340
|
}
|
|
@@ -308,41 +342,62 @@ function getResourceQueriesAndEntryPoints(symbol: Symbol): {
|
|
|
308
342
|
return resource;
|
|
309
343
|
}
|
|
310
344
|
|
|
311
|
-
function zodSchemaOfType(
|
|
312
|
-
|
|
345
|
+
function zodSchemaOfType(
|
|
346
|
+
sf: SourceFile,
|
|
347
|
+
tc: ts.TypeChecker,
|
|
348
|
+
t: ts.Type,
|
|
349
|
+
): string {
|
|
350
|
+
if (t.aliasSymbol) {
|
|
351
|
+
const decl = t.aliasSymbol.declarations?.at(0);
|
|
352
|
+
if (decl == null) {
|
|
353
|
+
logWarn('Could not handle type:', tc.typeToString(t));
|
|
354
|
+
return `z.any()`;
|
|
355
|
+
} else {
|
|
356
|
+
const importPath = sf.getRelativePathAsModuleSpecifierTo(
|
|
357
|
+
decl.getSourceFile().fileName,
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
return `z.transform((s: string) => s as import('${importPath}').${t.aliasSymbol.getName()})`;
|
|
361
|
+
}
|
|
362
|
+
} else if (t.getFlags() & TypeFlags.String) {
|
|
313
363
|
return `z.pipe(z.string(), z.transform(decodeURIComponent))`;
|
|
314
364
|
} else if (t.getFlags() & TypeFlags.Number) {
|
|
315
365
|
return `z.coerce.number<number>()`;
|
|
316
366
|
} else if (t.getFlags() & TypeFlags.Null) {
|
|
317
367
|
return `z.preprocess(s => s == null ? undefined : s, z.undefined())`;
|
|
318
368
|
} else if (t.isUnion()) {
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
369
|
+
const nullishTypes: ts.Type[] = [];
|
|
370
|
+
const nonNullishTypes: ts.Type[] = [];
|
|
371
|
+
for (const s of t.types) {
|
|
372
|
+
const flags = s.getFlags();
|
|
373
|
+
if (flags & TypeFlags.Null || flags & TypeFlags.Undefined) {
|
|
374
|
+
nullishTypes.push(s);
|
|
375
|
+
} else {
|
|
376
|
+
nonNullishTypes.push(s);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
327
379
|
|
|
328
|
-
|
|
380
|
+
if (nullishTypes.length > 0 && nonNullishTypes.length > 0) {
|
|
381
|
+
const nonOptionalType = t.getNonNullableType();
|
|
382
|
+
return `z.pipe(z.nullish(${zodSchemaOfType(sf, tc, nonOptionalType)}), z.transform(s => s == null ? undefined : s))`;
|
|
329
383
|
} else {
|
|
330
|
-
return `z.union([${t.types.map((it) => zodSchemaOfType(tc, it)).join(', ')}])`;
|
|
384
|
+
return `z.union([${t.types.map((it) => zodSchemaOfType(sf, tc, it)).join(', ')}])`;
|
|
331
385
|
}
|
|
332
386
|
} else if (tc.isArrayLikeType(t)) {
|
|
333
387
|
const typeArg = tc.getTypeArguments(t as ts.TypeReference)[0];
|
|
334
388
|
const argZodSchema =
|
|
335
|
-
typeArg == null ? `z.any()` : zodSchemaOfType(tc, typeArg);
|
|
389
|
+
typeArg == null ? `z.any()` : zodSchemaOfType(sf, tc, typeArg);
|
|
336
390
|
|
|
337
391
|
return `z.array(${argZodSchema})`;
|
|
338
392
|
} else {
|
|
339
|
-
|
|
393
|
+
logWarn('Could not handle type:', tc.typeToString(t));
|
|
340
394
|
return `z.any()`;
|
|
341
395
|
}
|
|
342
396
|
}
|
|
343
397
|
|
|
344
398
|
function writeEntryPoint(
|
|
345
399
|
writer: CodeBlockWriter,
|
|
400
|
+
project: Project,
|
|
346
401
|
metadata: PastoriaMetadata,
|
|
347
402
|
consumedQueries: Set<string>,
|
|
348
403
|
resourceName: string,
|
|
@@ -350,6 +405,7 @@ function writeEntryPoint(
|
|
|
350
405
|
parseVars = true,
|
|
351
406
|
) {
|
|
352
407
|
writer.writeLine(`root: JSResource.fromModuleId('${resourceName}'),`);
|
|
408
|
+
|
|
353
409
|
writer
|
|
354
410
|
.write(`getPreloadProps(${parseVars ? '{params, schema}' : ''})`)
|
|
355
411
|
.block(() => {
|
|
@@ -364,11 +420,28 @@ function writeEntryPoint(
|
|
|
364
420
|
for (const [queryRef, query] of resource.queries.entries()) {
|
|
365
421
|
consumedQueries.add(query);
|
|
366
422
|
|
|
423
|
+
// Determine which variables this specific query needs
|
|
424
|
+
const queryVars = collectQueryParameters(project, [query]);
|
|
425
|
+
const hasVariables = queryVars.size > 0;
|
|
426
|
+
|
|
367
427
|
writer
|
|
368
428
|
.write(`${queryRef}:`)
|
|
369
429
|
.block(() => {
|
|
370
430
|
writer.writeLine(`parameters: ${query}Parameters,`);
|
|
371
|
-
|
|
431
|
+
|
|
432
|
+
if (hasVariables) {
|
|
433
|
+
const varNames = Array.from(queryVars.keys());
|
|
434
|
+
// Always pick from the variables object
|
|
435
|
+
writer.write(`variables: {`);
|
|
436
|
+
writer.write(
|
|
437
|
+
varNames.map((v) => `${v}: variables.${v}`).join(', '),
|
|
438
|
+
);
|
|
439
|
+
writer.write(`}`);
|
|
440
|
+
} else {
|
|
441
|
+
// Query has no variables, pass empty object
|
|
442
|
+
writer.write(`variables: {}`);
|
|
443
|
+
}
|
|
444
|
+
writer.newLine();
|
|
372
445
|
})
|
|
373
446
|
.write(',');
|
|
374
447
|
}
|
|
@@ -382,19 +455,23 @@ function writeEntryPoint(
|
|
|
382
455
|
] of resource.entryPoints.entries()) {
|
|
383
456
|
const subresource = metadata.resources.get(subresourceName);
|
|
384
457
|
if (subresource) {
|
|
385
|
-
writer
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
458
|
+
writer
|
|
459
|
+
.write(`${epRef}:`)
|
|
460
|
+
.block(() => {
|
|
461
|
+
writer.writeLine(`entryPointParams: {},`);
|
|
462
|
+
writer.write('entryPoint:').block(() => {
|
|
463
|
+
writeEntryPoint(
|
|
464
|
+
writer,
|
|
465
|
+
project,
|
|
466
|
+
metadata,
|
|
467
|
+
consumedQueries,
|
|
468
|
+
subresourceName,
|
|
469
|
+
subresource,
|
|
470
|
+
false,
|
|
471
|
+
);
|
|
472
|
+
});
|
|
473
|
+
})
|
|
474
|
+
.writeLine(',');
|
|
398
475
|
}
|
|
399
476
|
}
|
|
400
477
|
});
|
|
@@ -415,7 +492,7 @@ async function generateRouter(project: Project, metadata: PastoriaMetadata) {
|
|
|
415
492
|
routerConf.getPropertyOrThrow('noop').remove();
|
|
416
493
|
|
|
417
494
|
let entryPointImportIndex = 0;
|
|
418
|
-
for (
|
|
495
|
+
for (let [
|
|
419
496
|
routeName,
|
|
420
497
|
{sourceFile, symbol, params},
|
|
421
498
|
] of metadata.routes.entries()) {
|
|
@@ -448,6 +525,7 @@ async function generateRouter(project: Project, metadata: PastoriaMetadata) {
|
|
|
448
525
|
writer.write('return ').block(() => {
|
|
449
526
|
writeEntryPoint(
|
|
450
527
|
writer,
|
|
528
|
+
project,
|
|
451
529
|
metadata,
|
|
452
530
|
consumedQueries,
|
|
453
531
|
resourceName,
|
|
@@ -457,6 +535,10 @@ async function generateRouter(project: Project, metadata: PastoriaMetadata) {
|
|
|
457
535
|
},
|
|
458
536
|
});
|
|
459
537
|
|
|
538
|
+
if (params.size === 0 && consumedQueries.size > 0) {
|
|
539
|
+
params = collectQueryParameters(project, Array.from(consumedQueries));
|
|
540
|
+
}
|
|
541
|
+
|
|
460
542
|
for (const query of consumedQueries) {
|
|
461
543
|
routerTemplate.addImportDeclaration({
|
|
462
544
|
moduleSpecifier: `#genfiles/queries/${query}$parameters`,
|
|
@@ -497,7 +579,7 @@ async function generateRouter(project: Project, metadata: PastoriaMetadata) {
|
|
|
497
579
|
writer.writeLine(`schema: z.object({`);
|
|
498
580
|
for (const [paramName, paramType] of Array.from(params)) {
|
|
499
581
|
writer.writeLine(
|
|
500
|
-
` ${paramName}: ${zodSchemaOfType(tc, paramType)},`,
|
|
582
|
+
` ${paramName}: ${zodSchemaOfType(routerTemplate, tc, paramType)},`,
|
|
501
583
|
);
|
|
502
584
|
}
|
|
503
585
|
|
|
@@ -508,7 +590,7 @@ async function generateRouter(project: Project, metadata: PastoriaMetadata) {
|
|
|
508
590
|
},
|
|
509
591
|
});
|
|
510
592
|
|
|
511
|
-
|
|
593
|
+
logInfo(
|
|
512
594
|
'Created route',
|
|
513
595
|
pc.cyan(routeName),
|
|
514
596
|
'for',
|
|
@@ -559,7 +641,7 @@ async function generateJsResource(
|
|
|
559
641
|
},
|
|
560
642
|
});
|
|
561
643
|
|
|
562
|
-
|
|
644
|
+
logInfo(
|
|
563
645
|
'Created resource',
|
|
564
646
|
pc.cyan(resourceName),
|
|
565
647
|
'for',
|
|
@@ -607,7 +689,7 @@ export {${appRootSymbol.getName()} as App} from '${moduleSpecifier}';
|
|
|
607
689
|
|
|
608
690
|
await appRootFile.save();
|
|
609
691
|
|
|
610
|
-
|
|
692
|
+
logInfo(
|
|
611
693
|
'Created app root for',
|
|
612
694
|
pc.green(appRootSymbol.getName()),
|
|
613
695
|
'exported from',
|
|
@@ -643,7 +725,7 @@ async function generateGraphqlContext(
|
|
|
643
725
|
export {${contextSymbol.getName()} as Context} from '${moduleSpecifier}';
|
|
644
726
|
`);
|
|
645
727
|
|
|
646
|
-
|
|
728
|
+
logInfo(
|
|
647
729
|
'Created GraphQL context for',
|
|
648
730
|
pc.green(contextSymbol.getName()),
|
|
649
731
|
'exported from',
|
|
@@ -663,10 +745,7 @@ import {PastoriaRootContext} from 'pastoria-runtime/server';
|
|
|
663
745
|
export class Context extends PastoriaRootContext {}
|
|
664
746
|
`);
|
|
665
747
|
|
|
666
|
-
|
|
667
|
-
'No @gqlContext found, generating default',
|
|
668
|
-
pc.green('Context'),
|
|
669
|
-
);
|
|
748
|
+
logInfo('No @gqlContext found, generating default', pc.green('Context'));
|
|
670
749
|
}
|
|
671
750
|
|
|
672
751
|
await contextFile.save();
|
|
@@ -715,7 +794,7 @@ export const router = express.Router();
|
|
|
715
794
|
`router.use('${routeName}', ${importAlias})`,
|
|
716
795
|
);
|
|
717
796
|
|
|
718
|
-
|
|
797
|
+
logInfo(
|
|
719
798
|
'Created server handler',
|
|
720
799
|
pc.cyan(routeName),
|
|
721
800
|
'for',
|
|
@@ -744,3 +823,96 @@ export async function generatePastoriaArtifacts(
|
|
|
744
823
|
await generateJsResource(project, metadata);
|
|
745
824
|
await generateServerHandler(project, metadata);
|
|
746
825
|
}
|
|
826
|
+
|
|
827
|
+
export interface PastoriaCapabilities {
|
|
828
|
+
hasAppRoot: boolean;
|
|
829
|
+
hasServerHandler: boolean;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
export function generateClientEntry({
|
|
833
|
+
hasAppRoot,
|
|
834
|
+
}: PastoriaCapabilities): string {
|
|
835
|
+
const appImport = hasAppRoot
|
|
836
|
+
? `import {App} from '#genfiles/router/app_root';`
|
|
837
|
+
: '';
|
|
838
|
+
const appValue = hasAppRoot ? 'App' : 'null';
|
|
839
|
+
|
|
840
|
+
return `// Generated by Pastoria.
|
|
841
|
+
import {createRouterApp} from '#genfiles/router/router';
|
|
842
|
+
${appImport}
|
|
843
|
+
import {hydrateRoot} from 'react-dom/client';
|
|
844
|
+
|
|
845
|
+
async function main() {
|
|
846
|
+
const RouterApp = await createRouterApp();
|
|
847
|
+
hydrateRoot(document, <RouterApp App={${appValue}} />);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
main();
|
|
851
|
+
`;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
export function generateServerEntry({
|
|
855
|
+
hasAppRoot,
|
|
856
|
+
hasServerHandler,
|
|
857
|
+
}: PastoriaCapabilities): string {
|
|
858
|
+
const appImport = hasAppRoot
|
|
859
|
+
? `import {App} from '#genfiles/router/app_root';`
|
|
860
|
+
: '';
|
|
861
|
+
const appValue = hasAppRoot ? 'App' : 'null';
|
|
862
|
+
|
|
863
|
+
const serverHandlerImport = hasServerHandler
|
|
864
|
+
? `import {router as serverHandler} from '#genfiles/router/server_handler';`
|
|
865
|
+
: '';
|
|
866
|
+
const serverHandlerUse = hasServerHandler
|
|
867
|
+
? ' router.use(serverHandler)'
|
|
868
|
+
: '';
|
|
869
|
+
|
|
870
|
+
return `// Generated by Pastoria.
|
|
871
|
+
import {JSResource} from '#genfiles/router/js_resource';
|
|
872
|
+
import {
|
|
873
|
+
listRoutes,
|
|
874
|
+
router__createAppFromEntryPoint,
|
|
875
|
+
router__loadEntryPoint,
|
|
876
|
+
} from '#genfiles/router/router';
|
|
877
|
+
import {getSchema} from '#genfiles/schema/schema';
|
|
878
|
+
import {Context} from '#genfiles/router/context';
|
|
879
|
+
${appImport}
|
|
880
|
+
${serverHandlerImport}
|
|
881
|
+
import express from 'express';
|
|
882
|
+
import {GraphQLSchema, specifiedDirectives} from 'graphql';
|
|
883
|
+
import {PastoriaConfig} from 'pastoria-config';
|
|
884
|
+
import {createRouterHandler} from 'pastoria-runtime/server';
|
|
885
|
+
import type {Manifest} from 'vite';
|
|
886
|
+
|
|
887
|
+
const schemaConfig = getSchema().toConfig();
|
|
888
|
+
const schema = new GraphQLSchema({
|
|
889
|
+
...schemaConfig,
|
|
890
|
+
directives: [...specifiedDirectives, ...schemaConfig.directives],
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
export function createHandler(
|
|
894
|
+
persistedQueries: Record<string, string>,
|
|
895
|
+
config: Required<PastoriaConfig>,
|
|
896
|
+
manifest?: Manifest,
|
|
897
|
+
) {
|
|
898
|
+
const routeHandler = createRouterHandler(
|
|
899
|
+
listRoutes(),
|
|
900
|
+
JSResource.srcOfModuleId,
|
|
901
|
+
router__loadEntryPoint,
|
|
902
|
+
router__createAppFromEntryPoint,
|
|
903
|
+
${appValue},
|
|
904
|
+
schema,
|
|
905
|
+
(req) => Context.createFromRequest(req),
|
|
906
|
+
persistedQueries,
|
|
907
|
+
config,
|
|
908
|
+
manifest,
|
|
909
|
+
);
|
|
910
|
+
|
|
911
|
+
const router = express.Router();
|
|
912
|
+
router.use(routeHandler);
|
|
913
|
+
${serverHandlerUse}
|
|
914
|
+
|
|
915
|
+
return router;
|
|
916
|
+
}
|
|
917
|
+
`;
|
|
918
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -30,7 +30,7 @@ async function main() {
|
|
|
30
30
|
'Specific build steps to run (schema, relay, router). If not provided, will infer from changed files.',
|
|
31
31
|
)
|
|
32
32
|
.option('-B, --always-make', 'Always make, never cache')
|
|
33
|
-
.option('--release', 'Build for production')
|
|
33
|
+
.option('-R, --release', 'Build for production')
|
|
34
34
|
.option('-w, --watch', 'Watch for changes and rebuild')
|
|
35
35
|
.action(createBuild);
|
|
36
36
|
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {createLogger} from 'vite';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
|
|
4
|
+
export const logger = createLogger('info', {prefix: pc.greenBright('[MAKE]')});
|
|
5
|
+
|
|
6
|
+
export function logInfo(...messages: string[]) {
|
|
7
|
+
logger.info(messages.join(' '), {timestamp: true});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function logWarn(...messages: string[]) {
|
|
11
|
+
logger.warn(messages.join(' '), {timestamp: true});
|
|
12
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import {access} from 'node:fs/promises';
|
|
4
|
+
import {
|
|
5
|
+
InlineConfig,
|
|
6
|
+
PluginOption,
|
|
7
|
+
type BuildEnvironmentOptions,
|
|
8
|
+
type Plugin,
|
|
9
|
+
} from 'vite';
|
|
10
|
+
import {cjsInterop} from 'vite-plugin-cjs-interop';
|
|
11
|
+
import {
|
|
12
|
+
generateClientEntry,
|
|
13
|
+
generateServerEntry,
|
|
14
|
+
PastoriaCapabilities,
|
|
15
|
+
} from './generate.js';
|
|
16
|
+
import {logger} from './logger.js';
|
|
17
|
+
|
|
18
|
+
async function determineCapabilities(): Promise<PastoriaCapabilities> {
|
|
19
|
+
const capabilities: PastoriaCapabilities = {
|
|
20
|
+
hasAppRoot: false,
|
|
21
|
+
hasServerHandler: false,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
async function hasAppRoot() {
|
|
25
|
+
try {
|
|
26
|
+
await access('__generated__/router/app_root.ts');
|
|
27
|
+
capabilities.hasAppRoot = true;
|
|
28
|
+
} catch {}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function hasServerHandler() {
|
|
32
|
+
try {
|
|
33
|
+
await access('__generated__/router/server_handler.ts');
|
|
34
|
+
capabilities.hasServerHandler = true;
|
|
35
|
+
} catch {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await Promise.all([hasAppRoot(), hasServerHandler()]);
|
|
39
|
+
return capabilities;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function pastoriaEntryPlugin(): Plugin {
|
|
43
|
+
const clientEntryModuleId = 'virtual:pastoria-entry-client.tsx';
|
|
44
|
+
const serverEntryModuleId = 'virtual:pastoria-entry-server.tsx';
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
name: 'pastoria-entry',
|
|
48
|
+
resolveId(id) {
|
|
49
|
+
if (id === clientEntryModuleId) {
|
|
50
|
+
return clientEntryModuleId; // Return without \0 prefix so React plugin can see .tsx extension
|
|
51
|
+
} else if (id === serverEntryModuleId) {
|
|
52
|
+
return serverEntryModuleId;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
async load(id) {
|
|
56
|
+
const capabilities = await determineCapabilities();
|
|
57
|
+
if (id === clientEntryModuleId) {
|
|
58
|
+
return generateClientEntry(capabilities);
|
|
59
|
+
} else if (id === serverEntryModuleId) {
|
|
60
|
+
return generateServerEntry(capabilities);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const CLIENT_BUILD: BuildEnvironmentOptions = {
|
|
67
|
+
outDir: 'dist/client',
|
|
68
|
+
rollupOptions: {
|
|
69
|
+
input: 'virtual:pastoria-entry-client.tsx',
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const SERVER_BUILD: BuildEnvironmentOptions = {
|
|
74
|
+
outDir: 'dist/server',
|
|
75
|
+
ssr: true,
|
|
76
|
+
rollupOptions: {
|
|
77
|
+
input: 'virtual:pastoria-entry-server.tsx',
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export function createBuildConfig(
|
|
82
|
+
buildEnv: BuildEnvironmentOptions,
|
|
83
|
+
): InlineConfig {
|
|
84
|
+
return {
|
|
85
|
+
appType: 'custom' as const,
|
|
86
|
+
customLogger: logger,
|
|
87
|
+
build: {
|
|
88
|
+
...buildEnv,
|
|
89
|
+
assetsInlineLimit: 0,
|
|
90
|
+
manifest: true,
|
|
91
|
+
ssrManifest: true,
|
|
92
|
+
},
|
|
93
|
+
plugins: [
|
|
94
|
+
pastoriaEntryPlugin(),
|
|
95
|
+
tailwindcss() as PluginOption,
|
|
96
|
+
react({
|
|
97
|
+
babel: {
|
|
98
|
+
plugins: [['babel-plugin-react-compiler', {}], 'relay'],
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
cjsInterop({
|
|
102
|
+
dependencies: ['react-relay', 'react-relay/hooks', 'relay-runtime'],
|
|
103
|
+
}),
|
|
104
|
+
],
|
|
105
|
+
ssr: {
|
|
106
|
+
noExternal: ['pastoria-runtime'],
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
package/templates/router.tsx
CHANGED
|
@@ -245,20 +245,7 @@ export function router__createAppFromEntryPoint(
|
|
|
245
245
|
|
|
246
246
|
return (
|
|
247
247
|
<RouterContext value={routerContextValue}>
|
|
248
|
-
{
|
|
249
|
-
<Suspense
|
|
250
|
-
fallback={
|
|
251
|
-
<EntryPointContainer
|
|
252
|
-
entryPointReference={entryPoint.entryPoints.fallback}
|
|
253
|
-
props={{}}
|
|
254
|
-
/>
|
|
255
|
-
}
|
|
256
|
-
>
|
|
257
|
-
<EntryPointContainer entryPointReference={entryPoint} props={{}} />
|
|
258
|
-
</Suspense>
|
|
259
|
-
) : (
|
|
260
|
-
<EntryPointContainer entryPointReference={entryPoint} props={{}} />
|
|
261
|
-
)}
|
|
248
|
+
<EntryPointContainer entryPointReference={entryPoint} props={{}} />
|
|
262
249
|
</RouterContext>
|
|
263
250
|
);
|
|
264
251
|
}
|