@gyoll/builder 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // code/scanner.js
26
+ var import_promises = __toESM(require("fs/promises"), 1);
27
+ var import_path = __toESM(require("path"), 1);
28
+ async function scanRoutes(routesDir) {
29
+ const routes = await walkDirectory(routesDir, routesDir);
30
+ return routes;
31
+ }
32
+ async function walkDirectory(dir, baseDir, segments = []) {
33
+ const entries = await import_promises.default.readdir(dir, { withFileTypes: true });
34
+ const files = entries.filter((e) => e.isFile());
35
+ const dirs = entries.filter((e) => e.isDirectory());
36
+ const routeFiles = collectRouteFiles(files, dir);
37
+ const routes = [];
38
+ if (routeFiles.page) {
39
+ const route = {
40
+ path: buildRoutePath(segments),
41
+ component: import_path.default.relative(baseDir, routeFiles.page)
42
+ };
43
+ if (routeFiles.layout) {
44
+ route.layout = import_path.default.relative(baseDir, routeFiles.layout);
45
+ }
46
+ if (routeFiles.error) {
47
+ route.error = import_path.default.relative(baseDir, routeFiles.error);
48
+ }
49
+ if (routeFiles.loading) {
50
+ route.loading = import_path.default.relative(baseDir, routeFiles.loading);
51
+ }
52
+ routes.push(route);
53
+ }
54
+ for (const subdir of dirs) {
55
+ const segment = parseSegment(subdir.name);
56
+ const subdirPath = import_path.default.join(dir, subdir.name);
57
+ const childRoutes = await walkDirectory(subdirPath, baseDir, [
58
+ ...segments,
59
+ segment
60
+ ]);
61
+ routes.push(...childRoutes);
62
+ }
63
+ return routes;
64
+ }
65
+ function collectRouteFiles(files, dir) {
66
+ const routeFiles = {
67
+ page: null,
68
+ layout: null,
69
+ error: null,
70
+ loading: null
71
+ };
72
+ for (const file of files) {
73
+ const name = file.name;
74
+ const fullPath = import_path.default.join(dir, name);
75
+ if (/^page\.(jsx?|tsx?)$/.test(name)) {
76
+ routeFiles.page = fullPath;
77
+ } else if (/^layout\.(jsx?|tsx?)$/.test(name)) {
78
+ routeFiles.layout = fullPath;
79
+ } else if (/^error\.(jsx?|tsx?)$/.test(name)) {
80
+ routeFiles.error = fullPath;
81
+ } else if (/^loading\.(jsx?|tsx?)$/.test(name)) {
82
+ routeFiles.loading = fullPath;
83
+ }
84
+ }
85
+ return routeFiles;
86
+ }
87
+ function parseSegment(segment) {
88
+ if (/^\(.+\)$/.test(segment)) {
89
+ return null;
90
+ }
91
+ if (/^\[\.\.\.(.+)\]$/.test(segment)) {
92
+ return "*";
93
+ }
94
+ if (/^\[(.+)\]$/.test(segment)) {
95
+ const param = segment.slice(1, -1);
96
+ return `:${param}`;
97
+ }
98
+ return segment;
99
+ }
100
+ function buildRoutePath(segments) {
101
+ const filtered = segments.filter((s) => s !== null);
102
+ if (filtered.length === 0) {
103
+ return "/";
104
+ }
105
+ return "/" + filtered.join("/");
106
+ }
107
+ async function generateManifest(routes, outFile) {
108
+ const content = `// Auto-generated by @gyoll/file-routes - DO NOT EDIT
109
+ export const routes = ${JSON.stringify(routes, null, 2).replace(/"component": "(.+?)"/g, '"component": () => import("./$1")').replace(/"layout": "(.+?)"/g, '"layout": () => import("./$1")').replace(/"error": "(.+?)"/g, '"error": () => import("./$1")').replace(/"loading": "(.+?)"/g, '"loading": () => import("./$1")')};
110
+ `;
111
+ await import_promises.default.writeFile(outFile, content, "utf-8");
112
+ }
113
+
114
+ // code/index.js
115
+ var import_fs = __toESM(require("fs"), 1);
116
+ var import_path2 = __toESM(require("path"), 1);
117
+ var VERSION = true ? "dev" : "dev";
118
+ var args = process.argv.slice(2);
119
+ var command = args[0];
120
+ function parseArgs(args2) {
121
+ const options2 = {
122
+ dir: "./src/routes",
123
+ out: "./src/gyoll-manifest.js",
124
+ version: false
125
+ };
126
+ for (let i = 0; i < args2.length; i++) {
127
+ if (args2[i] === "--dir" || args2[i] === "-d") {
128
+ options2.dir = args2[++i];
129
+ } else if (args2[i] === "--out" || args2[i] === "-o") {
130
+ options2.out = args2[++i];
131
+ } else if (args2[i] === "--version" || args2[i] === "-v") {
132
+ options2.version = true;
133
+ }
134
+ }
135
+ return options2;
136
+ }
137
+ async function build(options2) {
138
+ const routesDir = import_path2.default.resolve(options2.dir);
139
+ const outFile = import_path2.default.resolve(options2.out);
140
+ console.log(`Scanning routes in ${routesDir}...`);
141
+ const routes = await scanRoutes(routesDir);
142
+ await generateManifest(routes, outFile);
143
+ console.log(`\u2713 Generated ${outFile}`);
144
+ }
145
+ async function watch(options2) {
146
+ const routesDir = import_path2.default.resolve(options2.dir);
147
+ await build(options2);
148
+ console.log(`Watching ${routesDir} for changes...`);
149
+ import_fs.default.watch(routesDir, { recursive: true }, async () => {
150
+ await build(options2);
151
+ });
152
+ }
153
+ var options = parseArgs(args);
154
+ if (command === "build") {
155
+ build(options);
156
+ } else if (command === "watch") {
157
+ watch(options);
158
+ } else if (command && command[0] === "-" && options.version) {
159
+ console.log(`@gyoll/builder ${VERSION}`);
160
+ } else {
161
+ console.error(
162
+ "Usage: builder <build|watch> [--version|-v] [--dir <path>] [--out <path>]"
163
+ );
164
+ process.exit(1);
165
+ }
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+
3
+ // code/scanner.js
4
+ import fs from "fs/promises";
5
+ import path from "path";
6
+ async function scanRoutes(routesDir) {
7
+ const routes = await walkDirectory(routesDir, routesDir);
8
+ return routes;
9
+ }
10
+ async function walkDirectory(dir, baseDir, segments = []) {
11
+ const entries = await fs.readdir(dir, { withFileTypes: true });
12
+ const files = entries.filter((e) => e.isFile());
13
+ const dirs = entries.filter((e) => e.isDirectory());
14
+ const routeFiles = collectRouteFiles(files, dir);
15
+ const routes = [];
16
+ if (routeFiles.page) {
17
+ const route = {
18
+ path: buildRoutePath(segments),
19
+ component: path.relative(baseDir, routeFiles.page)
20
+ };
21
+ if (routeFiles.layout) {
22
+ route.layout = path.relative(baseDir, routeFiles.layout);
23
+ }
24
+ if (routeFiles.error) {
25
+ route.error = path.relative(baseDir, routeFiles.error);
26
+ }
27
+ if (routeFiles.loading) {
28
+ route.loading = path.relative(baseDir, routeFiles.loading);
29
+ }
30
+ routes.push(route);
31
+ }
32
+ for (const subdir of dirs) {
33
+ const segment = parseSegment(subdir.name);
34
+ const subdirPath = path.join(dir, subdir.name);
35
+ const childRoutes = await walkDirectory(subdirPath, baseDir, [
36
+ ...segments,
37
+ segment
38
+ ]);
39
+ routes.push(...childRoutes);
40
+ }
41
+ return routes;
42
+ }
43
+ function collectRouteFiles(files, dir) {
44
+ const routeFiles = {
45
+ page: null,
46
+ layout: null,
47
+ error: null,
48
+ loading: null
49
+ };
50
+ for (const file of files) {
51
+ const name = file.name;
52
+ const fullPath = path.join(dir, name);
53
+ if (/^page\.(jsx?|tsx?)$/.test(name)) {
54
+ routeFiles.page = fullPath;
55
+ } else if (/^layout\.(jsx?|tsx?)$/.test(name)) {
56
+ routeFiles.layout = fullPath;
57
+ } else if (/^error\.(jsx?|tsx?)$/.test(name)) {
58
+ routeFiles.error = fullPath;
59
+ } else if (/^loading\.(jsx?|tsx?)$/.test(name)) {
60
+ routeFiles.loading = fullPath;
61
+ }
62
+ }
63
+ return routeFiles;
64
+ }
65
+ function parseSegment(segment) {
66
+ if (/^\(.+\)$/.test(segment)) {
67
+ return null;
68
+ }
69
+ if (/^\[\.\.\.(.+)\]$/.test(segment)) {
70
+ return "*";
71
+ }
72
+ if (/^\[(.+)\]$/.test(segment)) {
73
+ const param = segment.slice(1, -1);
74
+ return `:${param}`;
75
+ }
76
+ return segment;
77
+ }
78
+ function buildRoutePath(segments) {
79
+ const filtered = segments.filter((s) => s !== null);
80
+ if (filtered.length === 0) {
81
+ return "/";
82
+ }
83
+ return "/" + filtered.join("/");
84
+ }
85
+ async function generateManifest(routes, outFile) {
86
+ const content = `// Auto-generated by @gyoll/file-routes - DO NOT EDIT
87
+ export const routes = ${JSON.stringify(routes, null, 2).replace(/"component": "(.+?)"/g, '"component": () => import("./$1")').replace(/"layout": "(.+?)"/g, '"layout": () => import("./$1")').replace(/"error": "(.+?)"/g, '"error": () => import("./$1")').replace(/"loading": "(.+?)"/g, '"loading": () => import("./$1")')};
88
+ `;
89
+ await fs.writeFile(outFile, content, "utf-8");
90
+ }
91
+
92
+ // code/index.js
93
+ import fs2 from "fs";
94
+ import path2 from "path";
95
+ var VERSION = true ? "dev" : "dev";
96
+ var args = process.argv.slice(2);
97
+ var command = args[0];
98
+ function parseArgs(args2) {
99
+ const options2 = {
100
+ dir: "./src/routes",
101
+ out: "./src/gyoll-manifest.js",
102
+ version: false
103
+ };
104
+ for (let i = 0; i < args2.length; i++) {
105
+ if (args2[i] === "--dir" || args2[i] === "-d") {
106
+ options2.dir = args2[++i];
107
+ } else if (args2[i] === "--out" || args2[i] === "-o") {
108
+ options2.out = args2[++i];
109
+ } else if (args2[i] === "--version" || args2[i] === "-v") {
110
+ options2.version = true;
111
+ }
112
+ }
113
+ return options2;
114
+ }
115
+ async function build(options2) {
116
+ const routesDir = path2.resolve(options2.dir);
117
+ const outFile = path2.resolve(options2.out);
118
+ console.log(`Scanning routes in ${routesDir}...`);
119
+ const routes = await scanRoutes(routesDir);
120
+ await generateManifest(routes, outFile);
121
+ console.log(`\u2713 Generated ${outFile}`);
122
+ }
123
+ async function watch(options2) {
124
+ const routesDir = path2.resolve(options2.dir);
125
+ await build(options2);
126
+ console.log(`Watching ${routesDir} for changes...`);
127
+ fs2.watch(routesDir, { recursive: true }, async () => {
128
+ await build(options2);
129
+ });
130
+ }
131
+ var options = parseArgs(args);
132
+ if (command === "build") {
133
+ build(options);
134
+ } else if (command === "watch") {
135
+ watch(options);
136
+ } else if (command && command[0] === "-" && options.version) {
137
+ console.log(`@gyoll/builder ${VERSION}`);
138
+ } else {
139
+ console.error(
140
+ "Usage: builder <build|watch> [--version|-v] [--dir <path>] [--out <path>]"
141
+ );
142
+ process.exit(1);
143
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@gyoll/builder",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "CLI tool for generating route manifests from file-system structure",
6
+ "bin": {
7
+ "gyoll": "./build-output/builder.cjs"
8
+ },
9
+ "main": "./build-output/builder.cjs",
10
+ "files": [
11
+ "build-output/builder.js",
12
+ "build-output/builder.cjs"
13
+ ],
14
+ "keywords": [
15
+ "routing",
16
+ "file-based-routing",
17
+ "cli",
18
+ "spa"
19
+ ],
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://gitlab.com/scottlindeman/public.git",
23
+ "directory": "01_code/gyoll/builder"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "scripts": {
29
+ "build": "node build.js",
30
+ "test:unit": "vitest run --dir tests/unit",
31
+ "test:integration": "vitest run --dir tests/integration"
32
+ }
33
+ }