rari 0.1.3
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/chunk-BLXvPPr8.js +30 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +193 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/platform-CvSUcmnc.js +102 -0
- package/dist/platform-DIsErRFA.js +3 -0
- package/dist/railway-XPhB0Ls4.js +216 -0
- package/dist/render-BtA14L2P.js +218 -0
- package/dist/runtime-client-BEWMJWMx.d.ts +283 -0
- package/dist/runtime-client-CC4YQweh.js +707 -0
- package/dist/server-BvGV8m79.js +7084 -0
- package/dist/server-MY0-nRif.d.ts +69 -0
- package/dist/server-build-Cp6_RdeA.js +3 -0
- package/dist/server-build-DaBgiV55.js +618 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +5 -0
- package/package.json +96 -0
- package/src/cli.ts +236 -0
- package/src/client.ts +61 -0
- package/src/deployment/railway.ts +244 -0
- package/src/deployment/render.ts +240 -0
- package/src/index.ts +1 -0
- package/src/platform.ts +163 -0
- package/src/router/file-routes.ts +453 -0
- package/src/router/index.ts +40 -0
- package/src/router/navigation.tsx +535 -0
- package/src/router/router.tsx +504 -0
- package/src/router/types.ts +168 -0
- package/src/router/utils.ts +473 -0
- package/src/router/vite-plugin.ts +324 -0
- package/src/runtime-client.ts +415 -0
- package/src/server.ts +79 -0
- package/src/vite/index.ts +1920 -0
- package/src/vite/server-build.ts +951 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Route, RouteGenerationOptions } from "./runtime-client-BEWMJWMx.js";
|
|
2
|
+
import { Plugin, UserConfig } from "rolldown-vite";
|
|
3
|
+
|
|
4
|
+
//#region src/router/file-routes.d.ts
|
|
5
|
+
declare class FileRouteGenerator {
|
|
6
|
+
private pagesDir;
|
|
7
|
+
private extensions;
|
|
8
|
+
private routes;
|
|
9
|
+
private routeTree;
|
|
10
|
+
constructor(options: RouteGenerationOptions);
|
|
11
|
+
generateRoutes(): Promise<Route[]>;
|
|
12
|
+
private scanPagesDirectory;
|
|
13
|
+
private scanDirectory;
|
|
14
|
+
private shouldSkipDirectory;
|
|
15
|
+
private shouldSkipFile;
|
|
16
|
+
private processFiles;
|
|
17
|
+
private fileToRoute;
|
|
18
|
+
private buildRouteTree;
|
|
19
|
+
private insertRouteIntoTree;
|
|
20
|
+
private applyLayoutToTree;
|
|
21
|
+
private establishParentChildRelations;
|
|
22
|
+
private flattenRouteTree;
|
|
23
|
+
getRoutes(): Route[];
|
|
24
|
+
getRouteByPath(path: string): Route | undefined;
|
|
25
|
+
getRouteByFilePath(filePath: string): Route | undefined;
|
|
26
|
+
refresh(): Promise<Route[]>;
|
|
27
|
+
}
|
|
28
|
+
declare function generateFileRoutes(options: RouteGenerationOptions): Promise<Route[]>;
|
|
29
|
+
declare function watchFileRoutes(options: RouteGenerationOptions, onChange: (routes: Route[]) => void): () => void;
|
|
30
|
+
declare function createRouteManifest(routes: Route[], outputPath: string): Promise<void>;
|
|
31
|
+
declare function loadRouteManifest(manifestPath: string): Promise<Route[]>;
|
|
32
|
+
declare function validateRoutes(routes: Route[]): {
|
|
33
|
+
valid: boolean;
|
|
34
|
+
errors: string[];
|
|
35
|
+
};
|
|
36
|
+
declare function convertFilePatternToRoutePattern(pattern: string): string;
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/router/vite-plugin.d.ts
|
|
39
|
+
interface RariRouterPluginOptions {
|
|
40
|
+
pagesDir?: string;
|
|
41
|
+
extensions?: string[];
|
|
42
|
+
outDir?: string;
|
|
43
|
+
generateTypes?: boolean;
|
|
44
|
+
validate?: boolean;
|
|
45
|
+
dev?: boolean;
|
|
46
|
+
transforms?: Array<(route: Route) => Route>;
|
|
47
|
+
}
|
|
48
|
+
declare function rariRouter(options?: RariRouterPluginOptions): Plugin;
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/vite/server-build.d.ts
|
|
51
|
+
interface ServerBuildOptions {
|
|
52
|
+
outDir?: string;
|
|
53
|
+
serverDir?: string;
|
|
54
|
+
manifestPath?: string;
|
|
55
|
+
minify?: boolean;
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/vite/index.d.ts
|
|
59
|
+
interface RariOptions {
|
|
60
|
+
projectRoot?: string;
|
|
61
|
+
serverBuild?: ServerBuildOptions;
|
|
62
|
+
serverHandler?: boolean;
|
|
63
|
+
}
|
|
64
|
+
declare function rari(options?: RariOptions): Plugin[];
|
|
65
|
+
declare function defineRariConfig(config: UserConfig & {
|
|
66
|
+
plugins?: Plugin[];
|
|
67
|
+
}): UserConfig;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { FileRouteGenerator, convertFilePatternToRoutePattern, createRouteManifest, defineRariConfig, generateFileRoutes, loadRouteManifest, rari, rariRouter, validateRoutes, watchFileRoutes };
|
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { build } from "esbuild";
|
|
5
|
+
|
|
6
|
+
//#region src/vite/server-build.ts
|
|
7
|
+
var ServerComponentBuilder = class {
|
|
8
|
+
serverComponents = /* @__PURE__ */ new Map();
|
|
9
|
+
options;
|
|
10
|
+
projectRoot;
|
|
11
|
+
getComponentCount() {
|
|
12
|
+
return this.serverComponents.size;
|
|
13
|
+
}
|
|
14
|
+
constructor(projectRoot, options = {}) {
|
|
15
|
+
this.projectRoot = projectRoot;
|
|
16
|
+
this.options = {
|
|
17
|
+
outDir: options.outDir || path.join(projectRoot, "dist"),
|
|
18
|
+
serverDir: options.serverDir || "server",
|
|
19
|
+
manifestPath: options.manifestPath || "server-manifest.json",
|
|
20
|
+
minify: options.minify ?? process.env.NODE_ENV === "production"
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
isServerComponent(filePath) {
|
|
24
|
+
try {
|
|
25
|
+
if (!fs.existsSync(filePath)) return false;
|
|
26
|
+
const code = fs.readFileSync(filePath, "utf-8");
|
|
27
|
+
const serverDirectives = [
|
|
28
|
+
"'use server'",
|
|
29
|
+
"\"use server\"",
|
|
30
|
+
"/* @server */",
|
|
31
|
+
"// @server"
|
|
32
|
+
];
|
|
33
|
+
const trimmedCode = code.trim();
|
|
34
|
+
const hasServerDirective = serverDirectives.some((directive) => trimmedCode.startsWith(directive) || code.includes(directive));
|
|
35
|
+
const isInFunctionsDir = filePath.includes("/functions/") || filePath.includes("\\functions\\");
|
|
36
|
+
const hasServerFunctionSignature = (code.includes("export async function") || code.includes("export function")) && code.includes("'use server'");
|
|
37
|
+
const hasNodeImports = code.includes("from 'node:") || code.includes("from \"node:") || code.includes("from 'fs'") || code.includes("from \"fs\"") || code.includes("from 'path'") || code.includes("from \"path\"");
|
|
38
|
+
const hasAsyncDefaultExport = /export\s+default\s+async\s+function/.test(code);
|
|
39
|
+
const isServer = hasServerDirective || isInFunctionsDir && hasServerFunctionSignature || hasNodeImports && hasAsyncDefaultExport;
|
|
40
|
+
return isServer;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
isClientComponent(filePath) {
|
|
46
|
+
try {
|
|
47
|
+
if (!fs.existsSync(filePath)) return false;
|
|
48
|
+
const code = fs.readFileSync(filePath, "utf-8");
|
|
49
|
+
const clientDirectives = [
|
|
50
|
+
"'use client'",
|
|
51
|
+
"\"use client\"",
|
|
52
|
+
"/* @client */",
|
|
53
|
+
"// @client"
|
|
54
|
+
];
|
|
55
|
+
const trimmedCode = code.trim();
|
|
56
|
+
const hasClientDirective = clientDirectives.some((directive) => trimmedCode.startsWith(directive) || code.includes(directive));
|
|
57
|
+
return hasClientDirective;
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
addServerComponent(filePath) {
|
|
63
|
+
if (!this.isServerComponent(filePath)) return;
|
|
64
|
+
const code = fs.readFileSync(filePath, "utf-8");
|
|
65
|
+
const dependencies = this.extractDependencies(code);
|
|
66
|
+
const hasNodeImports = this.hasNodeImports(code);
|
|
67
|
+
this.serverComponents.set(filePath, {
|
|
68
|
+
filePath,
|
|
69
|
+
originalCode: code,
|
|
70
|
+
dependencies,
|
|
71
|
+
hasNodeImports
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
extractDependencies(code) {
|
|
75
|
+
const dependencies = [];
|
|
76
|
+
const importRegex = /import(?:\s+(?:\w+|\{[^}]*\}|\*\s+as\s+\w+)(?:\s*,\s*(?:\w+|\{[^}]*\}|\*\s+as\s+\w+))*\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
77
|
+
let match;
|
|
78
|
+
while (true) {
|
|
79
|
+
match = importRegex.exec(code);
|
|
80
|
+
if (match === null) break;
|
|
81
|
+
const importPath = match[1];
|
|
82
|
+
if (!importPath.startsWith(".") && !importPath.startsWith("/") && !importPath.startsWith("node:") && !this.isNodeBuiltin(importPath)) dependencies.push(importPath);
|
|
83
|
+
}
|
|
84
|
+
return Array.from(new Set(dependencies));
|
|
85
|
+
}
|
|
86
|
+
isNodeBuiltin(moduleName) {
|
|
87
|
+
const nodeBuiltins = [
|
|
88
|
+
"fs",
|
|
89
|
+
"path",
|
|
90
|
+
"os",
|
|
91
|
+
"crypto",
|
|
92
|
+
"util",
|
|
93
|
+
"stream",
|
|
94
|
+
"events",
|
|
95
|
+
"process",
|
|
96
|
+
"buffer",
|
|
97
|
+
"url",
|
|
98
|
+
"querystring",
|
|
99
|
+
"zlib",
|
|
100
|
+
"http",
|
|
101
|
+
"https",
|
|
102
|
+
"net",
|
|
103
|
+
"tls",
|
|
104
|
+
"child_process",
|
|
105
|
+
"cluster",
|
|
106
|
+
"worker_threads"
|
|
107
|
+
];
|
|
108
|
+
return nodeBuiltins.includes(moduleName);
|
|
109
|
+
}
|
|
110
|
+
hasNodeImports(code) {
|
|
111
|
+
return code.includes("from 'node:") || code.includes("from \"node:") || code.includes("from 'fs'") || code.includes("from \"fs\"") || code.includes("from 'path'") || code.includes("from \"path\"") || code.includes("from 'os'") || code.includes("from \"os\"") || code.includes("from 'crypto'") || code.includes("from \"crypto\"") || code.includes("from 'util'") || code.includes("from \"util\"") || code.includes("from 'stream'") || code.includes("from \"stream\"") || code.includes("from 'events'") || code.includes("from \"events\"");
|
|
112
|
+
}
|
|
113
|
+
async getTransformedComponentsForDevelopment() {
|
|
114
|
+
const components = [];
|
|
115
|
+
for (const [filePath, component] of this.serverComponents) {
|
|
116
|
+
const relativePath = path.relative(this.projectRoot, filePath);
|
|
117
|
+
const componentId = this.getComponentId(relativePath);
|
|
118
|
+
const transformedCode = await this.buildComponentCodeOnly(filePath, componentId, component);
|
|
119
|
+
components.push({
|
|
120
|
+
id: componentId,
|
|
121
|
+
code: transformedCode
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return components;
|
|
125
|
+
}
|
|
126
|
+
async buildComponentCodeOnly(inputPath, componentId, _component) {
|
|
127
|
+
const originalCode = await fs.promises.readFile(inputPath, "utf-8");
|
|
128
|
+
const clientTransformedCode = this.transformClientImports(originalCode, inputPath);
|
|
129
|
+
const transformedCode = this.transformNodeImports(clientTransformedCode);
|
|
130
|
+
const ext = path.extname(inputPath);
|
|
131
|
+
let loader;
|
|
132
|
+
if (ext === ".tsx") loader = "tsx";
|
|
133
|
+
else if (ext === ".ts") loader = "ts";
|
|
134
|
+
else if (ext === ".jsx") loader = "jsx";
|
|
135
|
+
else loader = "js";
|
|
136
|
+
try {
|
|
137
|
+
const result = await build({
|
|
138
|
+
stdin: {
|
|
139
|
+
contents: transformedCode,
|
|
140
|
+
resolveDir: path.dirname(inputPath),
|
|
141
|
+
sourcefile: inputPath,
|
|
142
|
+
loader
|
|
143
|
+
},
|
|
144
|
+
bundle: true,
|
|
145
|
+
platform: "neutral",
|
|
146
|
+
target: "es2022",
|
|
147
|
+
format: "esm",
|
|
148
|
+
external: [],
|
|
149
|
+
mainFields: ["module", "main"],
|
|
150
|
+
conditions: [
|
|
151
|
+
"import",
|
|
152
|
+
"module",
|
|
153
|
+
"default"
|
|
154
|
+
],
|
|
155
|
+
define: {
|
|
156
|
+
"global": "globalThis",
|
|
157
|
+
"process.env.NODE_ENV": "\"production\""
|
|
158
|
+
},
|
|
159
|
+
loader: {
|
|
160
|
+
".ts": "ts",
|
|
161
|
+
".tsx": "tsx",
|
|
162
|
+
".js": "js",
|
|
163
|
+
".jsx": "jsx"
|
|
164
|
+
},
|
|
165
|
+
resolveExtensions: [
|
|
166
|
+
".ts",
|
|
167
|
+
".tsx",
|
|
168
|
+
".js",
|
|
169
|
+
".jsx"
|
|
170
|
+
],
|
|
171
|
+
minify: false,
|
|
172
|
+
minifyIdentifiers: false,
|
|
173
|
+
sourcemap: false,
|
|
174
|
+
metafile: false,
|
|
175
|
+
write: false,
|
|
176
|
+
plugins: [{
|
|
177
|
+
name: "hmr-auto-external",
|
|
178
|
+
setup(build$1) {
|
|
179
|
+
build$1.onResolve({ filter: /^[^./]/ }, async (args) => {
|
|
180
|
+
return {
|
|
181
|
+
path: args.path,
|
|
182
|
+
external: true
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}, {
|
|
187
|
+
name: "resolve-server-functions",
|
|
188
|
+
setup(build$1) {
|
|
189
|
+
build$1.onResolve({ filter: /^\.\.?\/.*functions/ }, async (args) => {
|
|
190
|
+
const resolvedPath = path.resolve(path.dirname(args.importer), args.path);
|
|
191
|
+
const possibleExtensions = [
|
|
192
|
+
".ts",
|
|
193
|
+
".js",
|
|
194
|
+
".tsx",
|
|
195
|
+
".jsx",
|
|
196
|
+
"/index.ts",
|
|
197
|
+
"/index.js"
|
|
198
|
+
];
|
|
199
|
+
for (const ext$1 of possibleExtensions) {
|
|
200
|
+
const fullPath = resolvedPath + ext$1;
|
|
201
|
+
if (fs.existsSync(fullPath)) return { path: fullPath };
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}],
|
|
207
|
+
banner: { js: `// Rari Server Component Bundle
|
|
208
|
+
// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
209
|
+
// Original file: ${path.relative(this.projectRoot, inputPath)}
|
|
210
|
+
` }
|
|
211
|
+
});
|
|
212
|
+
if (result.outputFiles && result.outputFiles.length > 0) {
|
|
213
|
+
const outputFile = result.outputFiles[0];
|
|
214
|
+
const finalTransformedCode = this.createSelfRegisteringModule(outputFile.text, componentId);
|
|
215
|
+
return finalTransformedCode;
|
|
216
|
+
}
|
|
217
|
+
if (result.errors.length > 0) {
|
|
218
|
+
console.error("ESBuild errors:", result.errors);
|
|
219
|
+
throw new Error(`ESBuild compilation failed with ${result.errors.length} errors`);
|
|
220
|
+
}
|
|
221
|
+
throw new Error("No output generated from ESBuild");
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error(`ESBuild failed for ${inputPath}:`, error);
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async buildServerComponents() {
|
|
228
|
+
const serverOutDir = path.join(this.options.outDir, this.options.serverDir);
|
|
229
|
+
await fs.promises.mkdir(serverOutDir, { recursive: true });
|
|
230
|
+
const manifest = {
|
|
231
|
+
components: {},
|
|
232
|
+
version: "1.0.0",
|
|
233
|
+
buildTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
234
|
+
};
|
|
235
|
+
for (const [filePath, component] of this.serverComponents) {
|
|
236
|
+
const relativePath = path.relative(this.projectRoot, filePath);
|
|
237
|
+
const componentId = this.getComponentId(relativePath);
|
|
238
|
+
const bundlePath = path.join(this.options.serverDir, `${componentId}.js`);
|
|
239
|
+
const fullBundlePath = path.join(this.options.outDir, bundlePath);
|
|
240
|
+
const bundleDir = path.dirname(fullBundlePath);
|
|
241
|
+
await fs.promises.mkdir(bundleDir, { recursive: true });
|
|
242
|
+
await this.buildSingleComponent(filePath, fullBundlePath, component);
|
|
243
|
+
manifest.components[componentId] = {
|
|
244
|
+
id: componentId,
|
|
245
|
+
filePath,
|
|
246
|
+
relativePath,
|
|
247
|
+
bundlePath,
|
|
248
|
+
dependencies: component.dependencies,
|
|
249
|
+
hasNodeImports: component.hasNodeImports
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const manifestPath = path.join(this.options.outDir, this.options.manifestPath);
|
|
253
|
+
await fs.promises.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
254
|
+
return manifest;
|
|
255
|
+
}
|
|
256
|
+
async buildSingleComponent(inputPath, outputPath, _component) {
|
|
257
|
+
const componentId = this.getComponentId(path.relative(this.projectRoot, inputPath));
|
|
258
|
+
const originalCode = await fs.promises.readFile(inputPath, "utf-8");
|
|
259
|
+
const clientTransformedCode = this.transformClientImports(originalCode, inputPath);
|
|
260
|
+
const transformedCode = this.transformNodeImports(clientTransformedCode);
|
|
261
|
+
const ext = path.extname(inputPath);
|
|
262
|
+
let loader;
|
|
263
|
+
if (ext === ".tsx") loader = "tsx";
|
|
264
|
+
else if (ext === ".ts") loader = "ts";
|
|
265
|
+
else if (ext === ".jsx") loader = "jsx";
|
|
266
|
+
else loader = "js";
|
|
267
|
+
try {
|
|
268
|
+
const result = await build({
|
|
269
|
+
stdin: {
|
|
270
|
+
contents: transformedCode,
|
|
271
|
+
resolveDir: path.dirname(inputPath),
|
|
272
|
+
sourcefile: inputPath,
|
|
273
|
+
loader
|
|
274
|
+
},
|
|
275
|
+
bundle: true,
|
|
276
|
+
platform: "neutral",
|
|
277
|
+
target: "es2022",
|
|
278
|
+
format: "esm",
|
|
279
|
+
outfile: outputPath,
|
|
280
|
+
external: [],
|
|
281
|
+
mainFields: ["module", "main"],
|
|
282
|
+
conditions: [
|
|
283
|
+
"import",
|
|
284
|
+
"module",
|
|
285
|
+
"default"
|
|
286
|
+
],
|
|
287
|
+
define: {
|
|
288
|
+
"global": "globalThis",
|
|
289
|
+
"process.env.NODE_ENV": "\"production\""
|
|
290
|
+
},
|
|
291
|
+
loader: {
|
|
292
|
+
".ts": "ts",
|
|
293
|
+
".tsx": "tsx",
|
|
294
|
+
".js": "js",
|
|
295
|
+
".jsx": "jsx"
|
|
296
|
+
},
|
|
297
|
+
resolveExtensions: [
|
|
298
|
+
".ts",
|
|
299
|
+
".tsx",
|
|
300
|
+
".js",
|
|
301
|
+
".jsx"
|
|
302
|
+
],
|
|
303
|
+
minify: false,
|
|
304
|
+
minifyIdentifiers: false,
|
|
305
|
+
sourcemap: false,
|
|
306
|
+
metafile: false,
|
|
307
|
+
write: false,
|
|
308
|
+
plugins: [{
|
|
309
|
+
name: "auto-external",
|
|
310
|
+
setup(build$1) {
|
|
311
|
+
build$1.onResolve({ filter: /^[^./]/ }, async (args) => {
|
|
312
|
+
return {
|
|
313
|
+
path: args.path,
|
|
314
|
+
external: true
|
|
315
|
+
};
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}, {
|
|
319
|
+
name: "resolve-server-functions",
|
|
320
|
+
setup(build$1) {
|
|
321
|
+
build$1.onResolve({ filter: /^\.\.?\/.*functions/ }, async (args) => {
|
|
322
|
+
const resolvedPath = path.resolve(path.dirname(args.importer), args.path);
|
|
323
|
+
const possibleExtensions = [
|
|
324
|
+
".ts",
|
|
325
|
+
".js",
|
|
326
|
+
".tsx",
|
|
327
|
+
".jsx",
|
|
328
|
+
"/index.ts",
|
|
329
|
+
"/index.js"
|
|
330
|
+
];
|
|
331
|
+
for (const ext$1 of possibleExtensions) {
|
|
332
|
+
const fullPath = resolvedPath + ext$1;
|
|
333
|
+
if (fs.existsSync(fullPath)) return { path: fullPath };
|
|
334
|
+
}
|
|
335
|
+
return null;
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}],
|
|
339
|
+
banner: { js: `// Rari Server Component Bundle
|
|
340
|
+
// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
341
|
+
// Original file: ${path.relative(this.projectRoot, inputPath)}
|
|
342
|
+
` }
|
|
343
|
+
});
|
|
344
|
+
if (result.outputFiles && result.outputFiles.length > 0) {
|
|
345
|
+
const outputFile = result.outputFiles[0];
|
|
346
|
+
const finalTransformedCode = this.createSelfRegisteringModule(outputFile.text, componentId);
|
|
347
|
+
await fs.promises.writeFile(outputPath, finalTransformedCode, "utf-8");
|
|
348
|
+
}
|
|
349
|
+
if (result.errors.length > 0) {
|
|
350
|
+
console.error("ESBuild errors:", result.errors);
|
|
351
|
+
throw new Error(`ESBuild compilation failed with ${result.errors.length} errors`);
|
|
352
|
+
}
|
|
353
|
+
if (result.warnings.length > 0) console.warn("ESBuild warnings:", result.warnings);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.error(`ESBuild failed for ${inputPath}:`, error);
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
createSelfRegisteringModule(code, componentId) {
|
|
360
|
+
if (code.includes("Self-registering Production Component")) return code;
|
|
361
|
+
let transformedCode = code;
|
|
362
|
+
let defaultExportName = null;
|
|
363
|
+
const namedExports = [];
|
|
364
|
+
transformedCode = transformedCode.replace(/^export\s+default\s+function\s+(\w+)/gm, (match, name) => {
|
|
365
|
+
defaultExportName = name;
|
|
366
|
+
return `function ${name}`;
|
|
367
|
+
});
|
|
368
|
+
transformedCode = transformedCode.replace(/^export\s+default\s+async\s+function\s+(\w+)/gm, (match, name) => {
|
|
369
|
+
defaultExportName = name;
|
|
370
|
+
return `async function ${name}`;
|
|
371
|
+
});
|
|
372
|
+
transformedCode = transformedCode.replace(/^export\s+default\s+(\w+);?\s*$/gm, (match, name) => {
|
|
373
|
+
defaultExportName = name;
|
|
374
|
+
return `// Default export: ${name}`;
|
|
375
|
+
});
|
|
376
|
+
transformedCode = transformedCode.replace(/^export\s*\{\s*(\w+)\s+as\s+default\s*\};?\s*$/gm, (match, name) => {
|
|
377
|
+
defaultExportName = name;
|
|
378
|
+
return `// Default export: ${name}`;
|
|
379
|
+
});
|
|
380
|
+
transformedCode = transformedCode.replace(/^export\s*\{([^}]+)\};?\s*$/gm, (match, exports) => {
|
|
381
|
+
const exportList = exports.split(",").map((exp) => exp.trim());
|
|
382
|
+
exportList.forEach((exp) => {
|
|
383
|
+
if (exp.includes("as default")) {
|
|
384
|
+
const actualName = exp.replace("as default", "").trim();
|
|
385
|
+
defaultExportName = actualName;
|
|
386
|
+
} else if (exp === "default") {
|
|
387
|
+
const possibleDefault = `${componentId}_default`;
|
|
388
|
+
if (transformedCode.includes(`var ${possibleDefault}`)) defaultExportName = possibleDefault;
|
|
389
|
+
} else namedExports.push(exp);
|
|
390
|
+
});
|
|
391
|
+
return `// Exports: ${exports}`;
|
|
392
|
+
});
|
|
393
|
+
transformedCode = transformedCode.replace(/^export\s+(?:async\s+)?function\s+(\w+)/gm, (match, name) => {
|
|
394
|
+
namedExports.push(name);
|
|
395
|
+
return match.replace("export ", "");
|
|
396
|
+
});
|
|
397
|
+
transformedCode = transformedCode.replace(/^export\s+(const|let|var)\s+(\w+)/gm, (match, keyword, name) => {
|
|
398
|
+
namedExports.push(name);
|
|
399
|
+
return `${keyword} ${name}`;
|
|
400
|
+
});
|
|
401
|
+
if (!defaultExportName) {
|
|
402
|
+
const possibleDefault = `${componentId}_default`;
|
|
403
|
+
if (transformedCode.includes(`var ${possibleDefault}`)) defaultExportName = possibleDefault;
|
|
404
|
+
}
|
|
405
|
+
const mainExport = defaultExportName || componentId;
|
|
406
|
+
const selfRegisteringCode = `// Self-registering Production Component: ${componentId}
|
|
407
|
+
"use module";
|
|
408
|
+
|
|
409
|
+
// Original component code with exports removed for self-registration
|
|
410
|
+
${transformedCode}
|
|
411
|
+
|
|
412
|
+
// Self-registration logic
|
|
413
|
+
(function() {
|
|
414
|
+
try {
|
|
415
|
+
const moduleKey = "${componentId}";
|
|
416
|
+
const registrationKey = "Component_${Math.random().toString(36).substr(2, 8)}";
|
|
417
|
+
let mainExport = null;
|
|
418
|
+
let exportedFunctions = {};
|
|
419
|
+
|
|
420
|
+
globalThis.__rsc_functions = globalThis.__rsc_functions || {};
|
|
421
|
+
|
|
422
|
+
// Register named exports
|
|
423
|
+
${namedExports.map((name) => `
|
|
424
|
+
if (typeof ${name} !== 'undefined') {
|
|
425
|
+
globalThis.${name} = ${name};
|
|
426
|
+
globalThis.__rsc_functions['${name}'] = ${name};
|
|
427
|
+
exportedFunctions['${name}'] = ${name};
|
|
428
|
+
}`).join("")}
|
|
429
|
+
|
|
430
|
+
// Set main export
|
|
431
|
+
if (typeof ${mainExport} !== 'undefined') {
|
|
432
|
+
mainExport = ${mainExport};
|
|
433
|
+
} else {
|
|
434
|
+
const potentialExports = {};
|
|
435
|
+
${namedExports.map((name) => `if (typeof ${name} !== 'undefined') potentialExports.${name} = ${name};`).join("\n ")}
|
|
436
|
+
|
|
437
|
+
if (Object.keys(potentialExports).length > 0) {
|
|
438
|
+
if (Object.keys(potentialExports).length === 1) {
|
|
439
|
+
mainExport = potentialExports[Object.keys(potentialExports)[0]];
|
|
440
|
+
} else {
|
|
441
|
+
mainExport = potentialExports;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (mainExport !== null) {
|
|
447
|
+
if (!globalThis[moduleKey]) {
|
|
448
|
+
globalThis[moduleKey] = mainExport;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (!globalThis[registrationKey]) {
|
|
452
|
+
globalThis[registrationKey] = mainExport;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (typeof globalThis.RscModuleManager !== 'undefined' && globalThis.RscModuleManager.register) {
|
|
456
|
+
globalThis.RscModuleManager.register(moduleKey, mainExport, exportedFunctions);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
} catch (error) {
|
|
460
|
+
console.error('Error in self-registration for ${componentId}:', error);
|
|
461
|
+
}
|
|
462
|
+
})();`;
|
|
463
|
+
return selfRegisteringCode;
|
|
464
|
+
}
|
|
465
|
+
transformClientImports(code, inputPath) {
|
|
466
|
+
let transformedCode = code;
|
|
467
|
+
const importRegex = /import\s+(\w+)(?:\s*,\s*\{[^}]*\})?\s+from\s+['"]([./][^'"]+)['"];?\s*$/gm;
|
|
468
|
+
let match;
|
|
469
|
+
const replacements = [];
|
|
470
|
+
let hasClientComponents = false;
|
|
471
|
+
while (true) {
|
|
472
|
+
match = importRegex.exec(code);
|
|
473
|
+
if (match === null) break;
|
|
474
|
+
const [fullMatch, defaultImport, importPath] = match;
|
|
475
|
+
const resolvedPath = this.resolveImportPath(importPath, inputPath);
|
|
476
|
+
if (this.isClientComponent(resolvedPath)) {
|
|
477
|
+
hasClientComponents = true;
|
|
478
|
+
const componentName = defaultImport || "default";
|
|
479
|
+
const replacement = `const ${componentName} = registerClientReference(
|
|
480
|
+
null,
|
|
481
|
+
${JSON.stringify(path.relative(this.projectRoot, resolvedPath))},
|
|
482
|
+
"default"
|
|
483
|
+
);`;
|
|
484
|
+
replacements.push({
|
|
485
|
+
original: fullMatch,
|
|
486
|
+
replacement
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (hasClientComponents) {
|
|
491
|
+
const functionDefinition = `
|
|
492
|
+
// registerClientReference function for client components
|
|
493
|
+
function registerClientReference(clientReference, id, exportName) {
|
|
494
|
+
const key = id + '#' + exportName;
|
|
495
|
+
|
|
496
|
+
const clientProxy = {};
|
|
497
|
+
|
|
498
|
+
Object.defineProperty(clientProxy, '$$typeof', {
|
|
499
|
+
value: Symbol.for('react.client.reference'),
|
|
500
|
+
enumerable: false
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
Object.defineProperty(clientProxy, '$$id', {
|
|
504
|
+
value: key,
|
|
505
|
+
enumerable: false
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
Object.defineProperty(clientProxy, '$$async', {
|
|
509
|
+
value: false,
|
|
510
|
+
enumerable: false
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
try {
|
|
514
|
+
if (typeof globalThis.__rari_bridge !== 'undefined' &&
|
|
515
|
+
typeof globalThis.__rari_bridge.registerClientReference === 'function') {
|
|
516
|
+
globalThis.__rari_bridge.registerClientReference(key, id, exportName);
|
|
517
|
+
}
|
|
518
|
+
} catch (error) {
|
|
519
|
+
console.error('Failed to register client reference with Rust bridge:', error);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return clientProxy;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
`;
|
|
526
|
+
transformedCode = functionDefinition + transformedCode;
|
|
527
|
+
}
|
|
528
|
+
for (const { original, replacement } of replacements) transformedCode = transformedCode.replace(original, replacement);
|
|
529
|
+
return transformedCode;
|
|
530
|
+
}
|
|
531
|
+
resolveImportPath(importPath, importerPath) {
|
|
532
|
+
const resolvedPath = path.resolve(path.dirname(importerPath), importPath);
|
|
533
|
+
const extensions = [
|
|
534
|
+
".tsx",
|
|
535
|
+
".jsx",
|
|
536
|
+
".ts",
|
|
537
|
+
".js"
|
|
538
|
+
];
|
|
539
|
+
for (const ext of extensions) {
|
|
540
|
+
const pathWithExt = `${resolvedPath}${ext}`;
|
|
541
|
+
if (fs.existsSync(pathWithExt)) return pathWithExt;
|
|
542
|
+
}
|
|
543
|
+
if (fs.existsSync(resolvedPath)) for (const ext of extensions) {
|
|
544
|
+
const indexPath = path.join(resolvedPath, `index${ext}`);
|
|
545
|
+
if (fs.existsSync(indexPath)) return indexPath;
|
|
546
|
+
}
|
|
547
|
+
return `${resolvedPath}.tsx`;
|
|
548
|
+
}
|
|
549
|
+
transformNodeImports(code) {
|
|
550
|
+
let transformedCode = code;
|
|
551
|
+
transformedCode = transformedCode.replace(/import\s+(\w+)\s+from\s+['"]node:process['"];?\s*$/gm, (match, importName) => {
|
|
552
|
+
return `const ${importName} = globalThis.process;`;
|
|
553
|
+
});
|
|
554
|
+
transformedCode = transformedCode.replace(/import\s+\{([^}]+)\}\s+from\s+['"]node:fs['"];?\s*$/gm, (match, imports) => {
|
|
555
|
+
const importList = imports.split(",").map((imp) => imp.trim());
|
|
556
|
+
return importList.map((imp) => {
|
|
557
|
+
const cleanImp = imp.replace(/\s+as\s+\w+/, "");
|
|
558
|
+
if (cleanImp === "existsSync") return `const ${cleanImp} = (path) => { try { if (globalThis.Deno?.statSync) { globalThis.Deno.statSync(path); return true; } return false; } catch (error) { return false; } };`;
|
|
559
|
+
if (cleanImp === "readFileSync") return `const ${cleanImp} = (path, encoding = 'utf8') => globalThis.Deno.readTextFileSync(path);`;
|
|
560
|
+
return `const ${cleanImp} = globalThis.Deno?.${cleanImp} || (() => { throw new Error('${cleanImp} not available'); });`;
|
|
561
|
+
}).join("\n");
|
|
562
|
+
});
|
|
563
|
+
transformedCode = transformedCode.replace(/import\s+\{([^}]+)\}\s+from\s+['"]node:path['"];?\s*$/gm, (match, imports) => {
|
|
564
|
+
const importList = imports.split(",").map((imp) => imp.trim());
|
|
565
|
+
return importList.map((imp) => {
|
|
566
|
+
const cleanImp = imp.replace(/\s+as\s+\w+/, "");
|
|
567
|
+
if (cleanImp === "join") return `const ${cleanImp} = (...paths) => paths.filter(Boolean).join('/').replace(/\\/+/g, '/');`;
|
|
568
|
+
return `const ${cleanImp} = globalThis.path?.${cleanImp} || (() => { throw new Error('${cleanImp} not available'); });`;
|
|
569
|
+
}).join("\n");
|
|
570
|
+
});
|
|
571
|
+
transformedCode = transformedCode.replace(/import\s+\{([^}]+)\}\s+from\s+['"]node:process['"];?\s*$/gm, (match, imports) => {
|
|
572
|
+
const importList = imports.split(",").map((imp) => imp.trim());
|
|
573
|
+
return importList.map((imp) => {
|
|
574
|
+
const cleanImp = imp.replace(/\s+as\s+\w+/, "");
|
|
575
|
+
if (cleanImp === "cwd") return `const ${cleanImp} = () => globalThis.Deno?.cwd?.() || '.';`;
|
|
576
|
+
return `const ${cleanImp} = globalThis.process?.${cleanImp} || (() => { throw new Error('${cleanImp} not available'); });`;
|
|
577
|
+
}).join("\n");
|
|
578
|
+
});
|
|
579
|
+
return transformedCode;
|
|
580
|
+
}
|
|
581
|
+
getComponentId(relativePath) {
|
|
582
|
+
return relativePath.replace(/\\/g, "/").replace(/\.(tsx?|jsx?)$/, "").replace(/[^\w/-]/g, "_").replace(/^src\//, "").replace(/^components\//, "");
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
function scanDirectory(dir, builder) {
|
|
586
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
587
|
+
for (const entry of entries) {
|
|
588
|
+
const fullPath = path.join(dir, entry.name);
|
|
589
|
+
if (entry.isDirectory()) scanDirectory(fullPath, builder);
|
|
590
|
+
else if (entry.isFile() && /\.(?:tsx?|jsx?)$/.test(entry.name)) try {
|
|
591
|
+
if (builder.isServerComponent(fullPath)) builder.addServerComponent(fullPath);
|
|
592
|
+
} catch (error) {
|
|
593
|
+
console.warn(`[server-build] Error checking ${fullPath}:`, error instanceof Error ? error.message : error);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function createServerBuildPlugin(options = {}) {
|
|
598
|
+
let builder = null;
|
|
599
|
+
let projectRoot;
|
|
600
|
+
return {
|
|
601
|
+
name: "rari-server-build",
|
|
602
|
+
configResolved(config) {
|
|
603
|
+
projectRoot = config.root;
|
|
604
|
+
builder = new ServerComponentBuilder(projectRoot, options);
|
|
605
|
+
},
|
|
606
|
+
buildStart() {
|
|
607
|
+
if (!builder) return;
|
|
608
|
+
const srcDir = path.join(projectRoot, "src");
|
|
609
|
+
if (fs.existsSync(srcDir)) scanDirectory(srcDir, builder);
|
|
610
|
+
},
|
|
611
|
+
async closeBundle() {
|
|
612
|
+
if (builder) await builder.buildServerComponents();
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
//#endregion
|
|
618
|
+
export { ServerComponentBuilder, createServerBuildPlugin, scanDirectory };
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ErrorBoundaryProps, FileRouteInfo, HttpRuntimeClient, LayoutProps, Link, LinkProps, LoadingProps, Navigate, NavigationOptions, NavigationState, Outlet, PageComponent, PageProps, Route, RouteComponent, RouteGenerationOptions, RouteMatch, RouteMeta, RouteParams, RouterConfig, RouterContext, RouterProvider, RouterProviderProps, Routes, RuntimeClient, SearchParams, buildSearchString, buildUrl, createHttpRuntimeClient, extractParamNames, findMatchingRoute, getRoutePriority, isDynamicRoute, isPathActive, joinPaths, normalizePathname, parseSearchParams, parseUrl, useNavigation, useParams, usePathname, useRoute, useRouter, useSearchParams, withRouter } from "./runtime-client-BEWMJWMx.js";
|
|
2
|
+
import { FileRouteGenerator, convertFilePatternToRoutePattern, createRouteManifest, defineRariConfig, generateFileRoutes, loadRouteManifest, rari, rariRouter, validateRoutes, watchFileRoutes } from "./server-MY0-nRif.js";
|
|
3
|
+
export { ErrorBoundaryProps, FileRouteGenerator, FileRouteInfo, HttpRuntimeClient, LayoutProps, Link, LinkProps, LoadingProps, Navigate, NavigationOptions, NavigationState, Outlet, PageComponent, PageProps, RouteComponent as Route, RouteGenerationOptions, RouteMatch, RouteMeta, RouteParams, Route as RouteType, RouterConfig, RouterContext, RouterProvider, RouterProviderProps, Routes, RuntimeClient, SearchParams, buildSearchString, buildUrl, convertFilePatternToRoutePattern, createHttpRuntimeClient, createRouteManifest, defineRariConfig, extractParamNames, findMatchingRoute, generateFileRoutes, getRoutePriority, isDynamicRoute, isPathActive, joinPaths, loadRouteManifest, normalizePathname, parseSearchParams, parseUrl, rari, rariRouter, useNavigation, useParams, usePathname, useRoute, useRouter, useSearchParams, validateRoutes, watchFileRoutes, withRouter };
|