@vertz/codegen 0.2.13 → 0.2.15
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/dist/index.js +131 -19
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -116,7 +116,7 @@ async function formatWithBiome(files) {
|
|
|
116
116
|
}
|
|
117
117
|
// src/generate.ts
|
|
118
118
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
119
|
-
import { dirname as
|
|
119
|
+
import { dirname as dirname4, join as join4, resolve as resolve4 } from "node:path";
|
|
120
120
|
|
|
121
121
|
// src/generators/client-generator.ts
|
|
122
122
|
import { posix } from "node:path";
|
|
@@ -666,9 +666,119 @@ ${props};
|
|
|
666
666
|
}
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
+
// src/generators/router-augmentation-generator.ts
|
|
670
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
671
|
+
import { dirname as dirname2, extname, join as join2, relative, resolve as resolve2, sep } from "node:path";
|
|
672
|
+
var FILE_HEADER5 = `// Generated by @vertz/codegen — do not edit
|
|
673
|
+
|
|
674
|
+
`;
|
|
675
|
+
var CANDIDATE_ROUTE_FILES = [
|
|
676
|
+
["src", "router.ts"],
|
|
677
|
+
["src", "router.tsx"],
|
|
678
|
+
["src", "ui", "router.ts"],
|
|
679
|
+
["src", "ui", "router.tsx"]
|
|
680
|
+
];
|
|
681
|
+
var SOURCE_EXTENSIONS = new Set([".ts", ".tsx"]);
|
|
682
|
+
var EXPORTED_ROUTES_PATTERN = /export\s+const\s+routes\b/m;
|
|
683
|
+
var REEXPORTED_ROUTES_PATTERN = /const\s+routes\b[\s\S]*?export\s*\{\s*routes\b/m;
|
|
684
|
+
|
|
685
|
+
class RouterAugmentationGenerator {
|
|
686
|
+
name = "router-augmentation";
|
|
687
|
+
generate(_ir, config) {
|
|
688
|
+
const projectRoot = findProjectRoot(config.outputDir);
|
|
689
|
+
if (!projectRoot)
|
|
690
|
+
return [];
|
|
691
|
+
const routeModulePath = findRouteModule(projectRoot);
|
|
692
|
+
if (!routeModulePath)
|
|
693
|
+
return [];
|
|
694
|
+
return [
|
|
695
|
+
{
|
|
696
|
+
path: "router.d.ts",
|
|
697
|
+
content: renderRouterAugmentation(routeModulePath, config.outputDir)
|
|
698
|
+
}
|
|
699
|
+
];
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function findProjectRoot(outputDir) {
|
|
703
|
+
let current = resolve2(outputDir);
|
|
704
|
+
while (true) {
|
|
705
|
+
if (existsSync(join2(current, "package.json"))) {
|
|
706
|
+
return current;
|
|
707
|
+
}
|
|
708
|
+
const parent = dirname2(current);
|
|
709
|
+
if (parent === current) {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
current = parent;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function findRouteModule(projectRoot) {
|
|
716
|
+
for (const candidate of CANDIDATE_ROUTE_FILES) {
|
|
717
|
+
const candidatePath = join2(projectRoot, ...candidate);
|
|
718
|
+
if (existsSync(candidatePath) && fileExportsRoutes(candidatePath)) {
|
|
719
|
+
return candidatePath;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return scanForRouteModule(join2(projectRoot, "src"));
|
|
723
|
+
}
|
|
724
|
+
function scanForRouteModule(dir) {
|
|
725
|
+
if (!existsSync(dir))
|
|
726
|
+
return null;
|
|
727
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
728
|
+
const fullPath = join2(dir, entry.name);
|
|
729
|
+
if (entry.isDirectory()) {
|
|
730
|
+
const nested = scanForRouteModule(fullPath);
|
|
731
|
+
if (nested) {
|
|
732
|
+
return nested;
|
|
733
|
+
}
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
if (!entry.isFile())
|
|
737
|
+
continue;
|
|
738
|
+
const extension = extname(entry.name);
|
|
739
|
+
if (extension === ".d.ts" || !SOURCE_EXTENSIONS.has(extension))
|
|
740
|
+
continue;
|
|
741
|
+
if (fileExportsRoutes(fullPath)) {
|
|
742
|
+
return fullPath;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
function fileExportsRoutes(filePath) {
|
|
748
|
+
const source = readFileSync(filePath, "utf-8");
|
|
749
|
+
if (!source.includes("defineRoutes("))
|
|
750
|
+
return false;
|
|
751
|
+
return EXPORTED_ROUTES_PATTERN.test(source) || REEXPORTED_ROUTES_PATTERN.test(source);
|
|
752
|
+
}
|
|
753
|
+
function renderRouterAugmentation(routeModulePath, outputDir) {
|
|
754
|
+
const importPath = toImportPath(relative(resolve2(outputDir), routeModulePath));
|
|
755
|
+
return [
|
|
756
|
+
FILE_HEADER5,
|
|
757
|
+
"import type { InferRouteMap, TypedRouter, UnwrapSignals } from '@vertz/ui';",
|
|
758
|
+
`import type { routes } from '${importPath}';`,
|
|
759
|
+
"",
|
|
760
|
+
"type AppRouteMap = InferRouteMap<typeof routes>;",
|
|
761
|
+
"",
|
|
762
|
+
"declare module '@vertz/ui' {",
|
|
763
|
+
" export function useRouter(): UnwrapSignals<TypedRouter<AppRouteMap>>;",
|
|
764
|
+
"}",
|
|
765
|
+
"",
|
|
766
|
+
"declare module '@vertz/ui/router' {",
|
|
767
|
+
" export function useRouter(): UnwrapSignals<TypedRouter<AppRouteMap>>;",
|
|
768
|
+
"}",
|
|
769
|
+
""
|
|
770
|
+
].join(`
|
|
771
|
+
`);
|
|
772
|
+
}
|
|
773
|
+
function toImportPath(pathToModule) {
|
|
774
|
+
const normalized = pathToModule.split(sep).join("/");
|
|
775
|
+
const withoutExtension = normalized.replace(/\.[^.]+$/, "");
|
|
776
|
+
return withoutExtension.startsWith(".") ? withoutExtension : `./${withoutExtension}`;
|
|
777
|
+
}
|
|
778
|
+
|
|
669
779
|
// src/incremental.ts
|
|
670
780
|
import { mkdir as mkdir2, readdir, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
671
|
-
import { dirname as
|
|
781
|
+
import { dirname as dirname3, join as join3, relative as relative2, resolve as resolve3 } from "node:path";
|
|
672
782
|
|
|
673
783
|
// src/hasher.ts
|
|
674
784
|
import { createHash } from "node:crypto";
|
|
@@ -686,11 +796,11 @@ async function collectFiles(dir, baseDir) {
|
|
|
686
796
|
return results;
|
|
687
797
|
}
|
|
688
798
|
for (const entry of entries) {
|
|
689
|
-
const fullPath =
|
|
799
|
+
const fullPath = join3(dir, entry.name);
|
|
690
800
|
if (entry.isDirectory()) {
|
|
691
801
|
results.push(...await collectFiles(fullPath, baseDir));
|
|
692
802
|
} else if (entry.isFile()) {
|
|
693
|
-
results.push(
|
|
803
|
+
results.push(relative2(baseDir, fullPath));
|
|
694
804
|
}
|
|
695
805
|
}
|
|
696
806
|
return results;
|
|
@@ -702,12 +812,12 @@ async function writeIncremental(files, outputDir, options) {
|
|
|
702
812
|
await mkdir2(outputDir, { recursive: true });
|
|
703
813
|
const generatedPaths = new Set(files.map((f) => f.path));
|
|
704
814
|
for (const file of files) {
|
|
705
|
-
const filePath =
|
|
706
|
-
const resolvedPath =
|
|
707
|
-
if (!resolvedPath.startsWith(
|
|
815
|
+
const filePath = join3(outputDir, file.path);
|
|
816
|
+
const resolvedPath = resolve3(filePath);
|
|
817
|
+
if (!resolvedPath.startsWith(resolve3(outputDir))) {
|
|
708
818
|
throw new Error(`Generated file path "${file.path}" escapes output directory`);
|
|
709
819
|
}
|
|
710
|
-
const dir =
|
|
820
|
+
const dir = dirname3(filePath);
|
|
711
821
|
await mkdir2(dir, { recursive: true });
|
|
712
822
|
let existingContent;
|
|
713
823
|
try {
|
|
@@ -724,7 +834,7 @@ async function writeIncremental(files, outputDir, options) {
|
|
|
724
834
|
const existingFiles = await collectFiles(outputDir, outputDir);
|
|
725
835
|
for (const existing of existingFiles) {
|
|
726
836
|
if (!generatedPaths.has(existing)) {
|
|
727
|
-
await rm2(
|
|
837
|
+
await rm2(join3(outputDir, existing), { force: true });
|
|
728
838
|
removed.push(existing);
|
|
729
839
|
}
|
|
730
840
|
}
|
|
@@ -863,6 +973,8 @@ function runTypescriptGenerator(ir, _config) {
|
|
|
863
973
|
files.push(...entitySdkGen.generate(ir, generatorConfig));
|
|
864
974
|
const clientGen = new ClientGenerator;
|
|
865
975
|
files.push(...clientGen.generate(ir, generatorConfig));
|
|
976
|
+
const routerAugmentationGen = new RouterAugmentationGenerator;
|
|
977
|
+
files.push(...routerAugmentationGen.generate(ir, generatorConfig));
|
|
866
978
|
return files;
|
|
867
979
|
}
|
|
868
980
|
function generateSync(ir, config) {
|
|
@@ -889,10 +1001,10 @@ async function mergeImportsToPackageJson(files, outputDir) {
|
|
|
889
1001
|
const imports = generated.imports;
|
|
890
1002
|
if (!imports || Object.keys(imports).length === 0)
|
|
891
1003
|
return false;
|
|
892
|
-
const projectRoot = await
|
|
1004
|
+
const projectRoot = await findProjectRoot2(resolve4(outputDir));
|
|
893
1005
|
if (!projectRoot)
|
|
894
1006
|
return false;
|
|
895
|
-
const pkgPath =
|
|
1007
|
+
const pkgPath = join4(projectRoot, "package.json");
|
|
896
1008
|
const raw = await readFile3(pkgPath, "utf-8");
|
|
897
1009
|
const pkg = JSON.parse(raw);
|
|
898
1010
|
const existing = pkg.imports;
|
|
@@ -904,15 +1016,15 @@ async function mergeImportsToPackageJson(files, outputDir) {
|
|
|
904
1016
|
`, "utf-8");
|
|
905
1017
|
return true;
|
|
906
1018
|
}
|
|
907
|
-
async function
|
|
1019
|
+
async function findProjectRoot2(startDir) {
|
|
908
1020
|
let dir = startDir;
|
|
909
|
-
const root =
|
|
1021
|
+
const root = dirname4(dir);
|
|
910
1022
|
while (dir !== root) {
|
|
911
1023
|
try {
|
|
912
|
-
await readFile3(
|
|
1024
|
+
await readFile3(join4(dir, "package.json"), "utf-8");
|
|
913
1025
|
return dir;
|
|
914
1026
|
} catch {
|
|
915
|
-
dir =
|
|
1027
|
+
dir = dirname4(dir);
|
|
916
1028
|
}
|
|
917
1029
|
}
|
|
918
1030
|
return null;
|
|
@@ -932,12 +1044,12 @@ async function generate(appIR, config) {
|
|
|
932
1044
|
} else {
|
|
933
1045
|
await mkdir3(config.outputDir, { recursive: true });
|
|
934
1046
|
for (const file of files) {
|
|
935
|
-
const filePath =
|
|
936
|
-
const resolvedPath =
|
|
937
|
-
if (!resolvedPath.startsWith(
|
|
1047
|
+
const filePath = join4(config.outputDir, file.path);
|
|
1048
|
+
const resolvedPath = resolve4(filePath);
|
|
1049
|
+
if (!resolvedPath.startsWith(resolve4(config.outputDir))) {
|
|
938
1050
|
throw new Error(`Generated file path "${file.path}" escapes output directory`);
|
|
939
1051
|
}
|
|
940
|
-
const dir =
|
|
1052
|
+
const dir = dirname4(filePath);
|
|
941
1053
|
await mkdir3(dir, { recursive: true });
|
|
942
1054
|
await writeFile3(filePath, file.content, "utf-8");
|
|
943
1055
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/codegen",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Vertz code generation — internal, no stability guarantee",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@vertz/compiler": "^0.2.
|
|
30
|
+
"@vertz/compiler": "^0.2.14"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^25.3.1",
|