@enhancd/react-file-router 1.0.0 → 1.0.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.
Files changed (63) hide show
  1. package/README.md +19 -4
  2. package/dist/index.cjs.js +2 -0
  3. package/dist/index.cjs.js.map +1 -0
  4. package/dist/index.d.ts +31 -0
  5. package/dist/index.esm.js +2 -0
  6. package/dist/index.esm.js.map +1 -0
  7. package/dist/lib/navigate-to-page/navigate-to-page.d.ts +25 -0
  8. package/{src/lib/reactFileRouter.ts → dist/lib/reactFileRouter.d.ts} +0 -1
  9. package/dist/lib/route/route.d.ts +2 -0
  10. package/dist/lib/router-schema/router-schema.d.ts +2 -0
  11. package/dist/lib/vite-plugin/__tests__/src/$router/$default/DefaultDeclaration.page.d.ts +1 -0
  12. package/dist/lib/vite-plugin/__tests__/src/$router/$lazy/Lazy.fallback.d.ts +1 -0
  13. package/dist/lib/vite-plugin/__tests__/src/$router/$lazy/Lazy.lazy.page.d.ts +1 -0
  14. package/dist/lib/vite-plugin/__tests__/src/$router/Index.404.d.ts +1 -0
  15. package/dist/lib/vite-plugin/__tests__/src/$router/Index.error.d.ts +1 -0
  16. package/dist/lib/vite-plugin/__tests__/src/$router/Index.layout.d.ts +1 -0
  17. package/dist/lib/vite-plugin/__tests__/src/$router/Index.page.d.ts +1 -0
  18. package/dist/lib/vite-plugin/compile-folder-to-schema/compileFolderToSchema.d.ts +57 -0
  19. package/dist/lib/vite-plugin/compile-lazy-pages/compileLazyPages.d.ts +6 -0
  20. package/dist/lib/vite-plugin/compile-schema-to-router-file/compileSchemaToRouterFile.d.ts +2 -0
  21. package/dist/lib/vite-plugin/transpile-lazy-page/transpile-lazy-page.d.ts +3 -0
  22. package/dist/lib/vite-plugin/vitePlugin.d.ts +6 -0
  23. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  24. package/dist/vite-plugin.cjs.js +4 -0
  25. package/dist/vite-plugin.cjs.js.map +1 -0
  26. package/dist/vite-plugin.d.ts +9 -0
  27. package/dist/vite-plugin.esm.js +4 -0
  28. package/dist/vite-plugin.esm.js.map +1 -0
  29. package/package.json +2 -1
  30. package/eslint.config.mjs +0 -8
  31. package/rollup.config.js +0 -99
  32. package/src/index.ts +0 -1
  33. package/src/lib/navigate-to-page/navigate-to-page.tsx +0 -23
  34. package/src/lib/route/route.ts +0 -12
  35. package/src/lib/router-schema/router-schema.ts +0 -5
  36. package/src/lib/vite-plugin/__tests__/reactFileRouterVitePlugin.spec.ts +0 -16
  37. package/src/lib/vite-plugin/__tests__/src/$router/$default/DefaultDeclaration.page.tsx +0 -3
  38. package/src/lib/vite-plugin/__tests__/src/$router/$lazy/Lazy.fallback.tsx +0 -3
  39. package/src/lib/vite-plugin/__tests__/src/$router/$lazy/Lazy.lazy.page.tsx +0 -3
  40. package/src/lib/vite-plugin/__tests__/src/$router/Index.404.tsx +0 -3
  41. package/src/lib/vite-plugin/__tests__/src/$router/Index.error.tsx +0 -3
  42. package/src/lib/vite-plugin/__tests__/src/$router/Index.layout.tsx +0 -10
  43. package/src/lib/vite-plugin/__tests__/src/$router/Index.page.tsx +0 -3
  44. package/src/lib/vite-plugin/compile-folder-to-schema/compileFolderToSchema.ts +0 -125
  45. package/src/lib/vite-plugin/compile-lazy-pages/compileLazyPages.ts +0 -30
  46. package/src/lib/vite-plugin/compile-schema-to-router-file/compileSchemaToRouterFile.ts +0 -64
  47. package/src/lib/vite-plugin/transpile-lazy-page/transpile-lazy-page.ts +0 -29
  48. package/src/lib/vite-plugin/vitePlugin.ts +0 -69
  49. package/src/types/reactFileRouterSchema.d.ts +0 -14
  50. package/tsconfig.json +0 -13
  51. package/tsconfig.lib.json +0 -32
  52. package/tsconfig.spec.json +0 -37
  53. package/vite.config.ts +0 -19
  54. /package/{src/lib/navigate-to-page/index.ts → dist/lib/navigate-to-page/index.d.ts} +0 -0
  55. /package/{src/lib/vite-plugin/__tests__/src/$router/$deep$nested$path/DeepNestedPath.page.tsx → dist/lib/vite-plugin/__tests__/src/$router/$deep$nested$path/DeepNestedPath.page.d.ts} +0 -0
  56. /package/{src/lib/vite-plugin/__tests__/src/$router/$default/$arrow/DefaultArrow.page.tsx → dist/lib/vite-plugin/__tests__/src/$router/$default/$arrow/DefaultArrow.page.d.ts} +0 -0
  57. /package/{src/lib/vite-plugin/__tests__/src/$router/$lazy/Lazy.layout.tsx → dist/lib/vite-plugin/__tests__/src/$router/$lazy/Lazy.layout.d.ts} +0 -0
  58. /package/{src/lib/vite-plugin/__tests__/src/$router/$named/$arrow/NamedArrowPage.tsx → dist/lib/vite-plugin/__tests__/src/$router/$named/$arrow/NamedArrowPage.d.ts} +0 -0
  59. /package/{src/lib/vite-plugin/__tests__/src/$router/$named/$declaration/NamedDeclaration.page.tsx → dist/lib/vite-plugin/__tests__/src/$router/$named/$declaration/NamedDeclaration.page.d.ts} +0 -0
  60. /package/{src/lib/vite-plugin/__tests__/src/$router/$query@idx/$nested@param/QueryNested.page.tsx → dist/lib/vite-plugin/__tests__/src/$router/$query@idx/$nested@param/QueryNested.page.d.ts} +0 -0
  61. /package/{src/lib/vite-plugin/__tests__/src/$router/$query@idx/Query.404.tsx → dist/lib/vite-plugin/__tests__/src/$router/$query@idx/Query.404.d.ts} +0 -0
  62. /package/{src/lib/vite-plugin/__tests__/src/$router/$query@idx/Query.layout.tsx → dist/lib/vite-plugin/__tests__/src/$router/$query@idx/Query.layout.d.ts} +0 -0
  63. /package/{src/lib/vite-plugin/__tests__/src/$router/$query@idx/Query.page.tsx → dist/lib/vite-plugin/__tests__/src/$router/$query@idx/Query.page.d.ts} +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@enhancd/react-file-router",
