pastoria 1.0.11 → 1.0.13
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 +5 -5
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +213 -164
- 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 +33 -1
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +150 -48
- package/dist/generate.js.map +1 -1
- package/dist/index.js +5 -6
- 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 +19 -9
- package/src/build.ts +277 -177
- package/src/devserver.ts +4 -3
- package/src/generate.ts +182 -64
- package/src/index.ts +8 -7
- 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) {
|
|
@@ -78,7 +77,7 @@ type ExportedSymbol = {
|
|
|
78
77
|
symbol: Symbol;
|
|
79
78
|
};
|
|
80
79
|
|
|
81
|
-
interface PastoriaMetadata {
|
|
80
|
+
export interface PastoriaMetadata {
|
|
82
81
|
resources: Map<string, RouterResource>;
|
|
83
82
|
routes: Map<string, RouterRoute>;
|
|
84
83
|
serverHandlers: Map<string, ExportedSymbol>;
|
|
@@ -87,9 +86,39 @@ interface PastoriaMetadata {
|
|
|
87
86
|
}
|
|
88
87
|
|
|
89
88
|
// Regex to quickly check if a file contains any Pastoria JSDoc tags
|
|
90
|
-
const PASTORIA_TAG_REGEX =
|
|
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>();
|
|
@@ -152,36 +181,13 @@ function collectPastoriaMetadata(project: Project): PastoriaMetadata {
|
|
|
152
181
|
serverHandlers.set(tag.comment, {sourceFile, symbol});
|
|
153
182
|
break;
|
|
154
183
|
}
|
|
155
|
-
// case 'query': {
|
|
156
|
-
// const match = tag.comment.match(
|
|
157
|
-
// /^\s*\{\s*(?<query>\w+)\s*\}\s+(?<name>\w+)\s*$/,
|
|
158
|
-
// )?.groups;
|
|
159
|
-
|
|
160
|
-
// if (match && match.query && match.name) {
|
|
161
|
-
// resourceQueries.set(match.name, match.query);
|
|
162
|
-
// }
|
|
163
|
-
// break;
|
|
164
|
-
// }
|
|
165
|
-
// case 'entrypoint': {
|
|
166
|
-
// const match = tag.comment.match(
|
|
167
|
-
// /^\s*\{\s*(?<resource>[\w#]+)\s*\}\s+(?<name>\w+)\s*$/,
|
|
168
|
-
// )?.groups;
|
|
169
|
-
|
|
170
|
-
// if (match && match.resource && match.name) {
|
|
171
|
-
// resourceEntryPoints.set(match.name, match.resource);
|
|
172
|
-
// }
|
|
173
|
-
// break;
|
|
174
|
-
// }
|
|
175
184
|
}
|
|
176
185
|
} else {
|
|
177
186
|
// Handle tags without comments (like @ExportedSymbol, @gqlContext)
|
|
178
187
|
switch (tag.tagName.getText()) {
|
|
179
188
|
case 'appRoot': {
|
|
180
189
|
if (appRoot != null) {
|
|
181
|
-
|
|
182
|
-
pc.yellow('Warning:'),
|
|
183
|
-
'Multiple @appRoot tags found. Using the first one.',
|
|
184
|
-
);
|
|
190
|
+
logWarn('Multiple @appRoot tags found. Using the first one.');
|
|
185
191
|
} else {
|
|
186
192
|
appRoot = {
|
|
187
193
|
sourceFile,
|
|
@@ -215,8 +221,7 @@ function collectPastoriaMetadata(project: Project): PastoriaMetadata {
|
|
|
215
221
|
|
|
216
222
|
if (extendsPastoriaRootContext) {
|
|
217
223
|
if (gqlContext != null) {
|
|
218
|
-
|
|
219
|
-
pc.yellow('Warning:'),
|
|
224
|
+
logWarn(
|
|
220
225
|
'Multiple classes with @gqlContext extending PastoriaRootContext found. Using the first one.',
|
|
221
226
|
);
|
|
222
227
|
} else {
|
|
@@ -328,35 +333,55 @@ function getResourceQueriesAndEntryPoints(symbol: Symbol): {
|
|
|
328
333
|
return resource;
|
|
329
334
|
}
|
|
330
335
|
|
|
331
|
-
function zodSchemaOfType(
|
|
332
|
-
|
|
336
|
+
function zodSchemaOfType(
|
|
337
|
+
sf: SourceFile,
|
|
338
|
+
tc: ts.TypeChecker,
|
|
339
|
+
t: ts.Type,
|
|
340
|
+
): string {
|
|
341
|
+
if (t.aliasSymbol) {
|
|
342
|
+
const decl = t.aliasSymbol.declarations?.at(0);
|
|
343
|
+
if (decl == null) {
|
|
344
|
+
logWarn('Could not handle type:', tc.typeToString(t));
|
|
345
|
+
return `z.any()`;
|
|
346
|
+
} else {
|
|
347
|
+
const importPath = sf.getRelativePathAsModuleSpecifierTo(
|
|
348
|
+
decl.getSourceFile().fileName,
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
return `z.transform((s: string) => s as import('${importPath}').${t.aliasSymbol.getName()})`;
|
|
352
|
+
}
|
|
353
|
+
} else if (t.getFlags() & TypeFlags.String) {
|
|
333
354
|
return `z.pipe(z.string(), z.transform(decodeURIComponent))`;
|
|
334
355
|
} else if (t.getFlags() & TypeFlags.Number) {
|
|
335
356
|
return `z.coerce.number<number>()`;
|
|
336
357
|
} else if (t.getFlags() & TypeFlags.Null) {
|
|
337
358
|
return `z.preprocess(s => s == null ? undefined : s, z.undefined())`;
|
|
338
359
|
} else if (t.isUnion()) {
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
360
|
+
const nullishTypes: ts.Type[] = [];
|
|
361
|
+
const nonNullishTypes: ts.Type[] = [];
|
|
362
|
+
for (const s of t.types) {
|
|
363
|
+
const flags = s.getFlags();
|
|
364
|
+
if (flags & TypeFlags.Null || flags & TypeFlags.Undefined) {
|
|
365
|
+
nullishTypes.push(s);
|
|
366
|
+
} else {
|
|
367
|
+
nonNullishTypes.push(s);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
347
370
|
|
|
348
|
-
|
|
371
|
+
if (nullishTypes.length > 0 && nonNullishTypes.length > 0) {
|
|
372
|
+
const nonOptionalType = t.getNonNullableType();
|
|
373
|
+
return `z.pipe(z.nullish(${zodSchemaOfType(sf, tc, nonOptionalType)}), z.transform(s => s == null ? undefined : s))`;
|
|
349
374
|
} else {
|
|
350
|
-
return `z.union([${t.types.map((it) => zodSchemaOfType(tc, it)).join(', ')}])`;
|
|
375
|
+
return `z.union([${t.types.map((it) => zodSchemaOfType(sf, tc, it)).join(', ')}])`;
|
|
351
376
|
}
|
|
352
377
|
} else if (tc.isArrayLikeType(t)) {
|
|
353
378
|
const typeArg = tc.getTypeArguments(t as ts.TypeReference)[0];
|
|
354
379
|
const argZodSchema =
|
|
355
|
-
typeArg == null ? `z.any()` : zodSchemaOfType(tc, typeArg);
|
|
380
|
+
typeArg == null ? `z.any()` : zodSchemaOfType(sf, tc, typeArg);
|
|
356
381
|
|
|
357
382
|
return `z.array(${argZodSchema})`;
|
|
358
383
|
} else {
|
|
359
|
-
|
|
384
|
+
logWarn('Could not handle type:', tc.typeToString(t));
|
|
360
385
|
return `z.any()`;
|
|
361
386
|
}
|
|
362
387
|
}
|
|
@@ -435,7 +460,7 @@ async function generateRouter(project: Project, metadata: PastoriaMetadata) {
|
|
|
435
460
|
routerConf.getPropertyOrThrow('noop').remove();
|
|
436
461
|
|
|
437
462
|
let entryPointImportIndex = 0;
|
|
438
|
-
for (
|
|
463
|
+
for (let [
|
|
439
464
|
routeName,
|
|
440
465
|
{sourceFile, symbol, params},
|
|
441
466
|
] of metadata.routes.entries()) {
|
|
@@ -477,6 +502,10 @@ async function generateRouter(project: Project, metadata: PastoriaMetadata) {
|
|
|
477
502
|
},
|
|
478
503
|
});
|
|
479
504
|
|
|
505
|
+
if (params.size === 0 && consumedQueries.size > 0) {
|
|
506
|
+
params = collectQueryParameters(project, Array.from(consumedQueries));
|
|
507
|
+
}
|
|
508
|
+
|
|
480
509
|
for (const query of consumedQueries) {
|
|
481
510
|
routerTemplate.addImportDeclaration({
|
|
482
511
|
moduleSpecifier: `#genfiles/queries/${query}$parameters`,
|
|
@@ -517,7 +546,7 @@ async function generateRouter(project: Project, metadata: PastoriaMetadata) {
|
|
|
517
546
|
writer.writeLine(`schema: z.object({`);
|
|
518
547
|
for (const [paramName, paramType] of Array.from(params)) {
|
|
519
548
|
writer.writeLine(
|
|
520
|
-
` ${paramName}: ${zodSchemaOfType(tc, paramType)},`,
|
|
549
|
+
` ${paramName}: ${zodSchemaOfType(routerTemplate, tc, paramType)},`,
|
|
521
550
|
);
|
|
522
551
|
}
|
|
523
552
|
|
|
@@ -528,7 +557,7 @@ async function generateRouter(project: Project, metadata: PastoriaMetadata) {
|
|
|
528
557
|
},
|
|
529
558
|
});
|
|
530
559
|
|
|
531
|
-
|
|
560
|
+
logInfo(
|
|
532
561
|
'Created route',
|
|
533
562
|
pc.cyan(routeName),
|
|
534
563
|
'for',
|
|
@@ -579,7 +608,7 @@ async function generateJsResource(
|
|
|
579
608
|
},
|
|
580
609
|
});
|
|
581
610
|
|
|
582
|
-
|
|
611
|
+
logInfo(
|
|
583
612
|
'Created resource',
|
|
584
613
|
pc.cyan(resourceName),
|
|
585
614
|
'for',
|
|
@@ -627,7 +656,7 @@ export {${appRootSymbol.getName()} as App} from '${moduleSpecifier}';
|
|
|
627
656
|
|
|
628
657
|
await appRootFile.save();
|
|
629
658
|
|
|
630
|
-
|
|
659
|
+
logInfo(
|
|
631
660
|
'Created app root for',
|
|
632
661
|
pc.green(appRootSymbol.getName()),
|
|
633
662
|
'exported from',
|
|
@@ -663,7 +692,7 @@ async function generateGraphqlContext(
|
|
|
663
692
|
export {${contextSymbol.getName()} as Context} from '${moduleSpecifier}';
|
|
664
693
|
`);
|
|
665
694
|
|
|
666
|
-
|
|
695
|
+
logInfo(
|
|
667
696
|
'Created GraphQL context for',
|
|
668
697
|
pc.green(contextSymbol.getName()),
|
|
669
698
|
'exported from',
|
|
@@ -683,10 +712,7 @@ import {PastoriaRootContext} from 'pastoria-runtime/server';
|
|
|
683
712
|
export class Context extends PastoriaRootContext {}
|
|
684
713
|
`);
|
|
685
714
|
|
|
686
|
-
|
|
687
|
-
'No @gqlContext found, generating default',
|
|
688
|
-
pc.green('Context'),
|
|
689
|
-
);
|
|
715
|
+
logInfo('No @gqlContext found, generating default', pc.green('Context'));
|
|
690
716
|
}
|
|
691
717
|
|
|
692
718
|
await contextFile.save();
|
|
@@ -735,7 +761,7 @@ export const router = express.Router();
|
|
|
735
761
|
`router.use('${routeName}', ${importAlias})`,
|
|
736
762
|
);
|
|
737
763
|
|
|
738
|
-
|
|
764
|
+
logInfo(
|
|
739
765
|
'Created server handler',
|
|
740
766
|
pc.cyan(routeName),
|
|
741
767
|
'for',
|
|
@@ -748,20 +774,112 @@ export const router = express.Router();
|
|
|
748
774
|
await serverHandlerTemplate.save();
|
|
749
775
|
}
|
|
750
776
|
|
|
751
|
-
export async function
|
|
752
|
-
const targetDir = process.cwd();
|
|
753
|
-
const project = new Project({
|
|
754
|
-
tsConfigFilePath: path.join(targetDir, 'tsconfig.json'),
|
|
755
|
-
manipulationSettings: {
|
|
756
|
-
indentationText: IndentationText.TwoSpaces,
|
|
757
|
-
},
|
|
758
|
-
});
|
|
759
|
-
|
|
777
|
+
export async function generatePastoriaExports(project: Project) {
|
|
760
778
|
const metadata = collectPastoriaMetadata(project);
|
|
761
779
|
|
|
762
780
|
await generateAppRoot(project, metadata);
|
|
763
781
|
await generateGraphqlContext(project, metadata);
|
|
782
|
+
return metadata;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
export async function generatePastoriaArtifacts(
|
|
786
|
+
project: Project,
|
|
787
|
+
metadata = collectPastoriaMetadata(project),
|
|
788
|
+
) {
|
|
764
789
|
await generateRouter(project, metadata);
|
|
765
790
|
await generateJsResource(project, metadata);
|
|
766
791
|
await generateServerHandler(project, metadata);
|
|
767
792
|
}
|
|
793
|
+
|
|
794
|
+
export interface PastoriaCapabilities {
|
|
795
|
+
hasAppRoot: boolean;
|
|
796
|
+
hasServerHandler: boolean;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
export function generateClientEntry({
|
|
800
|
+
hasAppRoot,
|
|
801
|
+
}: PastoriaCapabilities): string {
|
|
802
|
+
const appImport = hasAppRoot
|
|
803
|
+
? `import {App} from '#genfiles/router/app_root';`
|
|
804
|
+
: '';
|
|
805
|
+
const appValue = hasAppRoot ? 'App' : 'null';
|
|
806
|
+
|
|
807
|
+
return `// Generated by Pastoria.
|
|
808
|
+
import {createRouterApp} from '#genfiles/router/router';
|
|
809
|
+
${appImport}
|
|
810
|
+
import {hydrateRoot} from 'react-dom/client';
|
|
811
|
+
|
|
812
|
+
async function main() {
|
|
813
|
+
const RouterApp = await createRouterApp();
|
|
814
|
+
hydrateRoot(document, <RouterApp App={${appValue}} />);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
main();
|
|
818
|
+
`;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
export function generateServerEntry({
|
|
822
|
+
hasAppRoot,
|
|
823
|
+
hasServerHandler,
|
|
824
|
+
}: PastoriaCapabilities): string {
|
|
825
|
+
const appImport = hasAppRoot
|
|
826
|
+
? `import {App} from '#genfiles/router/app_root';`
|
|
827
|
+
: '';
|
|
828
|
+
const appValue = hasAppRoot ? 'App' : 'null';
|
|
829
|
+
|
|
830
|
+
const serverHandlerImport = hasServerHandler
|
|
831
|
+
? `import {router as serverHandler} from '#genfiles/router/server_handler';`
|
|
832
|
+
: '';
|
|
833
|
+
const serverHandlerUse = hasServerHandler
|
|
834
|
+
? ' router.use(serverHandler)'
|
|
835
|
+
: '';
|
|
836
|
+
|
|
837
|
+
return `// Generated by Pastoria.
|
|
838
|
+
import {JSResource} from '#genfiles/router/js_resource';
|
|
839
|
+
import {
|
|
840
|
+
listRoutes,
|
|
841
|
+
router__createAppFromEntryPoint,
|
|
842
|
+
router__loadEntryPoint,
|
|
843
|
+
} from '#genfiles/router/router';
|
|
844
|
+
import {getSchema} from '#genfiles/schema/schema';
|
|
845
|
+
import {Context} from '#genfiles/router/context';
|
|
846
|
+
${appImport}
|
|
847
|
+
${serverHandlerImport}
|
|
848
|
+
import express from 'express';
|
|
849
|
+
import {GraphQLSchema, specifiedDirectives} from 'graphql';
|
|
850
|
+
import {PastoriaConfig} from 'pastoria-config';
|
|
851
|
+
import {createRouterHandler} from 'pastoria-runtime/server';
|
|
852
|
+
import type {Manifest} from 'vite';
|
|
853
|
+
|
|
854
|
+
const schemaConfig = getSchema().toConfig();
|
|
855
|
+
const schema = new GraphQLSchema({
|
|
856
|
+
...schemaConfig,
|
|
857
|
+
directives: [...specifiedDirectives, ...schemaConfig.directives],
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
export function createHandler(
|
|
861
|
+
persistedQueries: Record<string, string>,
|
|
862
|
+
config: Required<PastoriaConfig>,
|
|
863
|
+
manifest?: Manifest,
|
|
864
|
+
) {
|
|
865
|
+
const routeHandler = createRouterHandler(
|
|
866
|
+
listRoutes(),
|
|
867
|
+
JSResource.srcOfModuleId,
|
|
868
|
+
router__loadEntryPoint,
|
|
869
|
+
router__createAppFromEntryPoint,
|
|
870
|
+
${appValue},
|
|
871
|
+
schema,
|
|
872
|
+
(req) => Context.createFromRequest(req),
|
|
873
|
+
persistedQueries,
|
|
874
|
+
config,
|
|
875
|
+
manifest,
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
const router = express.Router();
|
|
879
|
+
router.use(routeHandler);
|
|
880
|
+
${serverHandlerUse}
|
|
881
|
+
|
|
882
|
+
return router;
|
|
883
|
+
}
|
|
884
|
+
`;
|
|
885
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,6 @@ import {readFile} from 'node:fs/promises';
|
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import {createBuild} from './build.js';
|
|
7
7
|
import {startDevserver} from './devserver.js';
|
|
8
|
-
import {generatePastoriaArtifacts} from './generate.js';
|
|
9
8
|
|
|
10
9
|
async function main() {
|
|
11
10
|
const packageData = JSON.parse(
|
|
@@ -17,11 +16,6 @@ async function main() {
|
|
|
17
16
|
.description(packageData.description)
|
|
18
17
|
.version(packageData.version);
|
|
19
18
|
|
|
20
|
-
program
|
|
21
|
-
.command('gen')
|
|
22
|
-
.description('Run Pastoria code generation')
|
|
23
|
-
.action(generatePastoriaArtifacts);
|
|
24
|
-
|
|
25
19
|
program
|
|
26
20
|
.command('dev')
|
|
27
21
|
.description('Start the pastoria devserver')
|
|
@@ -29,8 +23,15 @@ async function main() {
|
|
|
29
23
|
.action(startDevserver);
|
|
30
24
|
|
|
31
25
|
program
|
|
32
|
-
.command('
|
|
26
|
+
.command('make')
|
|
33
27
|
.description('Creates a production build of the project')
|
|
28
|
+
.argument(
|
|
29
|
+
'[steps...]',
|
|
30
|
+
'Specific build steps to run (schema, relay, router). If not provided, will infer from changed files.',
|
|
31
|
+
)
|
|
32
|
+
.option('-B, --always-make', 'Always make, never cache')
|
|
33
|
+
.option('-R, --release', 'Build for production')
|
|
34
|
+
.option('-w, --watch', 'Watch for changes and rebuild')
|
|
34
35
|
.action(createBuild);
|
|
35
36
|
|
|
36
37
|
program.parseAsync();
|
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
|
}
|