@rainfw/core 0.1.1 → 0.2.0
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/README.md +28 -28
- package/dist/client/dom.d.ts +4 -0
- package/dist/client/dom.d.ts.map +1 -0
- package/dist/client/dom.js +130 -0
- package/dist/client/dom.js.map +1 -0
- package/dist/client/hooks.d.ts +58 -0
- package/dist/client/hooks.d.ts.map +1 -0
- package/dist/client/hooks.js +173 -0
- package/dist/client/hooks.js.map +1 -0
- package/dist/client/hydrate.d.ts +14 -0
- package/dist/client/hydrate.d.ts.map +1 -0
- package/dist/client/hydrate.js +167 -0
- package/dist/client/hydrate.js.map +1 -0
- package/dist/client/jsx-runtime.d.ts +6 -0
- package/dist/client/jsx-runtime.d.ts.map +1 -0
- package/dist/client/jsx-runtime.js +20 -0
- package/dist/client/jsx-runtime.js.map +1 -0
- package/dist/client/reconciler.d.ts +4 -0
- package/dist/client/reconciler.d.ts.map +1 -0
- package/dist/client/reconciler.js +238 -0
- package/dist/client/reconciler.js.map +1 -0
- package/dist/client/runtime.d.ts +6 -0
- package/dist/client/runtime.d.ts.map +1 -0
- package/dist/client/runtime.js +17 -0
- package/dist/client/runtime.js.map +1 -0
- package/dist/client/scheduler.d.ts +4 -0
- package/dist/client/scheduler.d.ts.map +1 -0
- package/dist/client/scheduler.js +44 -0
- package/dist/client/scheduler.js.map +1 -0
- package/dist/compiler/inject.d.ts +6 -0
- package/dist/compiler/inject.d.ts.map +1 -0
- package/dist/compiler/inject.js +19 -0
- package/dist/compiler/inject.js.map +1 -0
- package/dist/compiler/server-action.d.ts +9 -0
- package/dist/compiler/server-action.d.ts.map +1 -0
- package/dist/compiler/server-action.js +98 -0
- package/dist/compiler/server-action.js.map +1 -0
- package/dist/context.js +1 -1
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/jsx/index.d.ts +3 -2
- package/dist/jsx/index.d.ts.map +1 -1
- package/dist/jsx/index.js +2 -2
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/render.d.ts +7 -1
- package/dist/jsx/render.d.ts.map +1 -1
- package/dist/jsx/render.js +117 -10
- package/dist/jsx/render.js.map +1 -1
- package/dist/jsx/types.d.ts +4 -0
- package/dist/jsx/types.d.ts.map +1 -1
- package/dist/jsx/types.js +10 -0
- package/dist/jsx/types.js.map +1 -1
- package/dist/router.d.ts +7 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +84 -3
- package/dist/router.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -1
- package/scripts/dev.js +28 -1
- package/scripts/generate.js +192 -10
package/scripts/generate.js
CHANGED
|
@@ -3,6 +3,13 @@ const path = require("node:path");
|
|
|
3
3
|
const { execSync } = require("node:child_process");
|
|
4
4
|
const ts = require("typescript");
|
|
5
5
|
|
|
6
|
+
let esbuild;
|
|
7
|
+
try {
|
|
8
|
+
esbuild = require("esbuild");
|
|
9
|
+
} catch (_esbuildOptional) {
|
|
10
|
+
esbuild = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
const PROJECT_ROOT = process.cwd();
|
|
7
14
|
|
|
8
15
|
function unwrapExpression(node) {
|
|
@@ -27,7 +34,11 @@ function extractStringProps(obj, sourceFile, keys) {
|
|
|
27
34
|
|
|
28
35
|
function loadBuildConfig() {
|
|
29
36
|
const configPath = path.join(PROJECT_ROOT, "rain.config.ts");
|
|
30
|
-
const defaults = {
|
|
37
|
+
const defaults = {
|
|
38
|
+
routesDir: "src/routes",
|
|
39
|
+
outDir: ".rainjs",
|
|
40
|
+
frameworkPackage: "@rainfw/core",
|
|
41
|
+
};
|
|
31
42
|
if (!fs.existsSync(configPath)) return defaults;
|
|
32
43
|
|
|
33
44
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
@@ -46,7 +57,11 @@ function loadBuildConfig() {
|
|
|
46
57
|
if (!ts.isObjectLiteralExpression(obj)) return;
|
|
47
58
|
Object.assign(
|
|
48
59
|
config,
|
|
49
|
-
extractStringProps(obj, sourceFile, [
|
|
60
|
+
extractStringProps(obj, sourceFile, [
|
|
61
|
+
"routesDir",
|
|
62
|
+
"outDir",
|
|
63
|
+
"frameworkPackage",
|
|
64
|
+
]),
|
|
50
65
|
);
|
|
51
66
|
});
|
|
52
67
|
|
|
@@ -158,6 +173,104 @@ function middlewarePathToDir(filePath) {
|
|
|
158
173
|
.replace(/\/$/, "");
|
|
159
174
|
}
|
|
160
175
|
|
|
176
|
+
function detectUseClientDirective(content) {
|
|
177
|
+
const sourceFile = ts.createSourceFile(
|
|
178
|
+
"file.tsx",
|
|
179
|
+
content,
|
|
180
|
+
ts.ScriptTarget.Latest,
|
|
181
|
+
true,
|
|
182
|
+
);
|
|
183
|
+
const firstStatement = sourceFile.statements[0];
|
|
184
|
+
if (!firstStatement) return false;
|
|
185
|
+
return (
|
|
186
|
+
ts.isExpressionStatement(firstStatement) &&
|
|
187
|
+
ts.isStringLiteral(firstStatement.expression) &&
|
|
188
|
+
firstStatement.expression.text === "use client"
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function getClientFiles(dir, base = "") {
|
|
193
|
+
if (!fs.existsSync(dir)) return [];
|
|
194
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
195
|
+
const files = [];
|
|
196
|
+
|
|
197
|
+
for (const entry of entries) {
|
|
198
|
+
const fullPath = path.join(dir, entry.name);
|
|
199
|
+
const relativePath = path.join(base, entry.name);
|
|
200
|
+
|
|
201
|
+
if (entry.isDirectory()) {
|
|
202
|
+
if (entry.name === "node_modules" || entry.name === ".rainjs") continue;
|
|
203
|
+
files.push(...getClientFiles(fullPath, relativePath));
|
|
204
|
+
} else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
|
|
205
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
206
|
+
if (detectUseClientDirective(content)) {
|
|
207
|
+
files.push(relativePath);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return files;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function bundleClientFilesSync(clientFiles, srcDir) {
|
|
216
|
+
if (clientFiles.length === 0) return [];
|
|
217
|
+
if (!esbuild) {
|
|
218
|
+
console.warn(
|
|
219
|
+
"[Rain] Warning: esbuild not found.\n" +
|
|
220
|
+
" → Install esbuild to enable client bundling: npm install -D esbuild\n" +
|
|
221
|
+
" → Client-side components will not be bundled.",
|
|
222
|
+
);
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const outDir = path.join(PROJECT_ROOT, "public", "_rain");
|
|
227
|
+
if (!fs.existsSync(outDir)) {
|
|
228
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (const file of fs.readdirSync(outDir)) {
|
|
232
|
+
if (file.startsWith("island-")) {
|
|
233
|
+
fs.unlinkSync(path.join(outDir, file));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const entryPoints = clientFiles.map((f) => path.join(srcDir, f));
|
|
238
|
+
|
|
239
|
+
const result = esbuild.buildSync({
|
|
240
|
+
entryPoints,
|
|
241
|
+
outdir: outDir,
|
|
242
|
+
bundle: true,
|
|
243
|
+
minify: true,
|
|
244
|
+
format: "esm",
|
|
245
|
+
metafile: true,
|
|
246
|
+
entryNames: "island-[hash]",
|
|
247
|
+
write: true,
|
|
248
|
+
treeShaking: true,
|
|
249
|
+
platform: "browser",
|
|
250
|
+
target: ["es2022"],
|
|
251
|
+
jsx: "automatic",
|
|
252
|
+
jsxImportSource: "@rainfw/core",
|
|
253
|
+
alias: {
|
|
254
|
+
"@rainfw/core/jsx-runtime": path.resolve(
|
|
255
|
+
PROJECT_ROOT,
|
|
256
|
+
"src/framework/client/jsx-runtime.ts",
|
|
257
|
+
),
|
|
258
|
+
},
|
|
259
|
+
loader: { ".ts": "ts", ".tsx": "tsx" },
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const publicDir = path.join(PROJECT_ROOT, "public");
|
|
263
|
+
const scripts = [];
|
|
264
|
+
for (const [outPath, meta] of Object.entries(result.metafile.outputs)) {
|
|
265
|
+
if (meta.entryPoint) {
|
|
266
|
+
const relPath = path.relative(publicDir, outPath);
|
|
267
|
+
scripts.push(`/${relPath.replace(/\\/g, "/")}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return scripts;
|
|
272
|
+
}
|
|
273
|
+
|
|
161
274
|
function routePathToDir(filePath) {
|
|
162
275
|
return filePath
|
|
163
276
|
.replace(/\\/g, "/")
|
|
@@ -533,6 +646,63 @@ function processPages(
|
|
|
533
646
|
}
|
|
534
647
|
}
|
|
535
648
|
|
|
649
|
+
function validateCompatibilityFlags() {
|
|
650
|
+
const wranglerPath = path.join(PROJECT_ROOT, "wrangler.toml");
|
|
651
|
+
if (!fs.existsSync(wranglerPath)) return;
|
|
652
|
+
|
|
653
|
+
const content = fs.readFileSync(wranglerPath, "utf-8");
|
|
654
|
+
const flagsMatch = content.match(/compatibility_flags\s*=\s*\[([^\]]*)\]/);
|
|
655
|
+
const flags = flagsMatch
|
|
656
|
+
? (flagsMatch[1].match(/"([^"]+)"/g) || []).map((s) => s.replace(/"/g, ""))
|
|
657
|
+
: [];
|
|
658
|
+
|
|
659
|
+
if (!(flags.includes("nodejs_compat") || flags.includes("nodejs_als"))) {
|
|
660
|
+
console.error(
|
|
661
|
+
"[Rain] Error: Missing required compatibility flag.\n" +
|
|
662
|
+
' → Rain.js requires "nodejs_compat" in your wrangler.toml.\n' +
|
|
663
|
+
" → Add the following to your wrangler.toml:\n\n" +
|
|
664
|
+
' compatibility_flags = ["nodejs_compat"]\n\n' +
|
|
665
|
+
" → Without this flag, the Workers runtime will fail with:\n" +
|
|
666
|
+
' "No such module node:async_hooks"\n' +
|
|
667
|
+
" → See: https://developers.cloudflare.com/workers/" +
|
|
668
|
+
"configuration/compatibility-flags/" +
|
|
669
|
+
"#nodejs-compatibility-flag",
|
|
670
|
+
);
|
|
671
|
+
process.exit(1);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function buildAppInitLine(clientScripts, hasConfig) {
|
|
676
|
+
if (hasConfig) {
|
|
677
|
+
return clientScripts.length > 0
|
|
678
|
+
? `const app = new Rain({ ...config, clientScripts: ${JSON.stringify(clientScripts)} });`
|
|
679
|
+
: "const app = new Rain(config);";
|
|
680
|
+
}
|
|
681
|
+
return clientScripts.length > 0
|
|
682
|
+
? `const app = new Rain({ clientScripts: ${JSON.stringify(clientScripts)} });`
|
|
683
|
+
: "const app = new Rain();";
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function regenerateClient() {
|
|
687
|
+
const srcDir = path.join(PROJECT_ROOT, "src");
|
|
688
|
+
const clientFiles = getClientFiles(srcDir);
|
|
689
|
+
const clientScripts = bundleClientFilesSync(clientFiles, srcDir);
|
|
690
|
+
|
|
691
|
+
if (!fs.existsSync(ENTRY_FILE)) return;
|
|
692
|
+
|
|
693
|
+
const content = fs.readFileSync(ENTRY_FILE, "utf-8");
|
|
694
|
+
const hasConfig = fs.existsSync(CONFIG_FILE);
|
|
695
|
+
const appInit = buildAppInitLine(clientScripts, hasConfig);
|
|
696
|
+
const updated = content.replace(/^const app = new Rain\(.*\);$/m, appInit);
|
|
697
|
+
if (updated !== content) {
|
|
698
|
+
fs.writeFileSync(ENTRY_FILE, updated);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const clientMsg =
|
|
702
|
+
clientFiles.length > 0 ? `${clientFiles.length} client` : "0 client";
|
|
703
|
+
console.log(`[gen:client] ${clientMsg} -> .rainjs/entry.ts`);
|
|
704
|
+
}
|
|
705
|
+
|
|
536
706
|
function generate() {
|
|
537
707
|
if (!fs.existsSync(ROUTES_DIR)) {
|
|
538
708
|
console.error(
|
|
@@ -544,6 +714,8 @@ function generate() {
|
|
|
544
714
|
process.exit(1);
|
|
545
715
|
}
|
|
546
716
|
|
|
717
|
+
validateCompatibilityFlags();
|
|
718
|
+
|
|
547
719
|
try {
|
|
548
720
|
execSync("npx wrangler types", {
|
|
549
721
|
cwd: PROJECT_ROOT,
|
|
@@ -592,12 +764,18 @@ function generate() {
|
|
|
592
764
|
registrations,
|
|
593
765
|
);
|
|
594
766
|
|
|
767
|
+
const srcDir = path.join(PROJECT_ROOT, "src");
|
|
768
|
+
const clientFiles = getClientFiles(srcDir);
|
|
769
|
+
const clientScripts = bundleClientFilesSync(clientFiles, srcDir);
|
|
770
|
+
|
|
595
771
|
const hasConfig = fs.existsSync(CONFIG_FILE);
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
772
|
+
const fwPkg = BUILD_CONFIG.frameworkPackage;
|
|
773
|
+
const frameworkImport =
|
|
774
|
+
fwPkg.startsWith(".") || fwPkg.startsWith("/")
|
|
775
|
+
? relativeImportPath(path.join(PROJECT_ROOT, fwPkg))
|
|
776
|
+
: fwPkg;
|
|
599
777
|
|
|
600
|
-
const headerImports = [`import { Rain } from "${
|
|
778
|
+
const headerImports = [`import { Rain } from "${frameworkImport}";`];
|
|
601
779
|
if (hasConfig) {
|
|
602
780
|
const configPath = relativeImportPath(
|
|
603
781
|
path.join(PROJECT_ROOT, "rain.config"),
|
|
@@ -605,9 +783,7 @@ function generate() {
|
|
|
605
783
|
headerImports.push(`import config from "${configPath}";`);
|
|
606
784
|
}
|
|
607
785
|
|
|
608
|
-
const appInit = hasConfig
|
|
609
|
-
? "const app = new Rain(config);"
|
|
610
|
-
: "const app = new Rain();";
|
|
786
|
+
const appInit = buildAppInitLine(clientScripts, hasConfig);
|
|
611
787
|
|
|
612
788
|
const content = [
|
|
613
789
|
...headerImports,
|
|
@@ -623,18 +799,22 @@ function generate() {
|
|
|
623
799
|
|
|
624
800
|
fs.writeFileSync(ENTRY_FILE, content);
|
|
625
801
|
const total = files.length + pageFiles.length;
|
|
802
|
+
const clientMsg =
|
|
803
|
+
clientFiles.length > 0 ? `, ${clientFiles.length} client` : "";
|
|
626
804
|
console.log(
|
|
627
|
-
`[gen] ${total} route(s) (${files.length} api, ${pageFiles.length} page, ${layoutFiles.length} layout) -> .rainjs/entry.ts`,
|
|
805
|
+
`[gen] ${total} route(s) (${files.length} api, ${pageFiles.length} page, ${layoutFiles.length} layout${clientMsg}) -> .rainjs/entry.ts`,
|
|
628
806
|
);
|
|
629
807
|
}
|
|
630
808
|
|
|
631
809
|
module.exports = {
|
|
632
810
|
generate,
|
|
811
|
+
regenerateClient,
|
|
633
812
|
loadBuildConfig,
|
|
634
813
|
getRouteFiles,
|
|
635
814
|
getMiddlewareFiles,
|
|
636
815
|
getPageFiles,
|
|
637
816
|
getLayoutFiles,
|
|
817
|
+
getClientFiles,
|
|
638
818
|
getMiddlewaresForRoute,
|
|
639
819
|
getLayoutsForPage,
|
|
640
820
|
filePathToUrlPath,
|
|
@@ -649,6 +829,8 @@ module.exports = {
|
|
|
649
829
|
detectMiddlewareExportFromContent,
|
|
650
830
|
detectDefaultExport,
|
|
651
831
|
detectDefaultExportFromContent,
|
|
832
|
+
detectUseClientDirective,
|
|
833
|
+
bundleClientFilesSync,
|
|
652
834
|
validateNoPageRouteColocation,
|
|
653
835
|
ROUTES_DIR,
|
|
654
836
|
ENTRY_FILE,
|