@webtypen/webframez-react 0.0.2 → 0.0.4
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 +238 -110
- package/bin/webframez-react.mjs +179 -7
- package/defaults/tsconfig.server.example.json +46 -0
- package/defaults/tsconfig.server.json +13 -0
- package/defaults/webpack.client.cjs +4 -1
- package/defaults/webpack.server.cjs +8 -1
- package/dist/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/http.cjs +55 -19
- package/dist/http.d.ts +18 -7
- package/dist/http.js +55 -19
- package/dist/index.cjs +55 -19
- package/dist/index.d.ts +2 -5
- package/dist/index.js +55 -19
- package/dist/router.cjs +30 -10
- package/dist/router.js +30 -10
- package/dist/types.d.ts +25 -8
- package/dist/webframez-core.cjs +55 -19
- package/dist/webframez-core.d.ts +32 -8
- package/dist/webframez-core.js +55 -19
- package/package.json +5 -2
- package/register.cjs +1 -0
package/bin/webframez-react.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
+
import fsp from "node:fs/promises";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { spawn } from "node:child_process";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
8
|
|
|
8
9
|
const binFilePath = fileURLToPath(import.meta.url);
|
|
9
10
|
const packageRoot = path.resolve(path.dirname(binFilePath), "..");
|
|
@@ -12,6 +13,7 @@ const projectRoot = process.cwd();
|
|
|
12
13
|
const command = process.argv[2];
|
|
13
14
|
const passthroughStart = process.argv[3] === "--" ? 4 : 3;
|
|
14
15
|
const passthroughArgs = process.argv.slice(passthroughStart);
|
|
16
|
+
const customArgPrefixes = ["--client-entry", "--server-entry"];
|
|
15
17
|
|
|
16
18
|
function printHelp() {
|
|
17
19
|
console.log(
|
|
@@ -25,6 +27,7 @@ function printHelp() {
|
|
|
25
27
|
" webframez-react watch:client",
|
|
26
28
|
" webframez-react build:server:webpack",
|
|
27
29
|
" webframez-react watch:server:webpack",
|
|
30
|
+
" webframez-react exec -- <command> [args...]",
|
|
28
31
|
"",
|
|
29
32
|
"Config fallback order:",
|
|
30
33
|
" 1) project root override file",
|
|
@@ -34,6 +37,13 @@ function printHelp() {
|
|
|
34
37
|
" - tsconfig.server.json",
|
|
35
38
|
" - webpack.client.cjs",
|
|
36
39
|
" - webpack.server.cjs",
|
|
40
|
+
"",
|
|
41
|
+
"Optional project config file:",
|
|
42
|
+
" - webframez-react.config.mjs|cjs|js|json",
|
|
43
|
+
"",
|
|
44
|
+
"Optional CLI overrides:",
|
|
45
|
+
" --client-entry=src/client.tsx",
|
|
46
|
+
" --server-entry=src/server.ts",
|
|
37
47
|
].join("\n"),
|
|
38
48
|
);
|
|
39
49
|
}
|
|
@@ -42,6 +52,43 @@ function hasFlag(flag) {
|
|
|
42
52
|
return passthroughArgs.includes(flag);
|
|
43
53
|
}
|
|
44
54
|
|
|
55
|
+
function normalizeRelativePath(value) {
|
|
56
|
+
return value.replace(/\\/g, "/");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readCustomArg(name) {
|
|
60
|
+
const withEquals = `${name}=`;
|
|
61
|
+
for (let index = 0; index < passthroughArgs.length; index += 1) {
|
|
62
|
+
const value = passthroughArgs[index];
|
|
63
|
+
if (value === name) {
|
|
64
|
+
return passthroughArgs[index + 1] || null;
|
|
65
|
+
}
|
|
66
|
+
if (value.startsWith(withEquals)) {
|
|
67
|
+
return value.slice(withEquals.length);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function stripCustomArgs(args) {
|
|
74
|
+
const filtered = [];
|
|
75
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
76
|
+
const value = args[index];
|
|
77
|
+
const matchedPrefix = customArgPrefixes.find(
|
|
78
|
+
(prefix) => value === prefix || value.startsWith(`${prefix}=`),
|
|
79
|
+
);
|
|
80
|
+
if (!matchedPrefix) {
|
|
81
|
+
filtered.push(value);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (value === matchedPrefix) {
|
|
86
|
+
index += 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return filtered;
|
|
90
|
+
}
|
|
91
|
+
|
|
45
92
|
function resolveConfig(localFileName, fallbackFileName) {
|
|
46
93
|
const localPath = path.resolve(projectRoot, localFileName);
|
|
47
94
|
if (fs.existsSync(localPath)) {
|
|
@@ -69,7 +116,83 @@ function resolveBinary(name) {
|
|
|
69
116
|
return name;
|
|
70
117
|
}
|
|
71
118
|
|
|
72
|
-
function
|
|
119
|
+
function buildReactServerNodeOptions() {
|
|
120
|
+
const existing = process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ` : "";
|
|
121
|
+
return `${existing}--conditions react-server -r @webtypen/webframez-react/register`.trim();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function loadProjectConfig() {
|
|
125
|
+
const configFiles = [
|
|
126
|
+
"webframez-react.config.mjs",
|
|
127
|
+
"webframez-react.config.cjs",
|
|
128
|
+
"webframez-react.config.js",
|
|
129
|
+
"webframez-react.config.json",
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
for (const fileName of configFiles) {
|
|
133
|
+
const filePath = path.resolve(projectRoot, fileName);
|
|
134
|
+
if (!fs.existsSync(filePath)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (fileName.endsWith(".json")) {
|
|
139
|
+
return JSON.parse(await fsp.readFile(filePath, "utf8"));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const imported = await import(pathToFileURL(filePath).href);
|
|
143
|
+
return imported.default ?? imported;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function resolveEntryPath(projectConfig, customArgValue, configKey, defaults) {
|
|
150
|
+
const configuredValue = customArgValue || projectConfig?.[configKey];
|
|
151
|
+
if (configuredValue && typeof configuredValue === "string") {
|
|
152
|
+
return path.resolve(projectRoot, configuredValue);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
for (const defaultPath of defaults) {
|
|
156
|
+
const resolved = path.resolve(projectRoot, defaultPath);
|
|
157
|
+
if (fs.existsSync(resolved)) {
|
|
158
|
+
return resolved;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return path.resolve(projectRoot, defaults[0]);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function createServerTsConfig(baseConfig, serverEntryPath, clientEntryPath) {
|
|
166
|
+
const generatedPath = path.resolve(projectRoot, ".webframez-react.tsconfig.server.json");
|
|
167
|
+
const extendsPath =
|
|
168
|
+
baseConfig.source === "project"
|
|
169
|
+
? `./${normalizeRelativePath(path.basename(baseConfig.path))}`
|
|
170
|
+
: normalizeRelativePath(path.relative(projectRoot, baseConfig.path));
|
|
171
|
+
|
|
172
|
+
const include = [
|
|
173
|
+
normalizeRelativePath(path.relative(projectRoot, serverEntryPath)),
|
|
174
|
+
"src/components/**/*.tsx",
|
|
175
|
+
"pages/**/*.tsx",
|
|
176
|
+
"src/types.d.ts",
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
const exclude = [
|
|
180
|
+
normalizeRelativePath(path.relative(projectRoot, clientEntryPath)),
|
|
181
|
+
"dist",
|
|
182
|
+
"node_modules",
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
const generatedConfig = {
|
|
186
|
+
extends: extendsPath,
|
|
187
|
+
include: Array.from(new Set(include)),
|
|
188
|
+
exclude: Array.from(new Set(exclude)),
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
await fsp.writeFile(generatedPath, JSON.stringify(generatedConfig, null, 2));
|
|
192
|
+
return generatedPath;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function run(binaryName, args, envAdditions = {}) {
|
|
73
196
|
const binary = resolveBinary(binaryName);
|
|
74
197
|
|
|
75
198
|
return new Promise((resolve, reject) => {
|
|
@@ -77,6 +200,10 @@ function run(binaryName, args) {
|
|
|
77
200
|
cwd: projectRoot,
|
|
78
201
|
stdio: "inherit",
|
|
79
202
|
shell: false,
|
|
203
|
+
env: {
|
|
204
|
+
...process.env,
|
|
205
|
+
...envAdditions,
|
|
206
|
+
},
|
|
80
207
|
});
|
|
81
208
|
|
|
82
209
|
child.on("error", (error) => {
|
|
@@ -95,11 +222,35 @@ async function main() {
|
|
|
95
222
|
return;
|
|
96
223
|
}
|
|
97
224
|
|
|
225
|
+
const projectConfig = await loadProjectConfig();
|
|
226
|
+
const customClientEntry = readCustomArg("--client-entry");
|
|
227
|
+
const customServerEntry = readCustomArg("--server-entry");
|
|
228
|
+
const passthroughArgsClean = stripCustomArgs(passthroughArgs);
|
|
229
|
+
|
|
230
|
+
const clientEntryPath = resolveEntryPath(
|
|
231
|
+
projectConfig,
|
|
232
|
+
customClientEntry,
|
|
233
|
+
"clientEntryPath",
|
|
234
|
+
["src/client.tsx"],
|
|
235
|
+
);
|
|
236
|
+
const serverEntryPath = resolveEntryPath(
|
|
237
|
+
projectConfig,
|
|
238
|
+
customServerEntry,
|
|
239
|
+
"serverEntryPath",
|
|
240
|
+
["src/server.ts", "src/server.tsx"],
|
|
241
|
+
);
|
|
242
|
+
|
|
98
243
|
if (command === "build:server" || command === "watch:server") {
|
|
99
244
|
const config = resolveConfig("tsconfig.server.json", "tsconfig.server.json");
|
|
245
|
+
const generatedConfigPath = await createServerTsConfig(
|
|
246
|
+
config,
|
|
247
|
+
serverEntryPath,
|
|
248
|
+
clientEntryPath,
|
|
249
|
+
);
|
|
100
250
|
console.log(`[webframez-react] tsc config (${config.source}): ${config.path}`);
|
|
251
|
+
console.log(`[webframez-react] server entry: ${serverEntryPath}`);
|
|
101
252
|
|
|
102
|
-
const args = ["-p",
|
|
253
|
+
const args = ["-p", generatedConfigPath, ...passthroughArgsClean];
|
|
103
254
|
if (command === "watch:server") {
|
|
104
255
|
if (!hasFlag("--watch")) {
|
|
105
256
|
args.push("--watch");
|
|
@@ -114,16 +265,34 @@ async function main() {
|
|
|
114
265
|
return;
|
|
115
266
|
}
|
|
116
267
|
|
|
268
|
+
if (command === "exec") {
|
|
269
|
+
if (passthroughArgsClean.length === 0) {
|
|
270
|
+
console.error("[webframez-react] Missing command for exec.");
|
|
271
|
+
printHelp();
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const [binaryName, ...binaryArgs] = passthroughArgsClean;
|
|
276
|
+
const code = await run(binaryName, binaryArgs, {
|
|
277
|
+
NODE_OPTIONS: buildReactServerNodeOptions(),
|
|
278
|
+
});
|
|
279
|
+
process.exit(code);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
117
283
|
if (command === "build:client" || command === "watch:client") {
|
|
118
284
|
const config = resolveConfig("webpack.client.cjs", "webpack.client.cjs");
|
|
119
285
|
console.log(`[webframez-react] webpack config (${config.source}): ${config.path}`);
|
|
286
|
+
console.log(`[webframez-react] client entry: ${clientEntryPath}`);
|
|
120
287
|
|
|
121
|
-
const args = ["--config", config.path, ...
|
|
288
|
+
const args = ["--config", config.path, ...passthroughArgsClean];
|
|
122
289
|
if (command === "watch:client" && !hasFlag("--watch")) {
|
|
123
290
|
args.push("--watch");
|
|
124
291
|
}
|
|
125
292
|
|
|
126
|
-
const code = await run("webpack", args
|
|
293
|
+
const code = await run("webpack", args, {
|
|
294
|
+
WEBFRAMEZ_REACT_CLIENT_ENTRY: clientEntryPath,
|
|
295
|
+
});
|
|
127
296
|
process.exit(code);
|
|
128
297
|
return;
|
|
129
298
|
}
|
|
@@ -131,13 +300,16 @@ async function main() {
|
|
|
131
300
|
if (command === "build:server:webpack" || command === "watch:server:webpack") {
|
|
132
301
|
const config = resolveConfig("webpack.server.cjs", "webpack.server.cjs");
|
|
133
302
|
console.log(`[webframez-react] webpack server config (${config.source}): ${config.path}`);
|
|
303
|
+
console.log(`[webframez-react] server entry: ${serverEntryPath}`);
|
|
134
304
|
|
|
135
|
-
const args = ["--config", config.path, ...
|
|
305
|
+
const args = ["--config", config.path, ...passthroughArgsClean];
|
|
136
306
|
if (command === "watch:server:webpack" && !hasFlag("--watch")) {
|
|
137
307
|
args.push("--watch");
|
|
138
308
|
}
|
|
139
309
|
|
|
140
|
-
const code = await run("webpack", args
|
|
310
|
+
const code = await run("webpack", args, {
|
|
311
|
+
WEBFRAMEZ_REACT_SERVER_ENTRY: serverEntryPath,
|
|
312
|
+
});
|
|
141
313
|
process.exit(code);
|
|
142
314
|
return;
|
|
143
315
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"moduleResolution": "Node",
|
|
6
|
+
"baseUrl": ".",
|
|
7
|
+
"paths": {
|
|
8
|
+
"@webtypen/webframez-react": [
|
|
9
|
+
"./node_modules/@webtypen/webframez-react/dist/index.d.ts"
|
|
10
|
+
],
|
|
11
|
+
"@webtypen/webframez-react/types": [
|
|
12
|
+
"./node_modules/@webtypen/webframez-react/dist/types.d.ts"
|
|
13
|
+
],
|
|
14
|
+
"@webtypen/webframez-react/router": [
|
|
15
|
+
"./node_modules/@webtypen/webframez-react/dist/router.d.ts"
|
|
16
|
+
],
|
|
17
|
+
"@webtypen/webframez-react/client": [
|
|
18
|
+
"./node_modules/@webtypen/webframez-react/dist/client.d.ts"
|
|
19
|
+
],
|
|
20
|
+
"@webtypen/webframez-react/navigation": [
|
|
21
|
+
"./node_modules/@webtypen/webframez-react/dist/navigation.d.ts"
|
|
22
|
+
],
|
|
23
|
+
"@webtypen/webframez-react/webframez-core": [
|
|
24
|
+
"./node_modules/@webtypen/webframez-react/dist/webframez-core.d.ts"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"jsx": "react-jsx",
|
|
28
|
+
"strict": true,
|
|
29
|
+
"esModuleInterop": true,
|
|
30
|
+
"skipLibCheck": true,
|
|
31
|
+
"outDir": "dist",
|
|
32
|
+
"rootDir": "."
|
|
33
|
+
},
|
|
34
|
+
"include": [
|
|
35
|
+
"src/server.ts",
|
|
36
|
+
"src/server.tsx",
|
|
37
|
+
"src/components/**/*.tsx",
|
|
38
|
+
"pages/**/*.tsx",
|
|
39
|
+
"src/types.d.ts"
|
|
40
|
+
],
|
|
41
|
+
"exclude": [
|
|
42
|
+
"src/client.tsx",
|
|
43
|
+
"dist",
|
|
44
|
+
"node_modules"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
"moduleResolution": "Node",
|
|
6
6
|
"baseUrl": ".",
|
|
7
7
|
"paths": {
|
|
8
|
+
"@webtypen/webframez-react": [
|
|
9
|
+
"./node_modules/@webtypen/webframez-react/dist/index.d.ts"
|
|
10
|
+
],
|
|
8
11
|
"@webtypen/webframez-react/types": [
|
|
9
12
|
"./node_modules/@webtypen/webframez-react/dist/types.d.ts"
|
|
10
13
|
],
|
|
@@ -17,6 +20,12 @@
|
|
|
17
20
|
"@webtypen/webframez-react/navigation": [
|
|
18
21
|
"./node_modules/@webtypen/webframez-react/dist/navigation.d.ts"
|
|
19
22
|
],
|
|
23
|
+
"@webtypen/webframez-react/webframez-core": [
|
|
24
|
+
"./node_modules/@webtypen/webframez-react/dist/webframez-core.d.ts"
|
|
25
|
+
],
|
|
26
|
+
"webframez-react": [
|
|
27
|
+
"./node_modules/@webtypen/webframez-react/dist/index.d.ts"
|
|
28
|
+
],
|
|
20
29
|
"webframez-react/types": [
|
|
21
30
|
"./node_modules/@webtypen/webframez-react/dist/types.d.ts"
|
|
22
31
|
],
|
|
@@ -28,6 +37,9 @@
|
|
|
28
37
|
],
|
|
29
38
|
"webframez-react/navigation": [
|
|
30
39
|
"./node_modules/@webtypen/webframez-react/dist/navigation.d.ts"
|
|
40
|
+
],
|
|
41
|
+
"webframez-react/webframez-core": [
|
|
42
|
+
"./node_modules/@webtypen/webframez-react/dist/webframez-core.d.ts"
|
|
31
43
|
]
|
|
32
44
|
},
|
|
33
45
|
"jsx": "react-jsx",
|
|
@@ -39,6 +51,7 @@
|
|
|
39
51
|
},
|
|
40
52
|
"include": [
|
|
41
53
|
"src/server.ts",
|
|
54
|
+
"src/server.tsx",
|
|
42
55
|
"src/components/**/*.tsx",
|
|
43
56
|
"pages/**/*.tsx",
|
|
44
57
|
"src/types.d.ts"
|
|
@@ -3,11 +3,14 @@ const ReactFlightWebpackPlugin = require("react-server-dom-webpack/plugin");
|
|
|
3
3
|
|
|
4
4
|
const projectRoot = process.cwd();
|
|
5
5
|
const frameworkDistDir = path.resolve(__dirname, "..", "dist");
|
|
6
|
+
const clientEntry = process.env.WEBFRAMEZ_REACT_CLIENT_ENTRY
|
|
7
|
+
? path.resolve(projectRoot, process.env.WEBFRAMEZ_REACT_CLIENT_ENTRY)
|
|
8
|
+
: path.resolve(projectRoot, "src/client.tsx");
|
|
6
9
|
|
|
7
10
|
module.exports = {
|
|
8
11
|
mode: process.env.NODE_ENV === "production" ? "production" : "development",
|
|
9
12
|
entry: {
|
|
10
|
-
client:
|
|
13
|
+
client: clientEntry,
|
|
11
14
|
},
|
|
12
15
|
output: {
|
|
13
16
|
path: path.resolve(projectRoot, "dist"),
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
|
+
const fs = require("fs");
|
|
2
3
|
|
|
3
4
|
const projectRoot = process.cwd();
|
|
5
|
+
const defaultServerEntry = fs.existsSync(path.resolve(projectRoot, "src/server.ts"))
|
|
6
|
+
? path.resolve(projectRoot, "src/server.ts")
|
|
7
|
+
: path.resolve(projectRoot, "src/server.tsx");
|
|
8
|
+
const serverEntry = process.env.WEBFRAMEZ_REACT_SERVER_ENTRY
|
|
9
|
+
? path.resolve(projectRoot, process.env.WEBFRAMEZ_REACT_SERVER_ENTRY)
|
|
10
|
+
: defaultServerEntry;
|
|
4
11
|
|
|
5
12
|
module.exports = {
|
|
6
13
|
mode: process.env.NODE_ENV === "production" ? "production" : "development",
|
|
7
14
|
target: "node",
|
|
8
|
-
entry:
|
|
15
|
+
entry: serverEntry,
|
|
9
16
|
output: {
|
|
10
17
|
path: path.resolve(projectRoot, "dist"),
|
|
11
18
|
filename: "server.cjs",
|
package/dist/client.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var k=Object.create;var d=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,L=Object.prototype.hasOwnProperty;var T=(e,t)=>{for(var n in t)d(e,n,{get:t[n],enumerable:!0})},R=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of O(t))!L.call(e,r)&&r!==n&&d(e,r,{get:()=>t[r],enumerable:!(o=A(t,r))||o.enumerable});return e};var U=(e,t,n)=>(n=e!=null?k(S(e)):{},R(t||!e||!e.__esModule?d(n,"default",{value:e,enumerable:!0}):n,e)),M=e=>R(d({},"__esModule",{value:!0}),e);var D={};T(D,{mountWebframezClient:()=>F,useCookie:()=>B,useRouter:()=>H});module.exports=M(D);var s=U(require("react"),1),E=require("react-dom/client"),_=require("react-server-dom-webpack/client"),i=require("react/jsx-runtime"),$={push:()=>{},replace:()=>{},refresh:()=>{}},l=typeof s.default.createContext=="function"?s.default.createContext(null):null,b="__WEBFRAMEZ_ROUTER__",I="[data-webframez-head='true']";function P(){return typeof window>"u"?null:window[b]??null}function N(e){typeof window>"u"||(window[b]=e)}function h(){let e={},t=typeof document>"u"?"":document.cookie;if(!t||t.trim()==="")return e;for(let n of t.split(";")){let o=n.trim();if(!o)continue;let r=o.indexOf("="),u=r>=0?o.slice(0,r).trim():o,c=r>=0?o.slice(r+1):"";u&&(e[u]=decodeURIComponent(c))}return e}function C(e,t,n={}){let o=[`${e}=${encodeURIComponent(t)}`];return n.path&&o.push(`Path=${n.path}`),n.domain&&o.push(`Domain=${n.domain}`),typeof n.maxAge=="number"&&o.push(`Max-Age=${Math.floor(n.maxAge)}`),n.expires&&o.push(`Expires=${n.expires.toUTCString()}`),n.sameSite&&o.push(`SameSite=${n.sameSite}`),n.secure&&o.push("Secure"),o.join("; ")}function B(){return s.default.useMemo(()=>({all:()=>h(),get:e=>h()[e],set:(e,t,n)=>{typeof document>"u"||(document.cookie=C(e,t,n))},remove:(e,t)=>{typeof document>"u"||(document.cookie=C(e,"",{...t??{},maxAge:0}))}}),[])}function H(){let e=l?s.default.useContext(l):null;if(!e){let t=P();if(t)return t;if(typeof window>"u")return $;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function v({active:e}){return(0,i.jsx)("div",{style:{position:"fixed",top:0,left:0,width:"100%",height:3,zIndex:9999,transformOrigin:"left",transform:e?"scaleX(1)":"scaleX(0)",transition:"transform 180ms ease",background:"linear-gradient(90deg, #0ea5e9, #22c55e)"}})}function y(e){let t=document.createElement("meta");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,r]of n)t.setAttribute(o,String(r));document.head.appendChild(t)}function x(e){let t=document.createElement("link");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,r]of n)t.setAttribute(o,String(r));document.head.appendChild(t)}function W(e){if(!(typeof document>"u")){document.title=e.title||"Webframez React";for(let t of document.head.querySelectorAll(I))t.remove();e.description&&y({name:"description",content:e.description}),e.favicon&&x({rel:"icon",href:e.favicon});for(let t of e.meta??[])y(t);for(let t of e.links??[])x(t)}}function z(e){return function(){let[n,o]=(0,s.useState)(null),[r,u]=(0,s.useState)(!1);async function c(a,p="push"){u(!0);try{let g=await(0,_.createFromFetch)(fetch(`${e}?path=${encodeURIComponent(a.pathname)}&search=${encodeURIComponent(a.search)}`,{headers:{Accept:"text/x-component"}}));W(g.head),o(g.model);let w=`${a.pathname}${a.search}`;p==="replace"?history.replaceState(null,"",w):p==="push"&&history.pushState(null,"",w)}catch(m){console.error("[webframez-react] Failed to render route",m),o((0,i.jsx)("p",{children:"Failed to load route."}))}finally{u(!1)}}(0,s.useEffect)(()=>{let a=()=>{c(new URL(window.location.href),"none")};return window.addEventListener("popstate",a),c(new URL(window.location.href),"none"),()=>{window.removeEventListener("popstate",a)}},[]);let f=s.default.useMemo(()=>({push:a=>{c(new URL(a,window.location.origin),"push")},replace:a=>{c(new URL(a,window.location.origin),"replace")},refresh:()=>{c(new URL(window.location.href),"none")}}),[]);return N(f),l?(0,i.jsxs)(l.Provider,{value:f,children:[(0,i.jsx)(v,{active:r}),n??(0,i.jsx)("p",{style:{padding:24},children:"Loading..."})]}):(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(v,{active:r}),n??(0,i.jsx)("p",{style:{padding:24},children:"Loading..."})]})}}function F(e={}){let t=e.rootId??"root",n=document.getElementById(t);if(!n)throw new Error(`Missing #${t} element`);let o=typeof window<"u"&&window.__RSC_ENDPOINT,r=e.rscEndpoint??o??"/rsc",u=z(r),c=(0,E.createRoot)(n);return c.render((0,i.jsx)(u,{})),c}
|
package/dist/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import u,{useEffect as _,useState as w}from"react";import{createRoot as b}from"react-dom/client";import{createFromFetch as k}from"react-server-dom-webpack/client";import{Fragment as M,jsx as c,jsxs as x}from"react/jsx-runtime";var A={push:()=>{},replace:()=>{},refresh:()=>{}},d=typeof u.createContext=="function"?u.createContext(null):null,E="__WEBFRAMEZ_ROUTER__",O="[data-webframez-head='true']";function S(){return typeof window>"u"?null:window[E]??null}function L(e){typeof window>"u"||(window[E]=e)}function R(){let e={},t=typeof document>"u"?"":document.cookie;if(!t||t.trim()==="")return e;for(let o of t.split(";")){let n=o.trim();if(!n)continue;let r=n.indexOf("="),s=r>=0?n.slice(0,r).trim():n,a=r>=0?n.slice(r+1):"";s&&(e[s]=decodeURIComponent(a))}return e}function h(e,t,o={}){let n=[`${e}=${encodeURIComponent(t)}`];return o.path&&n.push(`Path=${o.path}`),o.domain&&n.push(`Domain=${o.domain}`),typeof o.maxAge=="number"&&n.push(`Max-Age=${Math.floor(o.maxAge)}`),o.expires&&n.push(`Expires=${o.expires.toUTCString()}`),o.sameSite&&n.push(`SameSite=${o.sameSite}`),o.secure&&n.push("Secure"),n.join("; ")}function N(){return u.useMemo(()=>({all:()=>R(),get:e=>R()[e],set:(e,t,o)=>{typeof document>"u"||(document.cookie=h(e,t,o))},remove:(e,t)=>{typeof document>"u"||(document.cookie=h(e,"",{...t??{},maxAge:0}))}}),[])}function B(){let e=d?u.useContext(d):null;if(!e){let t=S();if(t)return t;if(typeof window>"u")return A;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function C({active:e}){return c("div",{style:{position:"fixed",top:0,left:0,width:"100%",height:3,zIndex:9999,transformOrigin:"left",transform:e?"scaleX(1)":"scaleX(0)",transition:"transform 180ms ease",background:"linear-gradient(90deg, #0ea5e9, #22c55e)"}})}function v(e){let t=document.createElement("meta");t.setAttribute("data-webframez-head","true");let o=Object.entries(e).filter(([,n])=>!!n);for(let[n,r]of o)t.setAttribute(n,String(r));document.head.appendChild(t)}function y(e){let t=document.createElement("link");t.setAttribute("data-webframez-head","true");let o=Object.entries(e).filter(([,n])=>!!n);for(let[n,r]of o)t.setAttribute(n,String(r));document.head.appendChild(t)}function T(e){if(!(typeof document>"u")){document.title=e.title||"Webframez React";for(let t of document.head.querySelectorAll(O))t.remove();e.description&&v({name:"description",content:e.description}),e.favicon&&y({rel:"icon",href:e.favicon});for(let t of e.meta??[])v(t);for(let t of e.links??[])y(t)}}function U(e){return function(){let[o,n]=w(null),[r,s]=w(!1);async function a(i,f="push"){s(!0);try{let m=await k(fetch(`${e}?path=${encodeURIComponent(i.pathname)}&search=${encodeURIComponent(i.search)}`,{headers:{Accept:"text/x-component"}}));T(m.head),n(m.model);let g=`${i.pathname}${i.search}`;f==="replace"?history.replaceState(null,"",g):f==="push"&&history.pushState(null,"",g)}catch(p){console.error("[webframez-react] Failed to render route",p),n(c("p",{children:"Failed to load route."}))}finally{s(!1)}}_(()=>{let i=()=>{a(new URL(window.location.href),"none")};return window.addEventListener("popstate",i),a(new URL(window.location.href),"none"),()=>{window.removeEventListener("popstate",i)}},[]);let l=u.useMemo(()=>({push:i=>{a(new URL(i,window.location.origin),"push")},replace:i=>{a(new URL(i,window.location.origin),"replace")},refresh:()=>{a(new URL(window.location.href),"none")}}),[]);return L(l),d?x(d.Provider,{value:l,children:[c(C,{active:r}),o??c("p",{style:{padding:24},children:"Loading..."})]}):x(M,{children:[c(C,{active:r}),o??c("p",{style:{padding:24},children:"Loading..."})]})}}function H(e={}){let t=e.rootId??"root",o=document.getElementById(t);if(!o)throw new Error(`Missing #${t} element`);let n=typeof window<"u"&&window.__RSC_ENDPOINT,r=e.rscEndpoint??n??"/rsc",s=U(r),a=b(o);return a.render(c(s,{})),a}export{H as mountWebframezClient,N as useCookie,B as useRouter};
|
package/dist/http.cjs
CHANGED
|
@@ -223,6 +223,10 @@ function readSearchParams(urlSearchParams) {
|
|
|
223
223
|
function escapeHtml(value) {
|
|
224
224
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\"/g, """).replace(/'/g, "'");
|
|
225
225
|
}
|
|
226
|
+
var MANAGED_HEAD_ATTR = "data-webframez-head";
|
|
227
|
+
function createManagedAttributes() {
|
|
228
|
+
return `${MANAGED_HEAD_ATTR}="true"`;
|
|
229
|
+
}
|
|
226
230
|
function toRouteEntry(pagesDir, filePath) {
|
|
227
231
|
const normalized = filePath.replace(/\\/g, "/");
|
|
228
232
|
if (!normalized.endsWith("/index.js")) {
|
|
@@ -291,19 +295,21 @@ function renderHeadToString(head) {
|
|
|
291
295
|
const tags = [];
|
|
292
296
|
if (head.description) {
|
|
293
297
|
tags.push(
|
|
294
|
-
`<meta name="description" content="${escapeHtml(head.description)}" />`
|
|
298
|
+
`<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
|
|
295
299
|
);
|
|
296
300
|
}
|
|
297
301
|
if (head.favicon) {
|
|
298
|
-
tags.push(
|
|
302
|
+
tags.push(
|
|
303
|
+
`<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
|
|
304
|
+
);
|
|
299
305
|
}
|
|
300
306
|
for (const meta of head.meta ?? []) {
|
|
301
307
|
const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
302
|
-
tags.push(`<meta ${attrs} />`);
|
|
308
|
+
tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
|
|
303
309
|
}
|
|
304
310
|
for (const link of head.links ?? []) {
|
|
305
311
|
const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
306
|
-
tags.push(`<link ${attrs} />`);
|
|
312
|
+
tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
|
|
307
313
|
}
|
|
308
314
|
return tags.join("\n");
|
|
309
315
|
}
|
|
@@ -317,6 +323,12 @@ async function resolveHead(candidate, context) {
|
|
|
317
323
|
}
|
|
318
324
|
return candidate.Head(context);
|
|
319
325
|
}
|
|
326
|
+
async function resolvePageData(candidate, context) {
|
|
327
|
+
if (!candidate.Data) {
|
|
328
|
+
return void 0;
|
|
329
|
+
}
|
|
330
|
+
return candidate.Data(context);
|
|
331
|
+
}
|
|
320
332
|
function findBestMatch(entries, pathname) {
|
|
321
333
|
const matches = [];
|
|
322
334
|
for (const entry of entries) {
|
|
@@ -396,10 +408,11 @@ function createFileRouter(options) {
|
|
|
396
408
|
};
|
|
397
409
|
}
|
|
398
410
|
const errorModule = resolveModule(errorPath);
|
|
399
|
-
const errorNode = errorModule.default(errorProps);
|
|
411
|
+
const errorNode = await errorModule.default(errorProps);
|
|
400
412
|
const layoutHead = layoutModule ? await resolveHead(layoutModule, context) : void 0;
|
|
401
413
|
const errorHead = await resolveHead(errorModule, errorProps);
|
|
402
|
-
const
|
|
414
|
+
const layoutNode = layoutModule ? await layoutModule.default(context) : null;
|
|
415
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, errorNode) : errorNode;
|
|
403
416
|
return {
|
|
404
417
|
statusCode,
|
|
405
418
|
model,
|
|
@@ -431,10 +444,17 @@ function createFileRouter(options) {
|
|
|
431
444
|
activeContext = context;
|
|
432
445
|
const pageModule = resolveModule(match.entry.filePath);
|
|
433
446
|
const layoutModule = import_node_fs.default.existsSync(layoutPath) ? resolveModule(layoutPath) : null;
|
|
434
|
-
const
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
447
|
+
const pageData = await resolvePageData(pageModule, context);
|
|
448
|
+
const pageContext = {
|
|
449
|
+
...context,
|
|
450
|
+
data: pageData
|
|
451
|
+
};
|
|
452
|
+
activeContext = pageContext;
|
|
453
|
+
const pageNode = await pageModule.default(pageContext);
|
|
454
|
+
const layoutHead = layoutModule ? await resolveHead(layoutModule, pageContext) : void 0;
|
|
455
|
+
const pageHead = await resolveHead(pageModule, pageContext);
|
|
456
|
+
const layoutNode = layoutModule ? await layoutModule.default(pageContext) : null;
|
|
457
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, pageNode) : pageNode;
|
|
438
458
|
return {
|
|
439
459
|
statusCode: 200,
|
|
440
460
|
model,
|
|
@@ -479,14 +499,26 @@ function stripBasePath(pathname, basePath) {
|
|
|
479
499
|
}
|
|
480
500
|
function runNodeCommand(args) {
|
|
481
501
|
return new Promise((resolve, reject) => {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
502
|
+
const execArgs = [
|
|
503
|
+
"--conditions",
|
|
504
|
+
"react-server",
|
|
505
|
+
"-r",
|
|
506
|
+
"@webtypen/webframez-react/register",
|
|
507
|
+
...args
|
|
508
|
+
];
|
|
509
|
+
(0, import_node_child_process.execFile)(
|
|
510
|
+
process.execPath,
|
|
511
|
+
execArgs,
|
|
512
|
+
{ timeout: 1e4, maxBuffer: 1024 * 1024 * 5 },
|
|
513
|
+
(error, stdout, stderr) => {
|
|
514
|
+
if (error) {
|
|
515
|
+
const out = stderr && stderr.trim() !== "" ? stderr : stdout;
|
|
516
|
+
reject(new Error(out || error.message));
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
resolve({ stdout, stderr });
|
|
487
520
|
}
|
|
488
|
-
|
|
489
|
-
});
|
|
521
|
+
);
|
|
490
522
|
});
|
|
491
523
|
}
|
|
492
524
|
async function renderInitialHtmlInWorker(options) {
|
|
@@ -510,7 +542,7 @@ Module._resolveFilename = function(request, parent, isMain, options) {
|
|
|
510
542
|
}
|
|
511
543
|
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
512
544
|
};
|
|
513
|
-
const { createFileRouter } = require("webframez-react/router");
|
|
545
|
+
const { createFileRouter } = require("@webtypen/webframez-react/router");
|
|
514
546
|
const reactDomPkg = require.resolve("react-dom/package.json", {
|
|
515
547
|
paths: [process.cwd(), input.pagesDir]
|
|
516
548
|
});
|
|
@@ -649,7 +681,11 @@ function createNodeRequestHandler(options) {
|
|
|
649
681
|
cookies: requestCookies
|
|
650
682
|
})
|
|
651
683
|
);
|
|
652
|
-
|
|
684
|
+
const payload = {
|
|
685
|
+
model: resolved2.model,
|
|
686
|
+
head: resolved2.head
|
|
687
|
+
};
|
|
688
|
+
sendRSC(res, payload, {
|
|
653
689
|
moduleMap,
|
|
654
690
|
statusCode: resolved2.statusCode
|
|
655
691
|
});
|
package/dist/http.d.ts
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
|
|
3
|
-
export type
|
|
3
|
+
export type WebframezReactRoutePath = `/${string}` | "/";
|
|
4
|
+
export type WebframezReactAssetsPrefix = `${WebframezReactRoutePath}/` | "/";
|
|
5
|
+
|
|
6
|
+
export interface CreateNodeHandlerPathsOptions {
|
|
4
7
|
distRootDir: string;
|
|
5
8
|
pagesDir?: string;
|
|
6
9
|
manifestPath?: string;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CreateNodeHandlerRoutingOptions {
|
|
13
|
+
assetsPrefix?: WebframezReactAssetsPrefix;
|
|
14
|
+
rscPath?: WebframezReactRoutePath;
|
|
15
|
+
clientScriptUrl?: WebframezReactRoutePath;
|
|
16
|
+
basePath?: WebframezReactRoutePath;
|
|
17
|
+
liveReloadPath?: WebframezReactRoutePath | false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CreateNodeHandlerOptions
|
|
21
|
+
extends CreateNodeHandlerPathsOptions,
|
|
22
|
+
CreateNodeHandlerRoutingOptions {
|
|
23
|
+
}
|
|
13
24
|
|
|
14
25
|
export function createNodeRequestHandler(
|
|
15
26
|
options: CreateNodeHandlerOptions
|