@rainfw/core 0.2.1 → 0.2.2
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/package.json +5 -1
- package/scripts/generate.js +217 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rainfw/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "A TypeScript web framework for Cloudflare Workers",
|
|
5
5
|
"bin": {
|
|
6
6
|
"rainjs": "./cli/index.js"
|
|
@@ -58,6 +58,10 @@
|
|
|
58
58
|
"./db": {
|
|
59
59
|
"types": "./dist/db.d.ts",
|
|
60
60
|
"import": "./dist/db.js"
|
|
61
|
+
},
|
|
62
|
+
"./client": {
|
|
63
|
+
"types": "./dist/client/hooks.d.ts",
|
|
64
|
+
"import": "./dist/client/hooks.js"
|
|
61
65
|
}
|
|
62
66
|
},
|
|
63
67
|
"devDependencies": {
|
package/scripts/generate.js
CHANGED
|
@@ -587,6 +587,65 @@ function detectExportedMethods(filePath) {
|
|
|
587
587
|
return detectExportedMethodsFromContent(content);
|
|
588
588
|
}
|
|
589
589
|
|
|
590
|
+
function collectAllExportedNames(node) {
|
|
591
|
+
if (ts.isVariableStatement(node)) {
|
|
592
|
+
return node.declarationList.declarations
|
|
593
|
+
.filter((d) => ts.isIdentifier(d.name))
|
|
594
|
+
.map((d) => d.name.text);
|
|
595
|
+
}
|
|
596
|
+
if (
|
|
597
|
+
(ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) &&
|
|
598
|
+
node.name
|
|
599
|
+
) {
|
|
600
|
+
return [node.name.text];
|
|
601
|
+
}
|
|
602
|
+
return [];
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function collectAllNamedExports(node) {
|
|
606
|
+
if (
|
|
607
|
+
!(
|
|
608
|
+
ts.isExportDeclaration(node) &&
|
|
609
|
+
node.exportClause &&
|
|
610
|
+
ts.isNamedExports(node.exportClause)
|
|
611
|
+
)
|
|
612
|
+
) {
|
|
613
|
+
return [];
|
|
614
|
+
}
|
|
615
|
+
return node.exportClause.elements.map((el) => el.name.text);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function detectAllExportsFromContent(content) {
|
|
619
|
+
const sourceFile = ts.createSourceFile(
|
|
620
|
+
"file.tsx",
|
|
621
|
+
content,
|
|
622
|
+
ts.ScriptTarget.Latest,
|
|
623
|
+
true,
|
|
624
|
+
);
|
|
625
|
+
const named = [];
|
|
626
|
+
let hasDefault = false;
|
|
627
|
+
|
|
628
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
629
|
+
if (ts.isExportAssignment(node) && !node.isExportEquals) {
|
|
630
|
+
hasDefault = true;
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
if (hasExportKeyword(node)) {
|
|
634
|
+
const modifiers = ts.canHaveModifiers(node)
|
|
635
|
+
? ts.getModifiers(node)
|
|
636
|
+
: undefined;
|
|
637
|
+
if (modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)) {
|
|
638
|
+
hasDefault = true;
|
|
639
|
+
} else {
|
|
640
|
+
named.push(...collectAllExportedNames(node));
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
named.push(...collectAllNamedExports(node));
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
return { named, hasDefault };
|
|
647
|
+
}
|
|
648
|
+
|
|
590
649
|
function detectMiddlewareExportFromContent(content) {
|
|
591
650
|
return detectExportedNamesFromContent(content, ["onRequest"]).length > 0;
|
|
592
651
|
}
|
|
@@ -773,6 +832,156 @@ function regenerateClient() {
|
|
|
773
832
|
console.log(`[gen:client] ${clientMsg} -> .rainjs/entry.ts`);
|
|
774
833
|
}
|
|
775
834
|
|
|
835
|
+
function clientFileToIslandId(relPath) {
|
|
836
|
+
return relPath
|
|
837
|
+
.replace(/\\/g, "/")
|
|
838
|
+
.replace(/\.tsx?$/, "")
|
|
839
|
+
.replace(/[^a-zA-Z0-9_/]/g, "_");
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function generateIslandProxy(clientRelPath, srcDir, fwImport) {
|
|
843
|
+
const islandDir = path.join(PROJECT_ROOT, BUILD_CONFIG.outDir, "islands");
|
|
844
|
+
if (!fs.existsSync(islandDir)) {
|
|
845
|
+
fs.mkdirSync(islandDir, { recursive: true });
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const fullPath = path.join(srcDir, clientRelPath);
|
|
849
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
850
|
+
const { named, hasDefault } = detectAllExportsFromContent(content);
|
|
851
|
+
const islandId = clientFileToIslandId(clientRelPath);
|
|
852
|
+
|
|
853
|
+
const proxyFile = path.join(
|
|
854
|
+
islandDir,
|
|
855
|
+
clientRelPath.replace(/\\/g, "/").replace(/\.tsx?$/, ".ts"),
|
|
856
|
+
);
|
|
857
|
+
const proxyDir = path.dirname(proxyFile);
|
|
858
|
+
if (!fs.existsSync(proxyDir)) {
|
|
859
|
+
fs.mkdirSync(proxyDir, { recursive: true });
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
const proxyEntryDir = path.dirname(proxyFile);
|
|
863
|
+
let relOriginal = path
|
|
864
|
+
.relative(
|
|
865
|
+
proxyEntryDir,
|
|
866
|
+
path.join(srcDir, clientRelPath.replace(/\.tsx?$/, "")),
|
|
867
|
+
)
|
|
868
|
+
.replace(/\\/g, "/");
|
|
869
|
+
if (!relOriginal.startsWith(".")) relOriginal = `./${relOriginal}`;
|
|
870
|
+
|
|
871
|
+
const lines = [];
|
|
872
|
+
lines.push(`import { markAsIsland } from "${fwImport}";`);
|
|
873
|
+
|
|
874
|
+
const importSpecifiers = [];
|
|
875
|
+
if (hasDefault) {
|
|
876
|
+
importSpecifiers.push("default as _default");
|
|
877
|
+
}
|
|
878
|
+
for (const name of named) {
|
|
879
|
+
importSpecifiers.push(`${name} as _${name}`);
|
|
880
|
+
}
|
|
881
|
+
if (importSpecifiers.length > 0) {
|
|
882
|
+
lines.push(
|
|
883
|
+
`import { ${importSpecifiers.join(", ")} } from "${relOriginal}";`,
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
lines.push("");
|
|
887
|
+
|
|
888
|
+
if (hasDefault) {
|
|
889
|
+
lines.push(`export default markAsIsland("${islandId}:default", _default);`);
|
|
890
|
+
}
|
|
891
|
+
for (const name of named) {
|
|
892
|
+
lines.push(
|
|
893
|
+
`export const ${name} = markAsIsland("${islandId}:${name}", _${name});`,
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
lines.push("");
|
|
897
|
+
|
|
898
|
+
fs.writeFileSync(proxyFile, lines.join("\n"));
|
|
899
|
+
return { proxyFile, islandId, named, hasDefault };
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function generateAllIslandProxies(clientFiles, srcDir, fwImport) {
|
|
903
|
+
const islandDir = path.join(PROJECT_ROOT, BUILD_CONFIG.outDir, "islands");
|
|
904
|
+
if (fs.existsSync(islandDir)) {
|
|
905
|
+
fs.rmSync(islandDir, { recursive: true, force: true });
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
const proxies = [];
|
|
909
|
+
for (const cf of clientFiles) {
|
|
910
|
+
proxies.push(generateIslandProxy(cf, srcDir, fwImport));
|
|
911
|
+
}
|
|
912
|
+
return proxies;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function updateWranglerAliases(clientFiles, srcDir) {
|
|
916
|
+
const wranglerPath = path.join(PROJECT_ROOT, "wrangler.toml");
|
|
917
|
+
if (!fs.existsSync(wranglerPath)) return;
|
|
918
|
+
|
|
919
|
+
let content = fs.readFileSync(wranglerPath, "utf-8");
|
|
920
|
+
|
|
921
|
+
const markerStart = "# [rain:alias:start]";
|
|
922
|
+
const markerEnd = "# [rain:alias:end]";
|
|
923
|
+
|
|
924
|
+
const startIdx = content.indexOf(markerStart);
|
|
925
|
+
const endIdx = content.indexOf(markerEnd);
|
|
926
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
927
|
+
content =
|
|
928
|
+
content.slice(0, startIdx).trimEnd() +
|
|
929
|
+
"\n" +
|
|
930
|
+
content.slice(endIdx + markerEnd.length).trimStart();
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
if (clientFiles.length === 0) {
|
|
934
|
+
const cleaned = `${content.trimEnd()}
|
|
935
|
+
`;
|
|
936
|
+
if (cleaned !== fs.readFileSync(wranglerPath, "utf-8")) {
|
|
937
|
+
fs.writeFileSync(wranglerPath, cleaned);
|
|
938
|
+
}
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
const islandDir = path.join(PROJECT_ROOT, BUILD_CONFIG.outDir, "islands");
|
|
943
|
+
|
|
944
|
+
const aliasLines = [markerStart];
|
|
945
|
+
const hasExistingAlias = /^\[alias\]/m.test(content);
|
|
946
|
+
if (!hasExistingAlias) {
|
|
947
|
+
aliasLines.push("[alias]");
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
for (const cf of clientFiles) {
|
|
951
|
+
const originalAbs = path.join(srcDir, cf.replace(/\.tsx?$/, ""));
|
|
952
|
+
const proxyAbs = path.join(
|
|
953
|
+
islandDir,
|
|
954
|
+
cf.replace(/\\/g, "/").replace(/\.tsx?$/, ".ts"),
|
|
955
|
+
);
|
|
956
|
+
const relProxy = path.relative(PROJECT_ROOT, proxyAbs).replace(/\\/g, "/");
|
|
957
|
+
const relOriginal = path
|
|
958
|
+
.relative(PROJECT_ROOT, originalAbs)
|
|
959
|
+
.replace(/\\/g, "/");
|
|
960
|
+
aliasLines.push(`"./${relOriginal}" = "./${relProxy}"`);
|
|
961
|
+
}
|
|
962
|
+
aliasLines.push(markerEnd);
|
|
963
|
+
|
|
964
|
+
const aliasBlock = aliasLines.join("\n");
|
|
965
|
+
|
|
966
|
+
if (hasExistingAlias) {
|
|
967
|
+
const aliasIdx = content.search(/^\[alias\]/m);
|
|
968
|
+
let insertAt = content.indexOf("\n", aliasIdx);
|
|
969
|
+
if (insertAt === -1) insertAt = content.length;
|
|
970
|
+
content =
|
|
971
|
+
content.slice(0, insertAt + 1) +
|
|
972
|
+
aliasLines.filter((l) => l !== "[alias]").join("\n") +
|
|
973
|
+
"\n" +
|
|
974
|
+
content.slice(insertAt + 1);
|
|
975
|
+
} else {
|
|
976
|
+
content = `${content.trimEnd()}
|
|
977
|
+
|
|
978
|
+
${aliasBlock}
|
|
979
|
+
`;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
fs.writeFileSync(wranglerPath, content);
|
|
983
|
+
}
|
|
984
|
+
|
|
776
985
|
function generate() {
|
|
777
986
|
if (!fs.existsSync(ROUTES_DIR)) {
|
|
778
987
|
console.error(
|
|
@@ -851,6 +1060,9 @@ function generate() {
|
|
|
851
1060
|
? relativeImportPath(path.join(PROJECT_ROOT, fwPkg))
|
|
852
1061
|
: fwPkg;
|
|
853
1062
|
|
|
1063
|
+
generateAllIslandProxies(clientFiles, srcDir, frameworkImport);
|
|
1064
|
+
updateWranglerAliases(clientFiles, srcDir);
|
|
1065
|
+
|
|
854
1066
|
const headerImports = [`import { Rain } from "${frameworkImport}";`];
|
|
855
1067
|
if (hasConfig) {
|
|
856
1068
|
const configPath = relativeImportPath(
|
|
@@ -906,6 +1118,11 @@ module.exports = {
|
|
|
906
1118
|
detectDefaultExport,
|
|
907
1119
|
detectDefaultExportFromContent,
|
|
908
1120
|
detectUseClientDirective,
|
|
1121
|
+
detectAllExportsFromContent,
|
|
1122
|
+
generateIslandProxy,
|
|
1123
|
+
generateAllIslandProxies,
|
|
1124
|
+
updateWranglerAliases,
|
|
1125
|
+
clientFileToIslandId,
|
|
909
1126
|
bundleClientFilesSync,
|
|
910
1127
|
validateNoPageRouteColocation,
|
|
911
1128
|
validateNoDuplicateUrls,
|