@granite-js/plugin-router 0.0.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,41 @@
1
+ # @granite-js/plugin-router
2
+
3
+ ## 0.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - ed4d356: changeset
8
+ - Updated dependencies [ed4d356]
9
+ - @granite-js/plugin-core@0.0.3
10
+
11
+ ## 0.0.2
12
+
13
+ ### Patch Changes
14
+
15
+ - 0ae09b7: deploy guide
16
+ - 0ae09b7: type fix
17
+ - 0ae09b7: guide
18
+ - 0ae09b7: fix comment
19
+ - 0ae09b7: showcase
20
+ - 0ae09b7: refactor interface names
21
+ - 0ae09b7: plugin-router
22
+ - 0ae09b7: CanGoBackGuard 수정, typecheck fix, lint fix
23
+ - 0ae09b7: counter
24
+ - 0ae09b7: licenses
25
+ - Updated dependencies [0ae09b7]
26
+ - Updated dependencies [0ae09b7]
27
+ - Updated dependencies [0ae09b7]
28
+ - Updated dependencies [0ae09b7]
29
+ - Updated dependencies [0ae09b7]
30
+ - Updated dependencies [0ae09b7]
31
+ - Updated dependencies [0ae09b7]
32
+ - Updated dependencies [0ae09b7]
33
+ - Updated dependencies [0ae09b7]
34
+ - Updated dependencies [0ae09b7]
35
+ - @granite-js/plugin-core@0.0.2
36
+
37
+ ## 0.0.1
38
+
39
+ ### Patch Changes
40
+
41
+ - f47ca39: first release
package/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # @granite-js/plugin-router
2
+
3
+ A Route Generator for Granite project
4
+
package/dist/index.cjs ADDED
@@ -0,0 +1,278 @@
1
+ //#region rolldown:runtime
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") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ const fs = __toESM(require("fs"));
25
+ const path = __toESM(require("path"));
26
+ const __swc_core = __toESM(require("@swc/core"));
27
+ const es_toolkit = __toESM(require("es-toolkit"));
28
+ const fs_promises = __toESM(require("fs/promises"));
29
+ const chokidar = __toESM(require("chokidar"));
30
+
31
+ //#region src/checkExportRoute.ts
32
+ /**
33
+ * Checks if Route is exported from the file.
34
+ */
35
+ function checkExportRoute(path$1) {
36
+ try {
37
+ const ast = (0, __swc_core.parseFileSync)(path$1, {
38
+ syntax: "typescript",
39
+ tsx: true
40
+ });
41
+ const hasExportSpecifiers = ast.body.some((node) => {
42
+ if (node.type !== "ExportNamedDeclaration") return false;
43
+ return node.specifiers?.some((specifier) => {
44
+ if (specifier.type !== "ExportSpecifier") return false;
45
+ return specifier.orig?.value === "Route";
46
+ });
47
+ });
48
+ if (hasExportSpecifiers) return true;
49
+ const hasExportNamedVariable = ast.body.some((node) => {
50
+ if (node.type !== "ExportDeclaration") return false;
51
+ if (node.declaration.type !== "VariableDeclaration") return false;
52
+ return node.declaration.declarations.some((declaration) => declaration.id.type === "Identifier" && declaration.id.value === "Route");
53
+ });
54
+ return hasExportNamedVariable;
55
+ } catch {
56
+ return false;
57
+ }
58
+ }
59
+
60
+ //#endregion
61
+ //#region src/getComponentName.ts
62
+ function getComponentName(filePath) {
63
+ const path$1 = filePath.replace(/^pages\//, "").replace(/\.(tsx|ts)$/, "");
64
+ const segments = path$1.split("/").filter(Boolean);
65
+ if (segments[segments.length - 1] === "index") segments.pop();
66
+ const componentName = segments.map((segment) => (0, es_toolkit.pascalCase)(segment)).join("");
67
+ return componentName || "Index";
68
+ }
69
+
70
+ //#endregion
71
+ //#region src/getPath.ts
72
+ function getPath(filePath) {
73
+ let path$1 = filePath.replace(/^pages\//, "").replace(/\.[^/.]+$/, "");
74
+ if (path$1.endsWith("/index")) path$1 = path$1.replace(/\/index$/, "");
75
+ else if (path$1 === "index") path$1 = "";
76
+ return `/${path$1}`;
77
+ }
78
+
79
+ //#endregion
80
+ //#region src/template.ts
81
+ const NEW_ROUTE_FILE_TEMPLATE = `import React from 'react';
82
+ import { Text, View } from 'react-native';
83
+ import { createRoute } from '@granite-js/react-native';
84
+
85
+ export const Route = createRoute('%%path%%', {
86
+ validateParams: (params) => params,
87
+ component: %%componentName%%,
88
+ });
89
+
90
+ function %%componentName%%() {
91
+ return (
92
+ <View>
93
+ <Text>Hello %%componentName%%</Text>
94
+ </View>
95
+ );
96
+ }
97
+ `;
98
+ const ROUTER_GEN_TEMPLATE = `/* eslint-disable */
99
+ // This file is auto-generated by @granite-js/react-native. DO NOT EDIT.
100
+ %%pageImports%%
101
+
102
+ declare module '@granite-js/react-native' {
103
+ interface RegisterScreen {
104
+ %%pageRoutes%%
105
+ }
106
+ }
107
+ `;
108
+ const NEW_LAYOUT_FILE_TEMPLATE = `import React, { PropsWithChildren } from 'react';
109
+
110
+ export default function %%componentName%%Layout({ children }: PropsWithChildren) {
111
+ return (
112
+ <>
113
+ {children}
114
+ </>
115
+ );
116
+ }
117
+ `;
118
+
119
+ //#endregion
120
+ //#region src/utils/transformTemplate.ts
121
+ /**
122
+ * 템플릿 문자열에서 %%key%% 형식의 플레이스홀더를 values 객체의 값으로 대체합니다.
123
+ * 제네릭 타입 T를 통해 템플릿 문자열의 플레이스홀더 키를 자동으로 추론하여 타입 안정성을 보장합니다.
124
+ *
125
+ * @example
126
+ * const str = "안녕하세요 %%name%%님, 당신의 나이는 %%age%%살 입니다."
127
+ * const result = transformTemplate(str, { name: "홍길동", age: "20" })
128
+ * // 결과: "안녕하세요 홍길동님, 당신의 나이는 20살 입니다."
129
+ */
130
+ function transformTemplate(templateString, values) {
131
+ let result = templateString;
132
+ for (const key in values) {
133
+ const placeholder = `%%${key}%%`;
134
+ result = result.replace(new RegExp(placeholder, "g"), values[key]);
135
+ }
136
+ return result;
137
+ }
138
+
139
+ //#endregion
140
+ //#region src/generateRouterFile.ts
141
+ function generateRouterFile() {
142
+ const cwd = process.cwd();
143
+ function getPageFiles(dir, prefix = "") {
144
+ const files = (0, fs.readdirSync)((0, path.join)(cwd, dir), { withFileTypes: true });
145
+ return files.reduce((acc, file) => {
146
+ if (file.isDirectory()) return [...acc, ...getPageFiles(`${dir}/${file.name}`, `${prefix}${file.name}/`)];
147
+ if (file.name.endsWith(".tsx") || file.name.endsWith(".ts")) {
148
+ const name = (0, path.parse)(file.name).name;
149
+ const ext = (0, path.parse)(file.name).ext;
150
+ return [...acc, `${prefix}${name}${ext}`];
151
+ }
152
+ return acc;
153
+ }, []);
154
+ }
155
+ const allPages = getPageFiles("pages");
156
+ const exportRouteMap = new Map(allPages.map((page) => [page, checkExportRoute((0, path.join)(cwd, "pages", page))]));
157
+ const pageFiles = allPages.filter((page) => !page.startsWith("_") && exportRouteMap.get(page));
158
+ const pageImports = pageFiles.map((page) => {
159
+ const componentName = getComponentName(page);
160
+ const pagePath = getPath(page);
161
+ return transformTemplate("import { Route as _%%componentName%%Route } from '../pages%%pagePath%%';", {
162
+ componentName,
163
+ pagePath
164
+ });
165
+ }).join("\n");
166
+ const pageRoutes = pageFiles.map((page) => {
167
+ const componentName = getComponentName(page);
168
+ const pagePath = getPath(page);
169
+ return transformTemplate(" '%%pagePath%%': ReturnType<typeof _%%componentName%%Route.useParams>;", {
170
+ componentName,
171
+ pagePath
172
+ });
173
+ }).join("\n");
174
+ const generatedContent = transformTemplate(ROUTER_GEN_TEMPLATE, {
175
+ pageImports,
176
+ pageRoutes
177
+ });
178
+ const routerFilePath = (0, path.join)(cwd, "src", "router.gen.ts");
179
+ if ((0, fs.existsSync)(routerFilePath)) {
180
+ const existingContent = (0, fs.readFileSync)(routerFilePath, "utf-8");
181
+ if (existingContent === generatedContent) return;
182
+ }
183
+ (0, fs.writeFileSync)(routerFilePath, generatedContent);
184
+ console.log("✅ Router file generated successfully!");
185
+ }
186
+
187
+ //#endregion
188
+ //#region src/getPageName.ts
189
+ function getPageName(filePath) {
190
+ const parts = filePath.split("/");
191
+ const indexOfPages = parts.indexOf("pages");
192
+ if (indexOfPages === -1) return "";
193
+ const relevantParts = parts.slice(indexOfPages + 1);
194
+ if (relevantParts.at(-1)?.startsWith("_")) relevantParts.pop();
195
+ const lastPart = relevantParts.at(-1);
196
+ if (!lastPart) return "";
197
+ return (0, es_toolkit.pascalCase)(lastPart);
198
+ }
199
+
200
+ //#endregion
201
+ //#region src/transformNewRouteFile.ts
202
+ async function transformNewRouteFile(path$1) {
203
+ return transformTemplate(NEW_ROUTE_FILE_TEMPLATE, {
204
+ path: getPath(path$1),
205
+ componentName: getComponentName(path$1)
206
+ });
207
+ }
208
+ async function transformNewLayoutFile(path$1) {
209
+ return transformTemplate(NEW_LAYOUT_FILE_TEMPLATE, { componentName: getPageName(path$1) });
210
+ }
211
+
212
+ //#endregion
213
+ //#region src/watchRouter.ts
214
+ function watchRouter() {
215
+ const watcher = chokidar.default.watch("./pages", {
216
+ ignored: (path$1, stats) => {
217
+ return Boolean(stats?.isFile() && !path$1.endsWith(".ts") && !path$1.endsWith(".tsx"));
218
+ },
219
+ ignoreInitial: true,
220
+ persistent: true,
221
+ cwd: process.cwd()
222
+ });
223
+ const handleAdd = async (path$1) => {
224
+ const file = (0, path.join)(process.cwd(), path$1);
225
+ const code = await (0, fs_promises.readFile)(file, "utf8");
226
+ if (code !== "") return;
227
+ const filename = (0, path.parse)(path$1).name;
228
+ if (filename.startsWith("_")) switch (filename) {
229
+ case "_layout":
230
+ console.log("👀 Layout file has been added");
231
+ await (0, fs_promises.writeFile)(path$1, await transformNewLayoutFile(path$1));
232
+ return;
233
+ default: return;
234
+ }
235
+ const componentName = (0, es_toolkit.kebabCase)(filename);
236
+ if (componentName !== filename) {
237
+ console.log(`❌ File name should be in kebab-case format. Would you like to rename ${filename} to ${componentName}?`);
238
+ return;
239
+ }
240
+ console.log(`👀 File ${path$1} has been added`);
241
+ await (0, fs_promises.writeFile)(path$1, await transformNewRouteFile(path$1));
242
+ await generateRouterFile();
243
+ };
244
+ watcher.on("add", handleAdd);
245
+ watcher.on("change", generateRouterFile);
246
+ watcher.on("unlink", generateRouterFile);
247
+ return () => {
248
+ watcher.off("add", handleAdd);
249
+ watcher.off("change", generateRouterFile);
250
+ watcher.off("unlink", generateRouterFile);
251
+ watcher.close();
252
+ };
253
+ }
254
+
255
+ //#endregion
256
+ //#region src/routerPlugin.ts
257
+ const DEFAULT_OPTIONS = { watch: true };
258
+ const router = (options = DEFAULT_OPTIONS) => {
259
+ return {
260
+ name: "router-plugin",
261
+ build: {
262
+ order: "pre",
263
+ handler: () => {
264
+ generateRouterFile();
265
+ }
266
+ },
267
+ dev: {
268
+ order: "pre",
269
+ handler: () => {
270
+ generateRouterFile();
271
+ if (options.watch) watchRouter();
272
+ }
273
+ }
274
+ };
275
+ };
276
+
277
+ //#endregion
278
+ exports.router = router;
@@ -0,0 +1,10 @@
1
+ import { GranitePluginCore } from "@granite-js/plugin-core";
2
+
3
+ //#region src/routerPlugin.d.ts
4
+ interface RouterPluginOptions {
5
+ watch?: boolean;
6
+ }
7
+ declare const router: (options?: RouterPluginOptions) => GranitePluginCore;
8
+
9
+ //#endregion
10
+ export { router };
@@ -0,0 +1,10 @@
1
+ import { GranitePluginCore } from "@granite-js/plugin-core";
2
+
3
+ //#region src/routerPlugin.d.ts
4
+ interface RouterPluginOptions {
5
+ watch?: boolean;
6
+ }
7
+ declare const router: (options?: RouterPluginOptions) => GranitePluginCore;
8
+
9
+ //#endregion
10
+ export { router };
package/dist/index.js ADDED
@@ -0,0 +1,255 @@
1
+ import { existsSync, readFileSync, readdirSync, writeFileSync } from "fs";
2
+ import { join, parse } from "path";
3
+ import { parseFileSync } from "@swc/core";
4
+ import { kebabCase, pascalCase } from "es-toolkit";
5
+ import { readFile, writeFile } from "fs/promises";
6
+ import chokidar from "chokidar";
7
+
8
+ //#region src/checkExportRoute.ts
9
+ /**
10
+ * Checks if Route is exported from the file.
11
+ */
12
+ function checkExportRoute(path) {
13
+ try {
14
+ const ast = parseFileSync(path, {
15
+ syntax: "typescript",
16
+ tsx: true
17
+ });
18
+ const hasExportSpecifiers = ast.body.some((node) => {
19
+ if (node.type !== "ExportNamedDeclaration") return false;
20
+ return node.specifiers?.some((specifier) => {
21
+ if (specifier.type !== "ExportSpecifier") return false;
22
+ return specifier.orig?.value === "Route";
23
+ });
24
+ });
25
+ if (hasExportSpecifiers) return true;
26
+ const hasExportNamedVariable = ast.body.some((node) => {
27
+ if (node.type !== "ExportDeclaration") return false;
28
+ if (node.declaration.type !== "VariableDeclaration") return false;
29
+ return node.declaration.declarations.some((declaration) => declaration.id.type === "Identifier" && declaration.id.value === "Route");
30
+ });
31
+ return hasExportNamedVariable;
32
+ } catch {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ //#endregion
38
+ //#region src/getComponentName.ts
39
+ function getComponentName(filePath) {
40
+ const path = filePath.replace(/^pages\//, "").replace(/\.(tsx|ts)$/, "");
41
+ const segments = path.split("/").filter(Boolean);
42
+ if (segments[segments.length - 1] === "index") segments.pop();
43
+ const componentName = segments.map((segment) => pascalCase(segment)).join("");
44
+ return componentName || "Index";
45
+ }
46
+
47
+ //#endregion
48
+ //#region src/getPath.ts
49
+ function getPath(filePath) {
50
+ let path = filePath.replace(/^pages\//, "").replace(/\.[^/.]+$/, "");
51
+ if (path.endsWith("/index")) path = path.replace(/\/index$/, "");
52
+ else if (path === "index") path = "";
53
+ return `/${path}`;
54
+ }
55
+
56
+ //#endregion
57
+ //#region src/template.ts
58
+ const NEW_ROUTE_FILE_TEMPLATE = `import React from 'react';
59
+ import { Text, View } from 'react-native';
60
+ import { createRoute } from '@granite-js/react-native';
61
+
62
+ export const Route = createRoute('%%path%%', {
63
+ validateParams: (params) => params,
64
+ component: %%componentName%%,
65
+ });
66
+
67
+ function %%componentName%%() {
68
+ return (
69
+ <View>
70
+ <Text>Hello %%componentName%%</Text>
71
+ </View>
72
+ );
73
+ }
74
+ `;
75
+ const ROUTER_GEN_TEMPLATE = `/* eslint-disable */
76
+ // This file is auto-generated by @granite-js/react-native. DO NOT EDIT.
77
+ %%pageImports%%
78
+
79
+ declare module '@granite-js/react-native' {
80
+ interface RegisterScreen {
81
+ %%pageRoutes%%
82
+ }
83
+ }
84
+ `;
85
+ const NEW_LAYOUT_FILE_TEMPLATE = `import React, { PropsWithChildren } from 'react';
86
+
87
+ export default function %%componentName%%Layout({ children }: PropsWithChildren) {
88
+ return (
89
+ <>
90
+ {children}
91
+ </>
92
+ );
93
+ }
94
+ `;
95
+
96
+ //#endregion
97
+ //#region src/utils/transformTemplate.ts
98
+ /**
99
+ * 템플릿 문자열에서 %%key%% 형식의 플레이스홀더를 values 객체의 값으로 대체합니다.
100
+ * 제네릭 타입 T를 통해 템플릿 문자열의 플레이스홀더 키를 자동으로 추론하여 타입 안정성을 보장합니다.
101
+ *
102
+ * @example
103
+ * const str = "안녕하세요 %%name%%님, 당신의 나이는 %%age%%살 입니다."
104
+ * const result = transformTemplate(str, { name: "홍길동", age: "20" })
105
+ * // 결과: "안녕하세요 홍길동님, 당신의 나이는 20살 입니다."
106
+ */
107
+ function transformTemplate(templateString, values) {
108
+ let result = templateString;
109
+ for (const key in values) {
110
+ const placeholder = `%%${key}%%`;
111
+ result = result.replace(new RegExp(placeholder, "g"), values[key]);
112
+ }
113
+ return result;
114
+ }
115
+
116
+ //#endregion
117
+ //#region src/generateRouterFile.ts
118
+ function generateRouterFile() {
119
+ const cwd = process.cwd();
120
+ function getPageFiles(dir, prefix = "") {
121
+ const files = readdirSync(join(cwd, dir), { withFileTypes: true });
122
+ return files.reduce((acc, file) => {
123
+ if (file.isDirectory()) return [...acc, ...getPageFiles(`${dir}/${file.name}`, `${prefix}${file.name}/`)];
124
+ if (file.name.endsWith(".tsx") || file.name.endsWith(".ts")) {
125
+ const name = parse(file.name).name;
126
+ const ext = parse(file.name).ext;
127
+ return [...acc, `${prefix}${name}${ext}`];
128
+ }
129
+ return acc;
130
+ }, []);
131
+ }
132
+ const allPages = getPageFiles("pages");
133
+ const exportRouteMap = new Map(allPages.map((page) => [page, checkExportRoute(join(cwd, "pages", page))]));
134
+ const pageFiles = allPages.filter((page) => !page.startsWith("_") && exportRouteMap.get(page));
135
+ const pageImports = pageFiles.map((page) => {
136
+ const componentName = getComponentName(page);
137
+ const pagePath = getPath(page);
138
+ return transformTemplate("import { Route as _%%componentName%%Route } from '../pages%%pagePath%%';", {
139
+ componentName,
140
+ pagePath
141
+ });
142
+ }).join("\n");
143
+ const pageRoutes = pageFiles.map((page) => {
144
+ const componentName = getComponentName(page);
145
+ const pagePath = getPath(page);
146
+ return transformTemplate(" '%%pagePath%%': ReturnType<typeof _%%componentName%%Route.useParams>;", {
147
+ componentName,
148
+ pagePath
149
+ });
150
+ }).join("\n");
151
+ const generatedContent = transformTemplate(ROUTER_GEN_TEMPLATE, {
152
+ pageImports,
153
+ pageRoutes
154
+ });
155
+ const routerFilePath = join(cwd, "src", "router.gen.ts");
156
+ if (existsSync(routerFilePath)) {
157
+ const existingContent = readFileSync(routerFilePath, "utf-8");
158
+ if (existingContent === generatedContent) return;
159
+ }
160
+ writeFileSync(routerFilePath, generatedContent);
161
+ console.log("✅ Router file generated successfully!");
162
+ }
163
+
164
+ //#endregion
165
+ //#region src/getPageName.ts
166
+ function getPageName(filePath) {
167
+ const parts = filePath.split("/");
168
+ const indexOfPages = parts.indexOf("pages");
169
+ if (indexOfPages === -1) return "";
170
+ const relevantParts = parts.slice(indexOfPages + 1);
171
+ if (relevantParts.at(-1)?.startsWith("_")) relevantParts.pop();
172
+ const lastPart = relevantParts.at(-1);
173
+ if (!lastPart) return "";
174
+ return pascalCase(lastPart);
175
+ }
176
+
177
+ //#endregion
178
+ //#region src/transformNewRouteFile.ts
179
+ async function transformNewRouteFile(path) {
180
+ return transformTemplate(NEW_ROUTE_FILE_TEMPLATE, {
181
+ path: getPath(path),
182
+ componentName: getComponentName(path)
183
+ });
184
+ }
185
+ async function transformNewLayoutFile(path) {
186
+ return transformTemplate(NEW_LAYOUT_FILE_TEMPLATE, { componentName: getPageName(path) });
187
+ }
188
+
189
+ //#endregion
190
+ //#region src/watchRouter.ts
191
+ function watchRouter() {
192
+ const watcher = chokidar.watch("./pages", {
193
+ ignored: (path, stats) => {
194
+ return Boolean(stats?.isFile() && !path.endsWith(".ts") && !path.endsWith(".tsx"));
195
+ },
196
+ ignoreInitial: true,
197
+ persistent: true,
198
+ cwd: process.cwd()
199
+ });
200
+ const handleAdd = async (path) => {
201
+ const file = join(process.cwd(), path);
202
+ const code = await readFile(file, "utf8");
203
+ if (code !== "") return;
204
+ const filename = parse(path).name;
205
+ if (filename.startsWith("_")) switch (filename) {
206
+ case "_layout":
207
+ console.log("👀 Layout file has been added");
208
+ await writeFile(path, await transformNewLayoutFile(path));
209
+ return;
210
+ default: return;
211
+ }
212
+ const componentName = kebabCase(filename);
213
+ if (componentName !== filename) {
214
+ console.log(`❌ File name should be in kebab-case format. Would you like to rename ${filename} to ${componentName}?`);
215
+ return;
216
+ }
217
+ console.log(`👀 File ${path} has been added`);
218
+ await writeFile(path, await transformNewRouteFile(path));
219
+ await generateRouterFile();
220
+ };
221
+ watcher.on("add", handleAdd);
222
+ watcher.on("change", generateRouterFile);
223
+ watcher.on("unlink", generateRouterFile);
224
+ return () => {
225
+ watcher.off("add", handleAdd);
226
+ watcher.off("change", generateRouterFile);
227
+ watcher.off("unlink", generateRouterFile);
228
+ watcher.close();
229
+ };
230
+ }
231
+
232
+ //#endregion
233
+ //#region src/routerPlugin.ts
234
+ const DEFAULT_OPTIONS = { watch: true };
235
+ const router = (options = DEFAULT_OPTIONS) => {
236
+ return {
237
+ name: "router-plugin",
238
+ build: {
239
+ order: "pre",
240
+ handler: () => {
241
+ generateRouterFile();
242
+ }
243
+ },
244
+ dev: {
245
+ order: "pre",
246
+ handler: () => {
247
+ generateRouterFile();
248
+ if (options.watch) watchRouter();
249
+ }
250
+ }
251
+ };
252
+ };
253
+
254
+ //#endregion
255
+ export { router };
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@granite-js/plugin-router",
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "description": "A Route Generator for Granite project",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "scripts": {
22
+ "prepack": "yarn build",
23
+ "typecheck": "tsc --noEmit",
24
+ "lint": "eslint .",
25
+ "test": "vitest --no-watch",
26
+ "build": "tsdown"
27
+ },
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "devDependencies": {
32
+ "@types/node": "^22.10.2",
33
+ "tsdown": "^0.11.12",
34
+ "typescript": "5.8.3",
35
+ "vitest": "^2.1.8"
36
+ },
37
+ "dependencies": {
38
+ "@granite-js/plugin-core": "0.0.1",
39
+ "@swc/core": "1.5.24",
40
+ "chokidar": "4.0.1",
41
+ "es-toolkit": "^1.26.1"
42
+ },
43
+ "sideEffects": false
44
+ }