3
3
  "description": "File-based routing for Vite + React apps using react-router-dom V6. Automatically generates routes from a directory structure — no manual RouteObject configuration needed.",
4
- "version": "1.0.0",
4
+ "version": "1.0.2",
5
5
  "type": "module",
6
6
  "author": "Alexandr Maliovaniy <alexandrmaliovaniy@gmail.com>",
7
7
  "license": "MIT",
@@ -14,6 +14,7 @@
14
14
  "test": "vitest",
15
15
  "build": "tsc && rollup -c"
16
16
  },
17
+ "files": ["dist"],
17
18
  "main": "./dist/index.cjs.js",
18
19
  "module": "./dist/index.esm.js",
19
20
  "types": "./dist/index.d.ts",
package/eslint.config.mjs DELETED
@@ -1,8 +0,0 @@
1
- import { createConfig } from "../../eslint.config.mjs";
2
- import { fileURLToPath } from "url";
3
- import { dirname } from "path";
4
-
5
- const __filename = fileURLToPath(import.meta.url);
6
- const __dirname = dirname(__filename);
7
-
8
- export default createConfig(__dirname);
package/rollup.config.js DELETED
@@ -1,99 +0,0 @@
1
- import resolve from "@rollup/plugin-node-resolve";
2
- import commonjs from "@rollup/plugin-commonjs";
3
- import esbuild from "rollup-plugin-esbuild";
4
- import terser from "@rollup/plugin-terser";
5
- import peerDepsExternal from "rollup-plugin-peer-deps-external";
6
- import dts from "rollup-plugin-dts";
7
- import { readFileSync } from "fs";
8
-
9
- function logBundledModules() {
10
- const seen = new Set();
11
-
12
- return {
13
- name: "log-bundled-modules",
14
- moduleParsed(info) {
15
- if (seen.has(info.id)) return;
16
- seen.add(info.id);
17
- if (info.id.includes("\0")) return;
18
-
19
- console.log("[bundled]", info.id);
20
- }
21
- };
22
- }
23
-
24
- const pkg = JSON.parse(readFileSync("./package.json", "utf-8"));
25
-
26
- const entries = {
27
- index: "src/index.ts",
28
- "vite-plugin": "src/lib/vite-plugin/vitePlugin.ts",
29
- };
30
-
31
- const createConfig = (input, output, format) => ({
32
- input,
33
- output: {
34
- file: output,
35
- format,
36
- sourcemap: true,
37
- exports: "named",
38
- ...(format === "esm" && { preserveModules: false }),
39
- },
40
- onwarn(warning, warn) {
41
- if (warning.code === "UNRESOLVED_IMPORT") {
42
- throw new Error(warning.message);
43
- }
44
- warn(warning);
45
- },
46
- plugins: [
47
- logBundledModules(),
48
- peerDepsExternal(),
49
- esbuild({
50
- include: /\.[jt]sx?$/,
51
- minify: false,
52
- target: "es2015",
53
- jsx: "automatic",
54
- tsconfig: "tsconfig.json",
55
- }),
56
- resolve({
57
- extensions: [".ts", ".tsx", ".js", ".jsx"],
58
- }),
59
- commonjs(),
60
- terser(),
61
- ],
62
- external: [
63
- ...Object.keys(pkg.peerDependencies || {}),
64
- /^virtual:/,
65
- ],
66
- });
67
-
68
- const createDtsConfig = (input, output) => ({
69
- input,
70
- output: {
71
- file: output,
72
- format: "esm",
73
- },
74
- plugins: [
75
- dts({
76
- respectExternal: true,
77
- compilerOptions: {
78
- declarationMap: false,
79
- }
80
- })
81
- ],
82
- external: [
83
- /node_modules/,
84
- ...Object.keys(pkg.peerDependencies || {}),
85
- /^virtual:/,
86
- ],
87
- });
88
-
89
- const configs = Object.entries(entries).flatMap(([name, input]) => {
90
- const outputBase = name === "index" ? "dist/index" : `dist/${name}`;
91
-
92
- return [
93
- createConfig(input, `${outputBase}.esm.js`, "esm"),
94
- createConfig(input, `${outputBase}.cjs.js`, "cjs"),
95
- createDtsConfig(input, `${outputBase}.d.ts`),
96
- ];
97
- });
98
-
99
- export default configs;
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from "./lib/reactFileRouter";
@@ -1,23 +0,0 @@
1
- import { ReactElement } from "react";
2
- import { Navigate, useNavigate, NavigateProps, NavLink, Link, NavLinkProps, LinkProps } from "react-router-dom"
3
- import { route } from "../reactFileRouter";
4
-
5
- export const useNavigateToPage = () => {
6
- const navigate = useNavigate();
7
- return <Page extends (...args: any[]) => ReactElement>(page: Page, params?: Page extends (...args: [infer Args]) => ReactElement ? { params?: Args } & NavigateProps : { params: {} }) => navigate(route(page, params?.params), params);
8
- }
9
-
10
- export const NavigateToPage = <Page extends (...args: any[]) => ReactElement>(props: Omit<NavigateProps, "to"> & { to: Page, params?: Page extends (...args: [infer Args]) => ReactElement ? Args : { params: {} }}) => {
11
- const {to, params: prms, ...prps} = props;
12
- return <Navigate to={route(to, prms)} {...prps} />;
13
- }
14
-
15
- export const NavLinkToPage = <Page extends (...args: any[]) => ReactElement>(props: Omit<NavLinkProps, "to"> & { to: Page, params?: Page extends (...args: [infer Args]) => ReactElement ? Args : { params: {} }}) => {
16
- const {to, params: prms, ...prps} = props;
17
- return <NavLink to={route(to, prms)} {...prps} />;
18
- }
19
-
20
- export const LinkToPage = <Page extends (...args: any[]) => ReactElement>(props: Omit<LinkProps, "to"> & { to: Page, params?: Page extends (...args: [infer Args]) => ReactElement ? Args : { params: {} }}) => {
21
- const {to, params: prms, ...prps} = props;
22
- return <Link to={route(to, prms)} {...prps} />;
23
- }
@@ -1,12 +0,0 @@
1
- import { ReactElement } from "react";
2
- import { href } from "react-router-dom";
3
-
4
- export const route = <Page extends (...args: any[]) => ReactElement>(page: Page, params?: Page extends (...args: [infer Args]) => ReactElement ? Args : never): string => {
5
- if (!window) throw new Error("window object not found!");
6
- if (!("__ENHANCD_REACT_FILE_ROUTER__" in window)) throw new Error("enhancd react router not found! Make sure vite plugin enabled");
7
- if (!window.__ENHANCD_REACT_FILE_ROUTER__?.has(page)) throw new Error("Page not found!");
8
- const value = window.__ENHANCD_REACT_FILE_ROUTER__.get(page);
9
- if (!value) throw new Error("Path not setteled!");
10
- if (!params) return value;
11
- return href(value, params);
12
- }
@@ -1,5 +0,0 @@
1
-
2
- import { RouteObject } from "react-router-dom";
3
- import schema from "virtual:react-file-router-schema";
4
-
5
- export const routerSchema = schema as RouteObject[];
@@ -1,16 +0,0 @@
1
- import * as path from "path";
2
- import { reactFileRouterVitePlugin } from "../vitePlugin";
3
-
4
- describe("Vite plugin", () => {
5
- it("Init", () => {
6
-
7
- const routerPluginObj = reactFileRouterVitePlugin();
8
-
9
- expect(routerPluginObj.name).toEqual("enhancd-react-file-router");
10
- expect(routerPluginObj.enforce).toEqual("pre");
11
- expect(typeof routerPluginObj.configResolved).toBe("function");
12
- expect(typeof routerPluginObj.resolveId).toBe("function");
13
- expect(typeof routerPluginObj.load).toBe("function");
14
- });
15
-
16
- });
@@ -1,3 +0,0 @@
1
- export default function () {
2
- return "Default declaraction page";
3
- }
@@ -1,3 +0,0 @@
1
- export const LazyFallback = () => {
2
- return "Lazy loading...";
3
- };
@@ -1,3 +0,0 @@
1
- export const LazyLazyPage = () => {
2
- return "Lazy page"
3
- }
@@ -1,3 +0,0 @@
1
- export const Index404 = () => {
2
- return "404 page";
3
- }
@@ -1,3 +0,0 @@
1
- export const IndexError = () => {
2
- return "Error";
3
- }
@@ -1,10 +0,0 @@
1
- import { Outlet } from "react-router";
2
-
3
- export const IndexLayout = () => {
4
- return (
5
- <div>
6
- IndexLayout
7
- <Outlet />
8
- </div>
9
- )
10
- };
@@ -1,3 +0,0 @@
1
- export const IndexPage = () => {
2
- return "Index Page";
3
- }
@@ -1,125 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import * as crypto from "node:crypto";
4
-
5
- export type FolderSchema = {
6
- folderPath: string;
7
- files: {
8
- page?: {
9
- id: string;
10
- path: string;
11
- fileName: string;
12
- componentName: string;
13
- realtivePath: string;
14
- importString: string;
15
- export: "named" | "default";
16
- }
17
- "lazy.page"?: {
18
- id: string;
19
- path: string;
20
- fileName: string;
21
- componentName: string;
22
- realtivePath: string;
23
- importString: string;
24
- export: "named" | "default";
25
- },
26
- layout?: {
27
- id: string;
28
- path: string;
29
- fileName: string;
30
- componentName: string;
31
- importString: string;
32
- export: "named" | "default";
33
- },
34
- error?: {
35
- id: string;
36
- path: string;
37
- fileName: string;
38
- componentName: string;
39
- importString: string;
40
- export: "named" | "default";
41
- },
42
- fallback?: {
43
- id: string;
44
- path: string;
45
- fileName: string;
46
- componentName: string;
47
- importString: string;
48
- export: "named" | "default";
49
- },
50
- "404"?: {
51
- id: string;
52
- path: string;
53
- fileName: string;
54
- componentName: string;
55
- importString: string;
56
- export: "named" | "default";
57
- },
58
- };
59
- subroutes: FolderSchema[]
60
- }
61
-
62
-
63
- const getFunctionExportType = (fnName: string, rawFile: string): "named" | "default" => {
64
- if (new RegExp(`export\\s+function\\s+${fnName}`).test(rawFile)) return "named";
65
- if (new RegExp(`export\\s+const\\s+${fnName}\\s*=\\s*\\(`).test(rawFile)) return "named";
66
- if (new RegExp(`const\\s+${fnName}\\s*=\\s*function`).test(rawFile) && new RegExp(`export\\s*{[A-Za-z0-9\\s,]*${rawFile}`).test(rawFile)) return "named";
67
- if (new RegExp(`const\\s+${fnName}\\s*=\\s*\\(`).test(rawFile) && new RegExp(`export\\s*{[A-Za-z0-9\\s,]*${rawFile}`).test(rawFile)) return "named";
68
-
69
- if (new RegExp(`export\\s+default\\s+function\\s+${fnName}`).test(rawFile)) return "default";
70
- if (new RegExp("export\\s+default\\s+function").test(rawFile)) return "default";
71
- if (new RegExp(`const\\s+${fnName}\\s*=\\s*function`).test(rawFile) && new RegExp(`export\\s+default\\s+${rawFile}`).test(rawFile)) return "default";
72
- if (new RegExp(`const\\s+${fnName}\\s*=\\s*\\(`).test(rawFile) && new RegExp(`export\\s+default\\s+${rawFile}`).test(rawFile)) return "default";
73
- if (new RegExp(`function\\s+${fnName}`).test(rawFile) && new RegExp(`export\\s+default\\s+${rawFile}`).test(rawFile)) return "default";
74
-
75
- return "default";
76
- };
77
-
78
- const fileNametoComponentName = (name: string) => {
79
- return name.split(".").slice(0, -1).map(e => `${e.charAt(0).toUpperCase()}${e.slice(1)}`).join("");
80
- };
81
-
82
- const fileImportString = (componentName: string, id: string, path: string, exportType: "named" | "default", lazy = false) => {
83
- if (lazy && exportType === "named") return `const Lazy${componentName} = React.lazy(() => import("${path}").then(module => { return { default: module.${componentName} } }));`;
84
- if (lazy && exportType === "default") return `const Lazy${componentName} = React.lazy(() => import("${path}"));`;
85
- if (exportType === "named") return `import { ${componentName} as ${id} } from "${path}";`;
86
- return `import ${id} from "${path}";`;
87
- };
88
-
89
- export const compileFolderToSchema = (folderPath: string, routerPath = folderPath): FolderSchema => {
90
- if (!fs.existsSync(folderPath)) throw new Error(`Failed to run react-file-router vite plugin. Folder "${folderPath}" doesn't exist`);
91
- const dirContent = fs.readdirSync(folderPath);
92
- const subroutes = [];
93
- const files: FolderSchema["files"] = { };
94
- for (const itemName of dirContent) {
95
- const itemPath = path.join(folderPath, itemName);
96
- const itemStat = fs.statSync(itemPath);
97
- if (itemStat.isDirectory() && (itemName.startsWith("@") || itemName.startsWith("$"))) subroutes.push(itemPath);
98
- if (!itemStat.isFile()) continue;
99
- const match = itemName.match(/\.(?<type>lazy.page|page|error|layout|fallback|404)\./);
100
- if (!match || !match.groups) continue;
101
- const type = match.groups.type as keyof FolderSchema["files"];
102
- const componentName = fileNametoComponentName(itemName);
103
- const rawFile = fs.readFileSync(itemPath, "utf-8");
104
- const exportType = "named" as const;
105
-
106
- const relativePath = path.relative(routerPath, folderPath).replace(/\\/g, "/").replace(/\\/g, "/").replace(/@/g, "/:").replace(/\$/g, "/").replaceAll(/\/+/g, "/") || "/";
107
- const itemSchema = { path: itemPath.replace(/\\/g, "/"), id: `A${crypto.createHash("sha256").update(`${relativePath}/${itemName}`, "utf-8").digest("hex")}`, fileName: itemName, componentName, realtivePath: relativePath, importString: "", export: exportType };
108
-
109
- itemSchema.importString = fileImportString(itemSchema.componentName, itemSchema.id, itemSchema.path, exportType);
110
- if (type === "lazy.page") {
111
- files["page"] = itemSchema;
112
- files["lazy.page"] = { ...itemSchema, path: `virtual:react-file-router/${itemSchema.id}.tsx` };
113
- files["lazy.page"].importString = fileImportString(files["lazy.page"].componentName, files["lazy.page"].id, files["lazy.page"].path, files["lazy.page"].export, true);
114
- } else {
115
- files[type] = itemSchema;
116
- }
117
- }
118
-
119
-
120
- return {
121
- folderPath,
122
- files,
123
- subroutes: subroutes.map(subroutePath => compileFolderToSchema(subroutePath, routerPath))
124
- };
125
- };
@@ -1,30 +0,0 @@
1
- import * as fs from "fs";
2
- import { FolderSchema } from "../compile-folder-to-schema/compileFolderToSchema";
3
-
4
- const generatePageFile = (file: FolderSchema["files"]) => {
5
- return `import * as React from "react";
6
- ${file["fallback"] ? file["fallback"].importString : ""}
7
- ${file["lazy.page"]?.importString}
8
-
9
- ${file["page"]?.export === "default" ? "export default" : "export"} const ${file.page?.componentName} = (args = {}) => {
10
- return React.createElement(
11
- React.Suspense,
12
- { fallback: React.createElement(${file["fallback"] ? file["fallback"].id : "() => null"}, null) },
13
- React.createElement(Lazy${file["page"]?.componentName}, { ...args })
14
- )
15
- }
16
-
17
- `;
18
- };
19
-
20
- export const compileLazyPages = (schema: FolderSchema, rootFolder: string): Record<string, { rawFile: string, lazy: boolean, linkedFile: string }> => {
21
- const out: Record<string, { rawFile: string, lazy: boolean, linkedFile: string }> = {};
22
- if (schema.files["lazy.page"] && schema.files["page"]) {
23
- const rawFile = fs.readFileSync(schema.files["page"].path, "utf-8");
24
- out[schema.files["lazy.page"].path] = { lazy: true, rawFile, linkedFile: schema.files["page"].path };
25
- out[schema.files["page"].path] = { lazy: false, rawFile: generatePageFile(schema.files), linkedFile: schema.files["lazy.page"].path };
26
- }
27
- return schema.subroutes.reduce((acc, el) => {
28
- return { ...acc, ...compileLazyPages(el, rootFolder) };
29
- }, out);
30
- };
@@ -1,64 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { FolderSchema } from "../compile-folder-to-schema/compileFolderToSchema";
4
- import { routerSchema } from "src/lib/reactFileRouter";
5
-
6
-
7
- const compileImports = (schema: FolderSchema, rootDir: string): string => {
8
- const out: string[] = [];
9
- const fileTypes = Object.keys(schema.files) as `${keyof FolderSchema["files"]}`[];
10
-
11
-
12
- fileTypes.forEach(fileType => {
13
- const file = schema.files[fileType];
14
- if (["lazy.page", "fallback"].includes(fileType) || !file?.importString) return;
15
- out.push(file?.importString);
16
- })
17
-
18
- return [...out, ...schema.subroutes.map(subroutSchema => compileImports(subroutSchema, rootDir))].join("\n").replace(/^\n/gm, "");
19
- }
20
-
21
-
22
- const compileRoutes = (schema: FolderSchema, rootFolder: string): typeof routerSchema => {
23
- if (!schema.files.page && !schema.files[404] && !schema.files.layout) return [];
24
-
25
- const element = schema.files.page ? schema.files.page.realtivePath.search(":") > 0 ? `React.createElement(() => React.createElement(${schema.files.page.id}, useParams()), null)` : `React.createElement(${schema.files.page.id}, null)` : "null";
26
- const notFoundPage = schema.files[404] ? { path: "*", element: `React.createElement(${schema.files[404].id}, null)` } : {};
27
- if (!schema.files.layout) {
28
- return [{
29
- path: schema.files.page?.realtivePath || "",
30
- errorElement: schema.files.error ? `React.createElement(${schema.files.error.id}, null)` : undefined,
31
- element,
32
- }, notFoundPage, ...schema.subroutes.map(subrouteSchema => compileRoutes(subrouteSchema, rootFolder)).flat(1)];
33
- }
34
- return [{
35
- path: schema.files.page?.realtivePath || "",
36
- element: `React.createElement(${schema.files.layout.id}, null)`,
37
- errorElement: schema.files.error ? `React.createElement(${schema.files.error.id}, null)` : undefined,
38
- children: [{ path: "", element }, notFoundPage, ...schema.subroutes.map(subrouteSchema => compileRoutes(subrouteSchema, rootFolder)).flat(1)]
39
- }]
40
- }
41
- const compilePathInjects = (schema: FolderSchema, rootFolder: string): string => {
42
- const out = [];
43
-
44
- if (schema.files.page) out.push(`window.__ENHANCD_REACT_FILE_ROUTER__.set(${schema.files.page.id}, "${schema.files.page.realtivePath}");`);
45
-
46
- return [...out, ...schema.subroutes.map(subroutSchema => compilePathInjects(subroutSchema, rootFolder))].join("\n").replace(/^\n/gm, "");
47
- }
48
-
49
- export const compileSchemaToRouterFile = (schema: FolderSchema, routerFolder: string, rootFolder: string): string => {
50
- if (!fs.existsSync(routerFolder)) throw new Error(`Failed to run react-file-router vite plugin. Folder "${routerFolder}" doesn't exist`);
51
-
52
- const imports = compileImports(schema, rootFolder);
53
- const routes = JSON.stringify(compileRoutes(schema, routerFolder), null, 2).replace(/"(React\.createElement\(.+, null\))"/g, "$1");
54
-
55
- return `import * as React from "react";
56
- import { useParams } from "react-router-dom"
57
- ${imports}
58
-
59
- export default ${routes}
60
-
61
- window.__ENHANCD_REACT_FILE_ROUTER__ = new Map();
62
- ${compilePathInjects(schema, rootFolder)}`;
63
-
64
- }
@@ -1,29 +0,0 @@
1
- import { transform } from "@babel/standalone";
2
-
3
- export const transpileLazyPage = async (filename: string, source: string, resolve: (idString: string) => Promise<{ id: string } | null>) => {
4
- const imports = Array.from(source.matchAll(/^\s*import.+"(?<id>.+)".+/mg));
5
- const resolvedImports = await Promise.all(imports.map(async match => {
6
- if (!match.groups?.id) return "";
7
- const resolvedId = (await resolve(match.groups.id))?.id;
8
-
9
- if (typeof resolvedId !== "string") return "";
10
- return match[0].replace(match[1], resolvedId);
11
- }));
12
- const lastImportelement = imports.at(-1);
13
- const codeWithoutImports = lastImportelement ? source.slice(lastImportelement.index + lastImportelement[0].length, -1) : source;
14
-
15
- const transpiledCode = transform(codeWithoutImports, {
16
- presets: [["react", {
17
- pragma: "React.createElement",
18
- pragmaFrag: "React.Fragment"
19
- }],
20
- "typescript"],
21
- filename,
22
- plugins: [["transform-react-jsx", { runtime: "automatic" }]]
23
- }).code;
24
-
25
- if (!transpiledCode) throw Error(`Babel could transpile file: ${filename}`);
26
-
27
-
28
- return resolvedImports.join("") + transpiledCode;
29
- };
@@ -1,69 +0,0 @@
1
- import * as path from "path";
2
- import * as fs from "fs";
3
- import { Plugin, ResolvedConfig, ViteDevServer } from "vite";
4
- import { compileFolderToSchema } from "./compile-folder-to-schema/compileFolderToSchema";
5
- import { compileSchemaToRouterFile } from "./compile-schema-to-router-file/compileSchemaToRouterFile";
6
- import { compileLazyPages } from "./compile-lazy-pages/compileLazyPages";
7
- import { transpileLazyPage } from "./transpile-lazy-page/transpile-lazy-page";
8
-
9
- export function reactFileRouterVitePlugin(params?: { rootDir?: string, routerDir?: string, workDir?: string }): Plugin {
10
- let routerFile: string;
11
- let lazyPagesMap: Record<string, { rawFile: string, lazy: boolean, linkedFile: string }>;
12
- let config: ResolvedConfig;
13
- let server: ViteDevServer;
14
-
15
- return {
16
- name: "enhancd-react-file-router",
17
- enforce: "pre" as const,
18
- async configResolved(cfg) {
19
- config = cfg;
20
- const rootDir = params?.rootDir ?? cfg.root;
21
- const routerDir = path.join(rootDir, params?.workDir ?? "src", params?.routerDir ?? "$router");
22
- if (!fs.existsSync(routerDir)) throw new Error(`Folder "${routerDir}" doesn't exist!`);
23
- const schema = compileFolderToSchema(routerDir);
24
- routerFile = compileSchemaToRouterFile(schema, routerDir, rootDir);
25
- lazyPagesMap = compileLazyPages(schema, rootDir);
26
- },
27
- configureServer(viteServer) {
28
- server = viteServer;
29
- },
30
- resolveId(id: string) {
31
- if (id.startsWith("virtual:react-file-router-schema")) return "\0" + id;
32
- if (lazyPagesMap[id] && lazyPagesMap[id].lazy) return "\0" + id;
33
- },
34
- async load(id: string) {
35
- if (id.includes("virtual:react-file-router-schema")) return routerFile;
36
- const pagePath = id.replace("\0", "");
37
- const page = lazyPagesMap[pagePath];
38
-
39
- if (page && page.lazy) {
40
- return transpileLazyPage(id, page.rawFile, (source: string) => this.resolve(source, page.linkedFile));
41
-
42
- }
43
- if (page && !page.lazy) return page.rawFile;
44
- },
45
- watchChange(id, change) {
46
- const rootDir = params?.rootDir ?? config.root;
47
- const routerDir = path.join(rootDir, params?.workDir ?? "src", params?.routerDir ?? "$router");
48
- const relPath = path.relative(routerDir, id);
49
-
50
- if (relPath.startsWith("..")) return;
51
- const schema = compileFolderToSchema(routerDir);
52
- routerFile = compileSchemaToRouterFile(schema, routerDir, rootDir);
53
- lazyPagesMap = compileLazyPages(schema, rootDir);
54
- const virtualModule = server.moduleGraph.getModuleById("\0virtual:react-file-router-schema");
55
- if (virtualModule) server.moduleGraph.invalidateModule(virtualModule);
56
- const module = lazyPagesMap[id]?.linkedFile ? server.moduleGraph.getModuleById(`\0${lazyPagesMap[id]?.linkedFile}`) : server.moduleGraph.getModuleById(`\0${id}`);
57
- if (module) {
58
- server.moduleGraph.invalidateModule(module);
59
- module.importers.forEach(importer => {
60
- server.moduleGraph.invalidateModule(importer);
61
- });
62
- }
63
- server.ws.send({
64
- type: "full-reload",
65
- path: "*"
66
- });
67
- }
68
- };
69
- }
@@ -1,14 +0,0 @@
1
-
2
- type RouteSchema = Array<{
3
- path: string;
4
- element?: React.ReactNode;
5
- children: RouteSchema[];
6
- }>;
7
-
8
- declare module "virtual:react-file-router-schema" {
9
- export default schema as RouteSchema;
10
- }
11
-
12
- declare const window: {
13
- __ENHANCD_REACT_FILE_ROUTER__?: Map<object, string>;
14
- }
package/tsconfig.json DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "files": [],
4
- "include": [],
5
- "references": [
6
- {
7
- "path": "./tsconfig.lib.json"
8
- },
9
- {
10
- "path": "./tsconfig.spec.json"
11
- }
12
- ]
13
- }
package/tsconfig.lib.json DELETED
@@ -1,32 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "baseUrl": ".",
5
- "rootDir": "src",
6
- "outDir": "dist",
7
- "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
8
- "emitDeclarationOnly": true,
9
- "module": "esnext",
10
- "jsx": "react-jsx",
11
- "noEmit": false,
12
- "moduleResolution": "bundler",
13
- "forceConsistentCasingInFileNames": true,
14
- "types": ["node"]
15
- },
16
- "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts"],
17
- "references": [],
18
- "exclude": [
19
- "vite.config.ts",
20
- "vite.config.mts",
21
- "vitest.config.ts",
22
- "vitest.config.mts",
23
- "src/**/*.test.ts",
24
- "src/**/*.spec.ts",
25
- "src/**/*.test.tsx",
26
- "src/**/*.spec.tsx",
27
- "src/**/*.test.js",
28
- "src/**/*.spec.js",
29
- "src/**/*.test.jsx",
30
- "src/**/*.spec.jsx"
31
- ]
32
- }
@@ -1,37 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "outDir": "./out-tsc/vitest",
5
- "types": [
6
- "vitest/globals",
7
- "vitest/importMeta",
8
- "vite/client",
9
- "node",
10
- "vitest"
11
- ],
12
- "module": "esnext",
13
- "noEmit": false,
14
- "moduleResolution": "bundler",
15
- "forceConsistentCasingInFileNames": true
16
- },
17
- "include": [
18
- "vite.config.ts",
19
- "vite.config.mts",
20
- "vitest.config.ts",
21
- "vitest.config.mts",
22
- "src/**/*.test.ts",
23
- "src/**/*.spec.ts",
24
- "src/**/*.test.tsx",
25
- "src/**/*.spec.tsx",
26
- "src/**/*.test.js",
27
- "src/**/*.spec.js",
28
- "src/**/*.test.jsx",
29
- "src/**/*.spec.jsx",
30
- "src/**/*.d.ts"
31
- ],
32
- "references": [
33
- {
34
- "path": "./tsconfig.lib.json"
35
- }
36
- ]
37
- }