@nooh-ts/compiler 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,193 @@
1
+ //#region src/types.d.ts
2
+ declare const ROUTE_METHODS: readonly ["get", "post", "put", "patch", "delete", "options", "head", "all"];
3
+ type RouteMethod = (typeof ROUTE_METHODS)[number];
4
+ interface SourceFile {
5
+ readonly content: string;
6
+ readonly path: string;
7
+ }
8
+ interface SourceSnapshot {
9
+ readonly files: readonly SourceFile[];
10
+ }
11
+ interface ModuleLoader {
12
+ loadDefault: (modulePath: string) => Promise<unknown>;
13
+ }
14
+ interface CompileOptions {
15
+ readonly outputRoot?: string;
16
+ }
17
+ interface CompileInput {
18
+ readonly config: string;
19
+ readonly loader: ModuleLoader;
20
+ readonly options?: CompileOptions;
21
+ readonly root?: string;
22
+ readonly sources: SourceSnapshot;
23
+ }
24
+ interface RuntimeConfig {
25
+ readonly routes?: string | undefined;
26
+ }
27
+ interface LoadedConfig {
28
+ readonly root: string;
29
+ readonly routesRoot: string;
30
+ readonly source: string;
31
+ readonly value: RuntimeConfig;
32
+ }
33
+ interface ConfigLoadResult {
34
+ readonly config?: LoadedConfig;
35
+ readonly diagnostics: readonly Diagnostic[];
36
+ }
37
+ interface DiscoveredEndpoint {
38
+ readonly endpointsRoot: string;
39
+ readonly groupPath: string;
40
+ readonly localPath: string;
41
+ readonly source: string;
42
+ }
43
+ interface DiscoveredGroup {
44
+ readonly groupPath: string;
45
+ readonly source: string;
46
+ }
47
+ interface DiscoveredProject {
48
+ readonly config: LoadedConfig;
49
+ readonly endpoints: readonly DiscoveredEndpoint[];
50
+ readonly groups: readonly DiscoveredGroup[];
51
+ }
52
+ type RouteSegment = {
53
+ readonly kind: "static";
54
+ readonly value: string;
55
+ } | {
56
+ readonly kind: "param";
57
+ readonly name: string;
58
+ } | {
59
+ readonly kind: "splat";
60
+ readonly name?: string;
61
+ };
62
+ interface ParsedRoute {
63
+ readonly groupPath: string;
64
+ readonly method: RouteMethod;
65
+ readonly rawSegments: readonly string[];
66
+ readonly segments: readonly RouteSegment[];
67
+ readonly source: string;
68
+ }
69
+ interface ParsedGroup {
70
+ readonly groupPath: string;
71
+ readonly source: string;
72
+ }
73
+ interface ParsedProject {
74
+ readonly diagnostics: readonly Diagnostic[];
75
+ readonly groups: readonly ParsedGroup[];
76
+ readonly routes: readonly ParsedRoute[];
77
+ }
78
+ interface RouteModel {
79
+ readonly fullPath: string;
80
+ readonly groupPath: string;
81
+ readonly id: string;
82
+ readonly localPath: string;
83
+ readonly method: RouteMethod;
84
+ readonly routerPath: string;
85
+ readonly routeSegments: readonly RouteSegment[];
86
+ readonly source: string;
87
+ }
88
+ interface RouteGroup {
89
+ readonly children: readonly string[];
90
+ readonly configSource?: string;
91
+ readonly id: string;
92
+ readonly parentId?: string;
93
+ readonly path: string;
94
+ readonly routes: readonly string[];
95
+ }
96
+ interface ProjectModel {
97
+ readonly config: LoadedConfig;
98
+ readonly groups: readonly RouteGroup[];
99
+ readonly routes: readonly RouteModel[];
100
+ }
101
+ type DiagnosticSeverity = "error" | "warning" | "info";
102
+ interface Diagnostic {
103
+ readonly code: string;
104
+ readonly file?: string;
105
+ readonly message: string;
106
+ readonly severity: DiagnosticSeverity;
107
+ }
108
+ type ModuleKind = "types" | "router" | "middleware" | "group" | "app";
109
+ interface ModulePlan {
110
+ readonly groupId?: string;
111
+ readonly id: string;
112
+ readonly kind: ModuleKind;
113
+ readonly routeId?: string;
114
+ }
115
+ interface CompilationPlan {
116
+ readonly modules: readonly ModulePlan[];
117
+ readonly outputRoot: string;
118
+ }
119
+ interface GeneratedModule {
120
+ readonly code: string;
121
+ readonly id: string;
122
+ readonly kind: ModuleKind;
123
+ }
124
+ interface GeneratedOutput {
125
+ readonly modules: readonly GeneratedModule[];
126
+ }
127
+ interface Compilation {
128
+ readonly diagnostics: readonly Diagnostic[];
129
+ readonly model: ProjectModel;
130
+ readonly output: GeneratedOutput | null;
131
+ readonly plan: CompilationPlan | null;
132
+ }
133
+ interface NoohCompiler {
134
+ analyze: (parsed: ParsedProject, config: LoadedConfig) => {
135
+ model: ProjectModel;
136
+ diagnostics: readonly Diagnostic[];
137
+ };
138
+ compile: (input: CompileInput) => Promise<Compilation>;
139
+ discover: (sources: CompileInput["sources"], config: LoadedConfig) => DiscoveredProject;
140
+ generate: (plan: CompilationPlan, model: ProjectModel) => GeneratedOutput;
141
+ loadConfig: (input: CompileInput) => Promise<ConfigLoadResult>;
142
+ parse: (project: DiscoveredProject) => ParsedProject;
143
+ plan: (model: ProjectModel, outputRoot?: string) => CompilationPlan;
144
+ }
145
+ interface OutputDiff {
146
+ readonly added: readonly GeneratedModule[];
147
+ readonly changed: readonly GeneratedModule[];
148
+ readonly removed: readonly string[];
149
+ readonly unchanged: readonly GeneratedModule[];
150
+ }
151
+ interface RecompileInput {
152
+ readonly config?: string;
153
+ readonly loader: CompileInput["loader"];
154
+ readonly options?: CompileInput["options"];
155
+ readonly previous: Compilation;
156
+ readonly snapshot: SourceSnapshot;
157
+ }
158
+ //#endregion
159
+ //#region src/compile.d.ts
160
+ export declare const compile: (input: CompileInput) => Promise<Compilation>;
161
+ //#endregion
162
+ //#region src/compiler.d.ts
163
+ export declare const createCompiler: () => NoohCompiler;
164
+ //#endregion
165
+ //#region src/diff.d.ts
166
+ export declare const diff: (previous: GeneratedOutput, next: GeneratedOutput) => OutputDiff;
167
+ //#endregion
168
+ //#region src/generate/index.d.ts
169
+ export declare const generate: (plan: CompilationPlan, model: ProjectModel) => GeneratedOutput;
170
+ //#endregion
171
+ //#region src/pipeline/analyze.d.ts
172
+ export declare const analyze: (parsed: ParsedProject, config: LoadedConfig) => {
173
+ model: ProjectModel;
174
+ diagnostics: readonly Diagnostic[];
175
+ };
176
+ //#endregion
177
+ //#region src/pipeline/config.d.ts
178
+ export declare const loadConfig: (input: CompileInput) => Promise<ConfigLoadResult>;
179
+ //#endregion
180
+ //#region src/pipeline/discover.d.ts
181
+ export declare const discover: (snapshot: SourceSnapshot, config: LoadedConfig) => DiscoveredProject;
182
+ //#endregion
183
+ //#region src/pipeline/parse.d.ts
184
+ export declare const parse: (project: DiscoveredProject) => ParsedProject;
185
+ //#endregion
186
+ //#region src/pipeline/plan.d.ts
187
+ export declare const plan: (model: ProjectModel, outputRoot?: string) => CompilationPlan;
188
+ //#endregion
189
+ //#region src/recompile.d.ts
190
+ export declare const recompile: (input: RecompileInput) => Promise<Compilation>;
191
+ //#endregion
192
+ export type { Compilation, CompilationPlan, CompileInput, CompileOptions, ConfigLoadResult, Diagnostic, DiscoveredEndpoint, DiscoveredProject, GeneratedModule, GeneratedOutput, LoadedConfig, ModuleKind, ModuleLoader, ModulePlan, NoohCompiler, OutputDiff, ParsedProject, ParsedRoute, ProjectModel, RecompileInput, RouteGroup, RouteMethod, RouteModel, RouteSegment, RuntimeConfig, SourceFile, SourceSnapshot };
193
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/compile.ts","../src/compiler.ts","../src/diff.ts","../src/generate/index.ts","../src/pipeline/analyze.ts","../src/pipeline/config.ts","../src/pipeline/discover.ts","../src/pipeline/parse.ts","../src/pipeline/plan.ts","../src/recompile.ts"],"mappings":";cAAa;KAWD,sBAAsB;UAEjB;WACN;WACA;;UAGM;WACN,gBAAgB;;UAGV;EACf,cAAc,uBAAuB;;UAGtB;WACN;;UAGM;WACN;WACA,QAAQ;WACR,UAAU;WACV;WACA,SAAS;;UAGH;WACN;;UAGM;WACN;WACA;WACA;WACA,OAAO;;UAGD;WACN,SAAS;WACT,sBAAsB;;UAGhB;WACN;WACA;WACA;WACA;;UAGM;WACN;WACA;;UAGM;WACN,QAAQ;WACR,oBAAoB;WACpB,iBAAiB;;KAGhB;WAEG;WACA;;WAGA;WACA;;WAGA;WACA;;UAGE;WACN;WACA,QAAQ;WACR;WACA,mBAAmB;WACnB;;UAGM;WACN;WACA;;UAGM;WACN,sBAAsB;WACtB,iBAAiB;WACjB,iBAAiB;;UAGX;WACN;WAEA;WACA;WAEA;WAEA,QAAQ;WAER;WACA,wBAAwB;WACxB;;UAGM;WACN;WACA;WACA;WACA;WACA;WACA;;UAGM;WACN,QAAQ;WACR,iBAAiB;WACjB,iBAAiB;;KAGhB;UAEK;WACN;WACA;WACA;WACA,UAAU;;KAGT;UAEK;WACN;WACA;WACA,MAAM;WACN;;UAGM;WACN,kBAAkB;WAClB;;UAGM;WACN;WACA;WACA,MAAM;;UAGA;WACN,kBAAkB;;UAGZ;WACN,sBAAsB;WACtB,OAAO;WACP,QAAQ;WACR,MAAM;;UAGA;EACf,UACE,QAAQ,eACR,QAAQ;IAER,OAAO;IACP,sBAAsB;;EAExB,UAAU,OAAO,iBAAiB,QAAQ;EAC1C,WACE,SAAS,yBACT,QAAQ,iBACL;EACL,WAAW,MAAM,iBAAiB,OAAO,iBAAiB;EAC1D,aAAa,OAAO,iBAAiB,QAAQ;EAC7C,QAAQ,SAAS,sBAAsB;EACvC,OAAO,OAAO,cAAc,wBAAwB;;UAGrC;WACN,gBAAgB;WAChB,kBAAkB;WAClB;WACA,oBAAoB;;UAGd;WACN;WACA,QAAQ;WACR,UAAU;WACV,UAAU;WACV,UAAU;;;;qBC3MR,UAAW,OAAO,iBAAe,QAAQ;;;qBCOzC,sBAAqB;;;qBCRrB,OACX,UAAU,iBACV,MAAM,oBACL;;;qBCQU,WACX,MAAM,iBACN,OAAO,iBACN;;;qBC4MU,UACX,QAAQ,eACR,QAAQ;EAER,OAAO;EACP,sBAAsB;;;;qBCvNX,aACX,OAAO,iBACN,QAAQ;;;qBC8GE,WACX,UAAU,gBACV,QAAQ,iBACP;;;qBCpHU,QAAS,SAAS,sBAAoB;;;qBCDtC,OACX,OAAO,cACP,wBACC;;;qBCRU,YAAa,OAAO,mBAAiB,QAAQ"}
package/dist/index.mjs ADDED
@@ -0,0 +1,853 @@
1
+ //#region src/generate/utils.ts
2
+ /**
3
+ * Returns the output file path for a route group module.
4
+ * The root group uses the reserved name "root".
5
+ */
6
+ const groupModuleId = (plan, groupId) => {
7
+ if (groupId === "root") return `${plan.outputRoot}/groups/root.ts`;
8
+ return `${plan.outputRoot}/groups/${groupId}.ts`;
9
+ };
10
+ //#endregion
11
+ //#region src/utils/path.ts
12
+ const WINDOWS_DRIVE_REGEX = /^[A-Za-z]:\//;
13
+ const DRIVE_REGEX = /^([A-Za-z]):\//;
14
+ const isAbsolutePath = (value) => {
15
+ const normalized = value.replaceAll("\\", "/");
16
+ return normalized.startsWith("/") || WINDOWS_DRIVE_REGEX.test(normalized);
17
+ };
18
+ const normalizePath = (value) => {
19
+ const normalized = value.replaceAll("\\", "/");
20
+ const isPosixAbsolute = normalized.startsWith("/");
21
+ const driveMatch = normalized.match(DRIVE_REGEX);
22
+ const parts = (driveMatch ? normalized.slice(3) : isPosixAbsolute ? normalized.slice(1) : normalized).split("/");
23
+ const result = [];
24
+ for (const part of parts) {
25
+ if (!part || part === ".") continue;
26
+ if (part === "..") {
27
+ if (result.length > 0 && result.at(-1) !== "..") result.pop();
28
+ else if (!(isPosixAbsolute || driveMatch)) result.push("..");
29
+ continue;
30
+ }
31
+ result.push(part);
32
+ }
33
+ const joined = result.join("/");
34
+ if (driveMatch) return joined ? `${driveMatch[1]}:/${joined}` : `${driveMatch[1]}:/`;
35
+ if (isPosixAbsolute) return joined ? `/${joined}` : "/";
36
+ return joined;
37
+ };
38
+ const ensureLeadingSlash = (value) => {
39
+ if (!value) return "/";
40
+ return value.startsWith("/") ? value : `/${value}`;
41
+ };
42
+ const dirname = (value) => {
43
+ const normalized = normalizePath(value);
44
+ const index = normalized.lastIndexOf("/");
45
+ if (index === -1) return "";
46
+ if (index === 0) return "/";
47
+ return normalized.slice(0, index);
48
+ };
49
+ const relativePath = (from, to) => {
50
+ const fromParts = normalizePath(from).split("/").filter(Boolean);
51
+ const toParts = normalizePath(to).split("/").filter(Boolean);
52
+ let common = 0;
53
+ while (common < fromParts.length && common < toParts.length && fromParts[common] === toParts[common]) common += 1;
54
+ return [...fromParts.slice(common).map(() => ".."), ...toParts.slice(common)].join("/");
55
+ };
56
+ const toProjectPath = (value, root) => {
57
+ const normalizedValue = normalizePath(value);
58
+ const normalizedRoot = normalizePath(root);
59
+ if (!(normalizedRoot && isAbsolutePath(normalizedRoot))) return normalizedValue;
60
+ if (!isAbsolutePath(normalizedValue)) return normalizedValue;
61
+ if (!isPathInside(normalizedValue, normalizedRoot)) return normalizedValue;
62
+ return relativePath(normalizedRoot, normalizedValue);
63
+ };
64
+ const relativeModuleSpecifier = (fromModule, toSource) => {
65
+ const fromDirectory = dirname(fromModule);
66
+ let target = normalizePath(toSource);
67
+ if (target.endsWith(".ts")) target = `${target.slice(0, -3)}.js`;
68
+ else if (target.endsWith(".tsx")) target = `${target.slice(0, -4)}.js`;
69
+ const relative = relativePath(fromDirectory, target);
70
+ return relative.startsWith(".") ? relative : `./${relative}`;
71
+ };
72
+ const isPathInside = (file, root) => {
73
+ const normalizedFile = normalizePath(file);
74
+ const normalizedRoot = normalizePath(root);
75
+ if (normalizedFile === normalizedRoot) return true;
76
+ return normalizedFile.startsWith(`${normalizedRoot}/`);
77
+ };
78
+ //#endregion
79
+ //#region src/generate/app.ts
80
+ const generateAppModule = (plan, model) => {
81
+ const moduleId = `${plan.outputRoot}/app.ts`;
82
+ const typesModuleId = `${plan.outputRoot}/types.ts`;
83
+ const root = model.groups.find((group) => group.id === "root");
84
+ if (!root) throw new Error("Nooh compilation requires a root route group.");
85
+ return {
86
+ code: [
87
+ ...[
88
+ `import { Hono } from "hono";`,
89
+ `import type { App } from ${JSON.stringify(relativeModuleSpecifier(moduleId, typesModuleId))};`,
90
+ `import root from ${JSON.stringify(relativeModuleSpecifier(moduleId, groupModuleId(plan, root.id)))};`
91
+ ],
92
+ "",
93
+ "const app = new Hono<App>();",
94
+ "",
95
+ "app.route(\"/\", root);",
96
+ "",
97
+ "export type AppType = typeof app;",
98
+ "",
99
+ "export default app;",
100
+ ""
101
+ ].join("\n"),
102
+ id: moduleId,
103
+ kind: "app"
104
+ };
105
+ };
106
+ //#endregion
107
+ //#region src/generate/group.ts
108
+ const getGroupRoutes = (model, group) => {
109
+ const ids = new Set(group.routes);
110
+ return model.routes.filter((route) => ids.has(route.id)).sort((a, b) => {
111
+ const pathDifference = a.localPath.localeCompare(b.localPath);
112
+ if (pathDifference !== 0) return pathDifference;
113
+ const methodDifference = a.method.localeCompare(b.method);
114
+ if (methodDifference !== 0) return methodDifference;
115
+ return a.source.localeCompare(b.source);
116
+ });
117
+ };
118
+ const capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
119
+ const getGroup = (model, id) => model.groups.find((group) => group.id === id);
120
+ const getChildPath = (parent, child) => {
121
+ if (parent.path === "/") return child.path;
122
+ const prefix = `${parent.path}/`;
123
+ if (!child.path.startsWith(prefix)) throw new Error(`Invalid group tree: "${child.path}" is not a child of "${parent.path}".`);
124
+ return child.path.slice(prefix.length);
125
+ };
126
+ const generateGroupModule = (plan, model, group) => {
127
+ const moduleId = groupModuleId(plan, group.id);
128
+ const typesModuleId = `${plan.outputRoot}/types.ts`;
129
+ const routes = getGroupRoutes(model, group);
130
+ const children = group.children.map((childId) => getGroup(model, childId)).filter((child) => child !== void 0).sort((a, b) => a.path.localeCompare(b.path));
131
+ const imports = [`import { Hono } from "hono";`, `import type { App } from ${JSON.stringify(relativeModuleSpecifier(moduleId, typesModuleId))};`];
132
+ if (group.configSource) imports.push(`import groupConfig from ${JSON.stringify(relativeModuleSpecifier(moduleId, group.configSource))};`);
133
+ children.forEach((child, index) => {
134
+ imports.push(`import child${index} from ${JSON.stringify(relativeModuleSpecifier(moduleId, groupModuleId(plan, child.id)))};`);
135
+ });
136
+ routes.forEach((route, index) => {
137
+ imports.push(`import endpoint${index} from ${JSON.stringify(relativeModuleSpecifier(moduleId, route.source))};`);
138
+ });
139
+ const methods = [...new Set(routes.map((route) => route.method))].sort();
140
+ const registerTypes = [
141
+ "type RouteRegister = (",
142
+ " path: string,",
143
+ " ...handlers: any[]",
144
+ ") => typeof route;"
145
+ ];
146
+ const registerDeclarations = methods.map((method) => {
147
+ return `const ${`register${capitalize(method)}`} = route.${method} as unknown as RouteRegister;`;
148
+ });
149
+ const useDeclaration = group.configSource ? [
150
+ "type RouteUse = (",
151
+ " path: string,",
152
+ " ...handlers: any[]",
153
+ ") => typeof route;",
154
+ "",
155
+ "const use = route.use as unknown as RouteUse;",
156
+ "",
157
+ "use(\"*\", ...(groupConfig.middleware ?? []));"
158
+ ] : [];
159
+ const registrations = routes.map((route, index) => {
160
+ return ` ${`register${capitalize(route.method)}`}(${JSON.stringify(ensureLeadingSlash(route.localPath))}, ...endpoint${index});`;
161
+ });
162
+ const childRegistrations = children.map((child, index) => ` route.route(${JSON.stringify(getChildPath(group, child))}, child${index});`);
163
+ return {
164
+ code: [
165
+ ...imports,
166
+ "",
167
+ "const route = new Hono<App>();",
168
+ "",
169
+ ...registerTypes,
170
+ "",
171
+ ...registerDeclarations,
172
+ ...useDeclaration.length > 0 ? ["", ...useDeclaration] : [],
173
+ ...registrations.length > 0 ? ["", ...registrations] : [],
174
+ ...childRegistrations.length > 0 ? ["", ...childRegistrations] : [],
175
+ "",
176
+ "export default route;",
177
+ ""
178
+ ].join("\n"),
179
+ id: moduleId,
180
+ kind: "group"
181
+ };
182
+ };
183
+ //#endregion
184
+ //#region src/generate/middleware.ts
185
+ const generateMiddlewareModule = (plan) => {
186
+ const moduleId = `${plan.outputRoot}/router/middleware.ts`;
187
+ return {
188
+ code: [
189
+ `import { createMiddleware } from "hono/factory";`,
190
+ `import type { App } from "../types.js";`,
191
+ "",
192
+ "export const middleware = createMiddleware<App>;",
193
+ ""
194
+ ].join("\n"),
195
+ id: moduleId,
196
+ kind: "middleware"
197
+ };
198
+ };
199
+ //#endregion
200
+ //#region src/generate/router.ts
201
+ const VALIDATION_TARGETS = [
202
+ "json",
203
+ "form",
204
+ "query",
205
+ "param",
206
+ "header",
207
+ "cookie"
208
+ ];
209
+ const METHOD_FUNCTION_NAMES = {
210
+ all: "all",
211
+ delete: "del",
212
+ get: "get",
213
+ head: "head",
214
+ options: "options",
215
+ patch: "patch",
216
+ post: "post",
217
+ put: "put"
218
+ };
219
+ const getRoutesForRouter = (model, routerPath) => model.routes.filter((route) => route.routerPath === routerPath).sort((a, b) => a.method.localeCompare(b.method));
220
+ const renderMethod = (route) => {
221
+ const { method } = route;
222
+ const functionName = METHOD_FUNCTION_NAMES[method];
223
+ return [
224
+ `type Path = ${JSON.stringify(ensureLeadingSlash(route.localPath))};`,
225
+ "",
226
+ "type RouteMiddleware = MiddlewareHandler<App, Path>;",
227
+ "",
228
+ "type ValidationTarget = Parameters<typeof sValidator>[0];",
229
+ "type StandardSchema = Parameters<typeof sValidator>[1];",
230
+ "",
231
+ "type ValidationOptions = Partial<",
232
+ " Record<ValidationTarget, StandardSchema>",
233
+ ">;",
234
+ "",
235
+ "type HandlerInput<T> = T extends Handler<",
236
+ " any,",
237
+ " any,",
238
+ " infer I,",
239
+ " any",
240
+ "> ? I : never;",
241
+ "",
242
+ "type ValidationHandler<",
243
+ " Target extends ValidationTarget,",
244
+ " Schema extends StandardSchema,",
245
+ "> = ReturnType<",
246
+ " typeof sValidator<Schema, Target, App, Path>",
247
+ ">;",
248
+ "",
249
+ "type ValidationInput<V extends ValidationOptions> =",
250
+ ...VALIDATION_TARGETS.map((target) => ` & (${JSON.stringify(target)} extends keyof V ? HandlerInput<ValidationHandler<${JSON.stringify(target)}, NonNullable<V[${JSON.stringify(target)}]>>> : {})`),
251
+ ";",
252
+ "",
253
+ "type RouteHandler = Handler<App, Path, any, any>;",
254
+ "",
255
+ "type EndpointOptions<",
256
+ " M extends readonly RouteMiddleware[],",
257
+ " V extends ValidationOptions,",
258
+ " H extends Handler<App, Path, ValidationInput<V>>,",
259
+ "> = {",
260
+ " readonly middleware?: M;",
261
+ " readonly validation?: V;",
262
+ " readonly handler: H;",
263
+ "};",
264
+ "",
265
+ `export function ${functionName}<H extends Handler<App, Path>>(`,
266
+ " handler: H,",
267
+ "): readonly RouteHandler[];",
268
+ "",
269
+ `export function ${functionName}<`,
270
+ " M extends readonly RouteMiddleware[],",
271
+ " V extends ValidationOptions,",
272
+ " H extends Handler<App, Path, ValidationInput<V>>,",
273
+ ">(",
274
+ " options: EndpointOptions<M, V, H>,",
275
+ "): readonly RouteHandler[];",
276
+ "",
277
+ `export function ${functionName}<`,
278
+ " M extends readonly RouteMiddleware[],",
279
+ " V extends ValidationOptions,",
280
+ " H extends Handler<App, Path, ValidationInput<V>>,",
281
+ ">(",
282
+ " input:",
283
+ " | H",
284
+ " | EndpointOptions<M, V, H>,",
285
+ "): readonly RouteHandler[] {",
286
+ " if (typeof input === \"function\") {",
287
+ " return [input as RouteHandler];",
288
+ " }",
289
+ "",
290
+ " return [",
291
+ " ...(input.middleware ?? []) as readonly RouteHandler[],",
292
+ ...VALIDATION_TARGETS.map((target) => ` ...(input.validation?.${target} !== undefined ? [sValidator(${JSON.stringify(target)}, input.validation.${target}) as RouteHandler] : []),`),
293
+ " input.handler as RouteHandler,",
294
+ " ];",
295
+ "}",
296
+ ""
297
+ ].join("\n");
298
+ };
299
+ const generateRouterModule = (plan, model, routerPath) => {
300
+ const routes = getRoutesForRouter(model, routerPath);
301
+ const moduleId = `${plan.outputRoot}/${routerPath}.ts`;
302
+ const typesModuleId = `${plan.outputRoot}/types.ts`;
303
+ return {
304
+ code: [
305
+ `import { sValidator } from "@hono/standard-validator";`,
306
+ `import type { Handler, MiddlewareHandler } from "hono";`,
307
+ `import type { App } from ${JSON.stringify(relativeModuleSpecifier(moduleId, typesModuleId))};`,
308
+ "",
309
+ ...routes.map(renderMethod)
310
+ ].join("\n"),
311
+ id: moduleId,
312
+ kind: "router"
313
+ };
314
+ };
315
+ //#endregion
316
+ //#region src/generate/types.ts
317
+ const generateTypesModule = (plan, config) => {
318
+ const moduleId = `${plan.outputRoot}/types.ts`;
319
+ return {
320
+ code: [
321
+ `import type config from "${relativeModuleSpecifier(moduleId, config.source)}";`,
322
+ "",
323
+ "type ExtractEnvironment<T> = T extends {",
324
+ " readonly __nooh_env: infer Environment;",
325
+ "}",
326
+ " ? Environment",
327
+ " : never;",
328
+ "",
329
+ "export type App = ExtractEnvironment<typeof config>;",
330
+ ""
331
+ ].join("\n"),
332
+ id: moduleId,
333
+ kind: "types"
334
+ };
335
+ };
336
+ //#endregion
337
+ //#region src/generate/index.ts
338
+ const generate = (plan, model) => {
339
+ const modules = [];
340
+ modules.push(generateTypesModule(plan, model.config));
341
+ modules.push(generateMiddlewareModule(plan));
342
+ const routerPaths = [...new Set(model.routes.map((route) => route.routerPath))].sort();
343
+ for (const routerPath of routerPaths) modules.push(generateRouterModule(plan, model, routerPath));
344
+ for (const group of model.groups) modules.push(generateGroupModule(plan, model, group));
345
+ modules.push(generateAppModule(plan, model));
346
+ modules.sort((a, b) => a.id.localeCompare(b.id));
347
+ return { modules };
348
+ };
349
+ //#endregion
350
+ //#region src/pipeline/analyze.ts
351
+ const segmentToHono = (segment) => {
352
+ switch (segment.kind) {
353
+ case "static": return segment.value;
354
+ case "param": return `:${segment.name}`;
355
+ case "splat": return "*";
356
+ }
357
+ };
358
+ const segmentsToPath = (segments) => {
359
+ if (segments.length === 0) return "";
360
+ return segments.map(segmentToHono).join("/");
361
+ };
362
+ const combinePaths = (groupPath, localPath) => {
363
+ const group = normalizePath(groupPath).replace(/^\/+|\/+$/g, "");
364
+ const local = normalizePath(localPath).replace(/^\/+|\/+$/g, "");
365
+ if (!(group || local)) return "/";
366
+ if (!group) return `/${local}`;
367
+ if (!local) return `/${group}`;
368
+ return `/${group}/${local}`;
369
+ };
370
+ const routeSegmentsToRouterPath = (groupPath, rawSegments) => {
371
+ const parts = [...groupPath.split("/").filter(Boolean), ...rawSegments.filter(Boolean)];
372
+ return parts.length === 0 ? "router/index" : `router/${parts.join("/")}`;
373
+ };
374
+ const routeId = (route) => `${route.method.toUpperCase()} ${combinePaths(route.groupPath, segmentsToPath(route.segments))}`;
375
+ const sortRoutes = (a, b) => {
376
+ const pathDifference = a.fullPath.localeCompare(b.fullPath);
377
+ if (pathDifference !== 0) return pathDifference;
378
+ const methodDifference = a.method.localeCompare(b.method);
379
+ if (methodDifference !== 0) return methodDifference;
380
+ return a.source.localeCompare(b.source);
381
+ };
382
+ const parentGroupPath = (groupPath) => {
383
+ const normalized = normalizePath(groupPath);
384
+ if (!normalized) return null;
385
+ const parts = normalized.split("/").filter(Boolean);
386
+ if (parts.length <= 1) return "";
387
+ return parts.slice(0, -1).join("/");
388
+ };
389
+ const getAncestorGroupPaths = (groupPath) => {
390
+ const parts = normalizePath(groupPath).split("/").filter(Boolean);
391
+ return Array.from({ length: parts.length + 1 }, (_, index) => parts.slice(0, index).join("/"));
392
+ };
393
+ const groupId = (path) => path || "root";
394
+ const buildGroups = (parsed, routeModels, diagnostics) => {
395
+ const configSources = /* @__PURE__ */ new Map();
396
+ for (const group of parsed.groups) {
397
+ const path = normalizePath(group.groupPath);
398
+ const previous = configSources.get(path);
399
+ if (previous) {
400
+ diagnostics.push({
401
+ code: "NOOH006",
402
+ file: group.source,
403
+ message: [
404
+ `Duplicate group definition for "${path || "/"}".`,
405
+ "",
406
+ `First declaration: ${previous}`,
407
+ `Second declaration: ${group.source}`
408
+ ].join("\n"),
409
+ severity: "error"
410
+ });
411
+ continue;
412
+ }
413
+ configSources.set(path, group.source);
414
+ }
415
+ const groupPaths = /* @__PURE__ */ new Set([""]);
416
+ for (const group of parsed.groups) for (const ancestor of getAncestorGroupPaths(group.groupPath)) groupPaths.add(ancestor);
417
+ for (const route of routeModels) for (const ancestor of getAncestorGroupPaths(route.groupPath)) groupPaths.add(ancestor);
418
+ const routesByGroup = /* @__PURE__ */ new Map();
419
+ for (const route of routeModels) {
420
+ const routes = routesByGroup.get(route.groupPath);
421
+ if (routes) routes.push(route.id);
422
+ else routesByGroup.set(route.groupPath, [route.id]);
423
+ }
424
+ const childrenByGroup = /* @__PURE__ */ new Map();
425
+ for (const path of groupPaths) {
426
+ if (!path) continue;
427
+ const parent = parentGroupPath(path) ?? "";
428
+ const children = childrenByGroup.get(parent);
429
+ if (children) children.push(path);
430
+ else childrenByGroup.set(parent, [path]);
431
+ }
432
+ return [...groupPaths].sort((a, b) => {
433
+ if (!a && b) return -1;
434
+ if (a && !b) return 1;
435
+ return a.localeCompare(b);
436
+ }).map((path) => {
437
+ const children = [...childrenByGroup.get(path) ?? []].sort();
438
+ const configSource = configSources.get(path);
439
+ const parent = parentGroupPath(path);
440
+ return {
441
+ children: children.map(groupId),
442
+ id: groupId(path),
443
+ ...parent !== null && { parentId: groupId(parent) },
444
+ path: path ? ensureLeadingSlash(path) : "/",
445
+ routes: [...routesByGroup.get(path) ?? []].sort(),
446
+ ...configSource !== void 0 && { configSource }
447
+ };
448
+ });
449
+ };
450
+ const analyze = (parsed, config) => {
451
+ const diagnostics = [...parsed.diagnostics];
452
+ const routeModels = [];
453
+ const seen = /* @__PURE__ */ new Map();
454
+ for (const route of parsed.routes) {
455
+ const localPath = segmentsToPath(route.segments);
456
+ const fullPath = combinePaths(route.groupPath, localPath);
457
+ const id = routeId(route);
458
+ const previousSource = seen.get(id);
459
+ if (previousSource) {
460
+ diagnostics.push({
461
+ code: "NOOH005",
462
+ file: route.source,
463
+ message: [
464
+ `Duplicate route "${id}".`,
465
+ "",
466
+ `First declaration: ${previousSource}`,
467
+ `Second declaration: ${route.source}`
468
+ ].join("\n"),
469
+ severity: "error"
470
+ });
471
+ continue;
472
+ }
473
+ seen.set(id, route.source);
474
+ routeModels.push({
475
+ fullPath,
476
+ groupPath: normalizePath(route.groupPath),
477
+ id,
478
+ localPath,
479
+ method: route.method,
480
+ routerPath: routeSegmentsToRouterPath(route.groupPath, route.rawSegments),
481
+ routeSegments: route.segments,
482
+ source: route.source
483
+ });
484
+ }
485
+ routeModels.sort(sortRoutes);
486
+ return {
487
+ diagnostics,
488
+ model: {
489
+ config,
490
+ groups: buildGroups(parsed, routeModels, diagnostics),
491
+ routes: routeModels
492
+ }
493
+ };
494
+ };
495
+ //#endregion
496
+ //#region src/pipeline/config.ts
497
+ const DEFAULT_ROUTES_ROOT = "src/routes";
498
+ const isRecord = (value) => typeof value === "object" && value !== null;
499
+ const isString = (value) => typeof value === "string";
500
+ const loadConfig = async (input) => {
501
+ let defaultExport;
502
+ try {
503
+ defaultExport = await input.loader.loadDefault(input.config);
504
+ } catch (error) {
505
+ return { diagnostics: [{
506
+ code: "NOOH010",
507
+ file: input.config,
508
+ message: error instanceof Error ? `Failed to load config: ${error.message}` : "Failed to load config.",
509
+ severity: "error"
510
+ }] };
511
+ }
512
+ if (!isRecord(defaultExport)) return { diagnostics: [{
513
+ code: "NOOH011",
514
+ file: input.config,
515
+ message: "The Nooh config default export must be an object.",
516
+ severity: "error"
517
+ }] };
518
+ const routesValue = defaultExport.routes;
519
+ if (routesValue !== void 0 && !isString(routesValue)) return { diagnostics: [{
520
+ code: "NOOH012",
521
+ file: input.config,
522
+ message: "The Nooh config \"routes\" option must be a string.",
523
+ severity: "error"
524
+ }] };
525
+ const root = normalizePath(input.root ?? "");
526
+ const source = toProjectPath(input.config, root);
527
+ return {
528
+ config: {
529
+ root,
530
+ routesRoot: toProjectPath(routesValue ?? DEFAULT_ROUTES_ROOT, root),
531
+ source,
532
+ value: { routes: routesValue }
533
+ },
534
+ diagnostics: []
535
+ };
536
+ };
537
+ //#endregion
538
+ //#region src/pipeline/discover.ts
539
+ const isTypeScriptFile = (file) => file.path.endsWith(".ts") || file.path.endsWith(".tsx");
540
+ const isGroupFile = (filePath) => {
541
+ const filename = filePath.split("/").at(-1);
542
+ return filename === "$.ts" || filename === "$.tsx";
543
+ };
544
+ const discoverGroup = (config, file) => {
545
+ const source = toProjectPath(file.path, config.root);
546
+ if (!isPathInside(source, config.routesRoot)) return null;
547
+ const parts = relativePath(config.routesRoot, source).split("/").filter(Boolean);
548
+ if (!isGroupFile(source)) return null;
549
+ const directoryParts = parts.slice(0, -1);
550
+ if (directoryParts.includes("endpoints")) return null;
551
+ return {
552
+ groupPath: normalizePath(directoryParts.join("/")),
553
+ source
554
+ };
555
+ };
556
+ const discoverEndpoint = (config, file) => {
557
+ const source = toProjectPath(file.path, config.root);
558
+ if (!isPathInside(source, config.routesRoot)) return null;
559
+ const parts = relativePath(config.routesRoot, source).split("/").filter(Boolean);
560
+ const endpointIndex = parts.lastIndexOf("endpoints");
561
+ if (endpointIndex === -1) return null;
562
+ const endpointsRoot = normalizePath([
563
+ config.routesRoot,
564
+ ...parts.slice(0, endpointIndex),
565
+ "endpoints"
566
+ ].join("/"));
567
+ const groupParts = parts.slice(0, endpointIndex);
568
+ const localParts = parts.slice(endpointIndex + 1);
569
+ if (localParts.length === 0) return null;
570
+ return {
571
+ endpointsRoot,
572
+ groupPath: normalizePath(groupParts.join("/")),
573
+ localPath: normalizePath(localParts.join("/")),
574
+ source
575
+ };
576
+ };
577
+ const discover = (snapshot, config) => {
578
+ const endpoints = [];
579
+ const groups = [];
580
+ for (const file of snapshot.files) {
581
+ if (!isTypeScriptFile(file)) continue;
582
+ const source = toProjectPath(file.path, config.root);
583
+ if (!isPathInside(source, config.routesRoot)) continue;
584
+ const group = discoverGroup(config, file);
585
+ if (group) {
586
+ groups.push(group);
587
+ continue;
588
+ }
589
+ const endpoint = discoverEndpoint(config, file);
590
+ if (endpoint) endpoints.push(endpoint);
591
+ }
592
+ endpoints.sort((a, b) => a.source.localeCompare(b.source));
593
+ groups.sort((a, b) => a.source.localeCompare(b.source));
594
+ return {
595
+ config,
596
+ endpoints,
597
+ groups
598
+ };
599
+ };
600
+ //#endregion
601
+ //#region src/types.ts
602
+ const ROUTE_METHODS = [
603
+ "get",
604
+ "post",
605
+ "put",
606
+ "patch",
607
+ "delete",
608
+ "options",
609
+ "head",
610
+ "all"
611
+ ];
612
+ //#endregion
613
+ //#region src/pipeline/route-parser.ts
614
+ const METHOD_PATTERN = /^(.*)\.(get|post|put|patch|delete|options|head|all)\.(?:ts|tsx)$/;
615
+ const PARAM_PATTERN = /^\[([A-Za-z0-9_]+)\]$/;
616
+ const SPLAT_PATTERN = /^\[\.\.\.([A-Za-z0-9_]+)\]$/;
617
+ const methodSet = new Set(ROUTE_METHODS);
618
+ const parseSegment = (segment) => {
619
+ const parameter = segment.match(PARAM_PATTERN);
620
+ if (parameter?.[1]) return { segment: {
621
+ kind: "param",
622
+ name: parameter[1]
623
+ } };
624
+ const splat = segment.match(SPLAT_PATTERN);
625
+ if (splat?.[1]) return { segment: {
626
+ kind: "splat",
627
+ name: splat[1]
628
+ } };
629
+ if (segment.includes("[") || segment.includes("]")) return { error: {
630
+ code: "NOOH003",
631
+ message: `Invalid route segment "${segment}".`,
632
+ severity: "error"
633
+ } };
634
+ if (!segment) return { error: {
635
+ code: "NOOH004",
636
+ message: "Route segments cannot be empty.",
637
+ severity: "error"
638
+ } };
639
+ return { segment: {
640
+ kind: "static",
641
+ value: segment
642
+ } };
643
+ };
644
+ const parseEndpoint = (endpoint) => {
645
+ const localParts = endpoint.localPath.split("/").filter(Boolean);
646
+ const filename = localParts.pop();
647
+ if (!filename) return { diagnostics: [{
648
+ code: "NOOH001",
649
+ file: endpoint.source,
650
+ message: "Invalid empty endpoint filename.",
651
+ severity: "error"
652
+ }] };
653
+ const match = filename.match(METHOD_PATTERN);
654
+ if (!match) return { diagnostics: [{
655
+ code: "NOOH001",
656
+ file: endpoint.source,
657
+ message: "Invalid endpoint filename. Expected \"<name>.<method>.ts\".",
658
+ severity: "error"
659
+ }] };
660
+ const [_match, routeFile, method] = match;
661
+ if (!(method && methodSet.has(method))) return { diagnostics: [{
662
+ code: "NOOH002",
663
+ file: endpoint.source,
664
+ message: `Unsupported HTTP method "${method}".`,
665
+ severity: "error"
666
+ }] };
667
+ const validMethod = method;
668
+ const routeFileSegments = routeFile?.split("/").filter(Boolean) || [];
669
+ const rawSegments = [...localParts, ...routeFileSegments];
670
+ const effectiveSegments = rawSegments.at(-1) === "index" ? rawSegments.slice(0, -1) : rawSegments;
671
+ const diagnostics = [];
672
+ const segments = [];
673
+ for (const rawSegment of effectiveSegments) {
674
+ const result = parseSegment(rawSegment);
675
+ if (result.error) {
676
+ diagnostics.push({
677
+ ...result.error,
678
+ file: endpoint.source
679
+ });
680
+ continue;
681
+ }
682
+ segments.push(result.segment);
683
+ }
684
+ if (diagnostics.length > 0) return { diagnostics };
685
+ return {
686
+ diagnostics: [],
687
+ route: {
688
+ groupPath: normalizePath(endpoint.groupPath),
689
+ method: validMethod,
690
+ rawSegments: effectiveSegments,
691
+ segments,
692
+ source: endpoint.source
693
+ }
694
+ };
695
+ };
696
+ //#endregion
697
+ //#region src/pipeline/parse.ts
698
+ const parse = (project) => {
699
+ const routes = [];
700
+ const diagnostics = [];
701
+ for (const endpoint of project.endpoints) {
702
+ const result = parseEndpoint(endpoint);
703
+ diagnostics.push(...result.diagnostics);
704
+ if (result.route) routes.push(result.route);
705
+ }
706
+ const groups = project.groups.map((group) => ({
707
+ groupPath: group.groupPath,
708
+ source: group.source
709
+ }));
710
+ routes.sort((a, b) => a.source.localeCompare(b.source));
711
+ groups.sort((a, b) => a.source.localeCompare(b.source));
712
+ return {
713
+ diagnostics,
714
+ groups,
715
+ routes
716
+ };
717
+ };
718
+ //#endregion
719
+ //#region src/pipeline/plan.ts
720
+ const DEFAULT_OUTPUT_ROOT = ".nooh";
721
+ const modulePath = (outputRoot, value) => normalizePath(`${outputRoot}/${value}.ts`);
722
+ const plan = (model, outputRoot = DEFAULT_OUTPUT_ROOT) => {
723
+ const normalizedOutputRoot = normalizePath(outputRoot);
724
+ const modules = [{
725
+ id: modulePath(normalizedOutputRoot, "types"),
726
+ kind: "types"
727
+ }, {
728
+ id: modulePath(normalizedOutputRoot, "router/middleware"),
729
+ kind: "middleware"
730
+ }];
731
+ const routerPaths = [...new Set(model.routes.map((route) => route.routerPath))].sort();
732
+ for (const routerPath of routerPaths) {
733
+ const route = model.routes.find((candidate) => candidate.routerPath === routerPath);
734
+ if (!route) continue;
735
+ modules.push({
736
+ id: modulePath(normalizedOutputRoot, routerPath),
737
+ kind: "router",
738
+ routeId: route.routerPath
739
+ });
740
+ }
741
+ for (const group of model.groups) {
742
+ const groupPath = group.id === "root" ? "groups/root" : `groups/${group.id}`;
743
+ modules.push({
744
+ groupId: group.id,
745
+ id: modulePath(normalizedOutputRoot, groupPath),
746
+ kind: "group"
747
+ });
748
+ }
749
+ modules.push({
750
+ id: modulePath(normalizedOutputRoot, "app"),
751
+ kind: "app"
752
+ });
753
+ modules.sort((a, b) => a.id.localeCompare(b.id));
754
+ return {
755
+ modules,
756
+ outputRoot: normalizedOutputRoot
757
+ };
758
+ };
759
+ //#endregion
760
+ //#region src/compiler.ts
761
+ const createCompiler = () => ({
762
+ analyze,
763
+ compile: async (input) => {
764
+ const configResult = await loadConfig(input);
765
+ if (!configResult.config) return {
766
+ diagnostics: configResult.diagnostics,
767
+ model: {
768
+ config: {
769
+ root: input.root ? input.root.replaceAll("\\", "/") : "",
770
+ routesRoot: "",
771
+ source: input.config,
772
+ value: {}
773
+ },
774
+ groups: [],
775
+ routes: []
776
+ },
777
+ output: null,
778
+ plan: null
779
+ };
780
+ const discovered = discover(input.sources, configResult.config);
781
+ const parsed = parse(discovered);
782
+ const analyzed = analyze(parsed, configResult.config);
783
+ const diagnostics = [...configResult.diagnostics, ...analyzed.diagnostics];
784
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) return {
785
+ diagnostics,
786
+ model: analyzed.model,
787
+ output: null,
788
+ plan: null
789
+ };
790
+ const compilationPlan = plan(analyzed.model, input.options?.outputRoot);
791
+ const output = generate(compilationPlan, analyzed.model);
792
+ return {
793
+ diagnostics,
794
+ model: analyzed.model,
795
+ output,
796
+ plan: compilationPlan
797
+ };
798
+ },
799
+ discover,
800
+ generate,
801
+ loadConfig,
802
+ parse,
803
+ plan
804
+ });
805
+ //#endregion
806
+ //#region src/compile.ts
807
+ const compile = (input) => createCompiler().compile(input);
808
+ //#endregion
809
+ //#region src/diff.ts
810
+ const diff = (previous, next) => {
811
+ const previousMap = new Map(previous.modules.map((module) => [module.id, module]));
812
+ const nextMap = new Map(next.modules.map((module) => [module.id, module]));
813
+ const added = [];
814
+ const changed = [];
815
+ const unchanged = [];
816
+ const removed = [];
817
+ for (const module of next.modules) {
818
+ const previousModule = previousMap.get(module.id);
819
+ if (!previousModule) {
820
+ added.push(module);
821
+ continue;
822
+ }
823
+ if (previousModule.code !== module.code) {
824
+ changed.push(module);
825
+ continue;
826
+ }
827
+ unchanged.push(module);
828
+ }
829
+ for (const module of previous.modules) if (!nextMap.has(module.id)) removed.push(module.id);
830
+ return {
831
+ added,
832
+ changed,
833
+ removed,
834
+ unchanged
835
+ };
836
+ };
837
+ //#endregion
838
+ //#region src/recompile.ts
839
+ const recompile = (input) => {
840
+ const compiler = createCompiler();
841
+ const config = input.config ?? input.previous.model.config.source;
842
+ return compiler.compile({
843
+ config,
844
+ loader: input.loader,
845
+ root: input.previous.model.config.root,
846
+ ...input.options !== void 0 && { options: input.options },
847
+ sources: input.snapshot
848
+ });
849
+ };
850
+ //#endregion
851
+ export { analyze, compile, createCompiler, diff, discover, generate, loadConfig, parse, plan, recompile };
852
+
853
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/generate/utils.ts","../src/utils/path.ts","../src/generate/app.ts","../src/generate/group.ts","../src/generate/middleware.ts","../src/generate/router.ts","../src/generate/types.ts","../src/generate/index.ts","../src/pipeline/analyze.ts","../src/pipeline/config.ts","../src/pipeline/discover.ts","../src/types.ts","../src/pipeline/route-parser.ts","../src/pipeline/parse.ts","../src/pipeline/plan.ts","../src/compiler.ts","../src/compile.ts","../src/diff.ts","../src/recompile.ts"],"sourcesContent":["import type { CompilationPlan } from \"@/types\";\n\n/**\n * Returns the output file path for a route group module.\n * The root group uses the reserved name \"root\".\n */\nexport const groupModuleId = (\n plan: CompilationPlan,\n groupId: string\n): string => {\n if (groupId === \"root\") {\n return `${plan.outputRoot}/groups/root.ts`;\n }\n\n return `${plan.outputRoot}/groups/${groupId}.ts`;\n};\n","const WINDOWS_DRIVE_REGEX = /^[A-Za-z]:\\//;\nconst LEADING_SLASH_REGEX = /^\\/+/;\nconst DRIVE_REGEX = /^([A-Za-z]):\\//;\n\nexport const isAbsolutePath = (value: string): boolean => {\n const normalized = value.replaceAll(\"\\\\\", \"/\");\n\n return normalized.startsWith(\"/\") || WINDOWS_DRIVE_REGEX.test(normalized);\n};\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: ...\nexport const normalizePath = (value: string): string => {\n const normalized = value.replaceAll(\"\\\\\", \"/\");\n\n const isPosixAbsolute = normalized.startsWith(\"/\");\n const driveMatch = normalized.match(DRIVE_REGEX);\n\n const body = driveMatch\n ? normalized.slice(3)\n : // biome-ignore lint/style/noNestedTernary: ...\n isPosixAbsolute\n ? normalized.slice(1)\n : normalized;\n\n const parts = body.split(\"/\");\n const result: string[] = [];\n\n for (const part of parts) {\n if (!part || part === \".\") {\n continue;\n }\n\n if (part === \"..\") {\n if (result.length > 0 && result.at(-1) !== \"..\") {\n result.pop();\n } else if (!(isPosixAbsolute || driveMatch)) {\n result.push(\"..\");\n }\n\n continue;\n }\n\n result.push(part);\n }\n\n const joined = result.join(\"/\");\n\n if (driveMatch) {\n return joined ? `${driveMatch[1]}:/${joined}` : `${driveMatch[1]}:/`;\n }\n\n if (isPosixAbsolute) {\n return joined ? `/${joined}` : \"/\";\n }\n\n return joined;\n};\n\nexport const stripLeadingSlash = (value: string): string =>\n value.replace(LEADING_SLASH_REGEX, \"\");\n\nexport const ensureLeadingSlash = (value: string): string => {\n if (!value) {\n return \"/\";\n }\n\n return value.startsWith(\"/\") ? value : `/${value}`;\n};\n\nexport const joinPath = (...parts: string[]): string =>\n normalizePath(parts.filter(Boolean).join(\"/\"));\n\nexport const dirname = (value: string): string => {\n const normalized = normalizePath(value);\n const index = normalized.lastIndexOf(\"/\");\n\n if (index === -1) {\n return \"\";\n }\n\n if (index === 0) {\n return \"/\";\n }\n\n return normalized.slice(0, index);\n};\n\nexport const basename = (value: string): string => {\n const normalized = normalizePath(value);\n const index = normalized.lastIndexOf(\"/\");\n\n if (index === -1) {\n return normalized;\n }\n\n return normalized.slice(index + 1);\n};\n\nexport const relativePath = (from: string, to: string): string => {\n const fromParts = normalizePath(from).split(\"/\").filter(Boolean);\n const toParts = normalizePath(to).split(\"/\").filter(Boolean);\n\n let common = 0;\n\n while (\n common < fromParts.length &&\n common < toParts.length &&\n fromParts[common] === toParts[common]\n ) {\n common += 1;\n }\n\n const result = [\n ...fromParts.slice(common).map(() => \"..\"),\n ...toParts.slice(common),\n ];\n\n return result.join(\"/\");\n};\n\nexport const toProjectPath = (value: string, root: string): string => {\n const normalizedValue = normalizePath(value);\n const normalizedRoot = normalizePath(root);\n\n if (!(normalizedRoot && isAbsolutePath(normalizedRoot))) {\n return normalizedValue;\n }\n\n if (!isAbsolutePath(normalizedValue)) {\n return normalizedValue;\n }\n\n if (!isPathInside(normalizedValue, normalizedRoot)) {\n return normalizedValue;\n }\n\n return relativePath(normalizedRoot, normalizedValue);\n};\n\nexport const relativeModuleSpecifier = (\n fromModule: string,\n toSource: string\n): string => {\n const fromDirectory = dirname(fromModule);\n\n let target = normalizePath(toSource);\n\n if (target.endsWith(\".ts\")) {\n target = `${target.slice(0, -3)}.js`;\n } else if (target.endsWith(\".tsx\")) {\n target = `${target.slice(0, -4)}.js`;\n }\n\n const relative = relativePath(fromDirectory, target);\n\n return relative.startsWith(\".\") ? relative : `./${relative}`;\n};\n\nexport const isPathInside = (file: string, root: string): boolean => {\n const normalizedFile = normalizePath(file);\n const normalizedRoot = normalizePath(root);\n\n if (normalizedFile === normalizedRoot) {\n return true;\n }\n\n return normalizedFile.startsWith(`${normalizedRoot}/`);\n};\n\nconst EXTENSION_REGEX = /\\.[^.]+$/;\nexport const removeExtension = (value: string): string =>\n value.replace(EXTENSION_REGEX, \"\");\n","import { groupModuleId } from \"@/generate/utils\";\n\nimport type { CompilationPlan, GeneratedModule, ProjectModel } from \"@/types\";\nimport { relativeModuleSpecifier } from \"@/utils/path\";\n\nexport const generateAppModule = (\n plan: CompilationPlan,\n model: ProjectModel\n): GeneratedModule => {\n const moduleId = `${plan.outputRoot}/app.ts`;\n const typesModuleId = `${plan.outputRoot}/types.ts`;\n\n const root = model.groups.find((group) => group.id === \"root\");\n\n if (!root) {\n throw new Error(\"Nooh compilation requires a root route group.\");\n }\n\n const imports = [\n `import { Hono } from \"hono\";`,\n `import type { App } from ${JSON.stringify(\n relativeModuleSpecifier(moduleId, typesModuleId)\n )};`,\n `import root from ${JSON.stringify(\n relativeModuleSpecifier(moduleId, groupModuleId(plan, root.id))\n )};`,\n ];\n\n const code = [\n ...imports,\n \"\",\n \"const app = new Hono<App>();\",\n \"\",\n 'app.route(\"/\", root);',\n \"\",\n \"export type AppType = typeof app;\",\n \"\",\n \"export default app;\",\n \"\",\n ].join(\"\\n\");\n\n return {\n code,\n id: moduleId,\n kind: \"app\",\n };\n};\n","import { groupModuleId } from \"@/generate/utils\";\n\nimport type {\n CompilationPlan,\n GeneratedModule,\n ProjectModel,\n RouteGroup,\n RouteModel,\n} from \"@/types\";\nimport { ensureLeadingSlash, relativeModuleSpecifier } from \"@/utils/path\";\n\nconst getGroupRoutes = (\n model: ProjectModel,\n group: RouteGroup\n): readonly RouteModel[] => {\n const ids = new Set(group.routes);\n\n return model.routes\n .filter((route) => ids.has(route.id))\n .sort((a, b) => {\n const pathDifference = a.localPath.localeCompare(b.localPath);\n\n if (pathDifference !== 0) {\n return pathDifference;\n }\n\n const methodDifference = a.method.localeCompare(b.method);\n\n if (methodDifference !== 0) {\n return methodDifference;\n }\n\n return a.source.localeCompare(b.source);\n });\n};\n\nconst capitalize = (value: string): string =>\n value.charAt(0).toUpperCase() + value.slice(1);\n\nconst getGroup = (model: ProjectModel, id: string): RouteGroup | undefined =>\n model.groups.find((group) => group.id === id);\n\nconst getChildPath = (parent: RouteGroup, child: RouteGroup): string => {\n if (parent.path === \"/\") {\n return child.path;\n }\n\n const prefix = `${parent.path}/`;\n\n if (!child.path.startsWith(prefix)) {\n throw new Error(\n `Invalid group tree: \"${child.path}\" is not a child of \"${parent.path}\".`\n );\n }\n\n return child.path.slice(prefix.length);\n};\n\nexport const generateGroupModule = (\n plan: CompilationPlan,\n model: ProjectModel,\n group: RouteGroup\n): GeneratedModule => {\n const moduleId = groupModuleId(plan, group.id);\n const typesModuleId = `${plan.outputRoot}/types.ts`;\n const routes = getGroupRoutes(model, group);\n\n const children = group.children\n .map((childId) => getGroup(model, childId))\n .filter((child): child is RouteGroup => child !== undefined)\n .sort((a, b) => a.path.localeCompare(b.path));\n\n const imports = [\n `import { Hono } from \"hono\";`,\n `import type { App } from ${JSON.stringify(\n relativeModuleSpecifier(moduleId, typesModuleId)\n )};`,\n ];\n\n if (group.configSource) {\n imports.push(\n `import groupConfig from ${JSON.stringify(\n relativeModuleSpecifier(moduleId, group.configSource)\n )};`\n );\n }\n\n children.forEach((child, index) => {\n imports.push(\n `import child${index} from ${JSON.stringify(\n relativeModuleSpecifier(moduleId, groupModuleId(plan, child.id))\n )};`\n );\n });\n\n routes.forEach((route, index) => {\n imports.push(\n `import endpoint${index} from ${JSON.stringify(\n relativeModuleSpecifier(moduleId, route.source)\n )};`\n );\n });\n\n const methods = [...new Set(routes.map((route) => route.method))].sort();\n\n const registerTypes = [\n \"type RouteRegister = (\",\n \" path: string,\",\n \" ...handlers: any[]\",\n \") => typeof route;\",\n ];\n\n const registerDeclarations = methods.map((method) => {\n const name = `register${capitalize(method)}`;\n\n return `const ${name} = route.${method} as unknown as RouteRegister;`;\n });\n\n const useDeclaration = group.configSource\n ? [\n \"type RouteUse = (\",\n \" path: string,\",\n \" ...handlers: any[]\",\n \") => typeof route;\",\n \"\",\n \"const use = route.use as unknown as RouteUse;\",\n \"\",\n 'use(\"*\", ...(groupConfig.middleware ?? []));',\n ]\n : [];\n\n const registrations = routes.map((route, index) => {\n const register = `register${capitalize(route.method)}`;\n\n return ` ${register}(${JSON.stringify(\n ensureLeadingSlash(route.localPath)\n )}, ...endpoint${index});`;\n });\n\n const childRegistrations = children.map(\n (child, index) =>\n ` route.route(${JSON.stringify(\n getChildPath(group, child)\n )}, child${index});`\n );\n\n const code = [\n ...imports,\n \"\",\n \"const route = new Hono<App>();\",\n \"\",\n ...registerTypes,\n \"\",\n ...registerDeclarations,\n ...(useDeclaration.length > 0 ? [\"\", ...useDeclaration] : []),\n ...(registrations.length > 0 ? [\"\", ...registrations] : []),\n ...(childRegistrations.length > 0 ? [\"\", ...childRegistrations] : []),\n \"\",\n \"export default route;\",\n \"\",\n ].join(\"\\n\");\n\n return {\n code,\n id: moduleId,\n kind: \"group\",\n };\n};\n","import type { CompilationPlan, GeneratedModule } from \"@/types\";\n\nexport const generateMiddlewareModule = (\n plan: CompilationPlan\n): GeneratedModule => {\n const moduleId = `${plan.outputRoot}/router/middleware.ts`;\n\n return {\n code: [\n `import { createMiddleware } from \"hono/factory\";`,\n `import type { App } from \"../types.js\";`,\n \"\",\n \"export const middleware = createMiddleware<App>;\",\n \"\",\n ].join(\"\\n\"),\n id: moduleId,\n kind: \"middleware\",\n };\n};\n","import type {\n CompilationPlan,\n GeneratedModule,\n ProjectModel,\n RouteModel,\n} from \"@/types\";\nimport { ensureLeadingSlash, relativeModuleSpecifier } from \"@/utils/path\";\n\nconst VALIDATION_TARGETS = [\n \"json\",\n \"form\",\n \"query\",\n \"param\",\n \"header\",\n \"cookie\",\n] as const;\n\nconst METHOD_FUNCTION_NAMES: Record<RouteModel[\"method\"], string> = {\n all: \"all\",\n delete: \"del\",\n get: \"get\",\n head: \"head\",\n options: \"options\",\n patch: \"patch\",\n post: \"post\",\n put: \"put\",\n};\n\nconst getRoutesForRouter = (\n model: ProjectModel,\n routerPath: string\n): readonly RouteModel[] =>\n model.routes\n .filter((route) => route.routerPath === routerPath)\n .sort((a, b) => a.method.localeCompare(b.method));\n\nconst renderMethod = (route: RouteModel): string => {\n const { method } = route;\n const functionName = METHOD_FUNCTION_NAMES[method];\n\n const path = JSON.stringify(ensureLeadingSlash(route.localPath));\n\n return [\n `type Path = ${path};`,\n \"\",\n \"type RouteMiddleware = MiddlewareHandler<App, Path>;\",\n \"\",\n \"type ValidationTarget = Parameters<typeof sValidator>[0];\",\n \"type StandardSchema = Parameters<typeof sValidator>[1];\",\n \"\",\n \"type ValidationOptions = Partial<\",\n \" Record<ValidationTarget, StandardSchema>\",\n \">;\",\n \"\",\n \"type HandlerInput<T> = T extends Handler<\",\n \" any,\",\n \" any,\",\n \" infer I,\",\n \" any\",\n \"> ? I : never;\",\n \"\",\n \"type ValidationHandler<\",\n \" Target extends ValidationTarget,\",\n \" Schema extends StandardSchema,\",\n \"> = ReturnType<\",\n \" typeof sValidator<Schema, Target, App, Path>\",\n \">;\",\n \"\",\n \"type ValidationInput<V extends ValidationOptions> =\",\n ...VALIDATION_TARGETS.map(\n (target) =>\n ` & (${JSON.stringify(target)} extends keyof V ? HandlerInput<ValidationHandler<${JSON.stringify(target)}, NonNullable<V[${JSON.stringify(target)}]>>> : {})`\n ),\n \";\",\n \"\",\n \"type RouteHandler = Handler<App, Path, any, any>;\",\n \"\",\n \"type EndpointOptions<\",\n \" M extends readonly RouteMiddleware[],\",\n \" V extends ValidationOptions,\",\n \" H extends Handler<App, Path, ValidationInput<V>>,\",\n \"> = {\",\n \" readonly middleware?: M;\",\n \" readonly validation?: V;\",\n \" readonly handler: H;\",\n \"};\",\n \"\",\n `export function ${functionName}<H extends Handler<App, Path>>(`,\n \" handler: H,\",\n \"): readonly RouteHandler[];\",\n \"\",\n `export function ${functionName}<`,\n \" M extends readonly RouteMiddleware[],\",\n \" V extends ValidationOptions,\",\n \" H extends Handler<App, Path, ValidationInput<V>>,\",\n \">(\",\n \" options: EndpointOptions<M, V, H>,\",\n \"): readonly RouteHandler[];\",\n \"\",\n `export function ${functionName}<`,\n \" M extends readonly RouteMiddleware[],\",\n \" V extends ValidationOptions,\",\n \" H extends Handler<App, Path, ValidationInput<V>>,\",\n \">(\",\n \" input:\",\n \" | H\",\n \" | EndpointOptions<M, V, H>,\",\n \"): readonly RouteHandler[] {\",\n ' if (typeof input === \"function\") {',\n \" return [input as RouteHandler];\",\n \" }\",\n \"\",\n \" return [\",\n \" ...(input.middleware ?? []) as readonly RouteHandler[],\",\n ...VALIDATION_TARGETS.map(\n (target) =>\n ` ...(input.validation?.${target} !== undefined ? [sValidator(${JSON.stringify(target)}, input.validation.${target}) as RouteHandler] : []),`\n ),\n \" input.handler as RouteHandler,\",\n \" ];\",\n \"}\",\n \"\",\n ].join(\"\\n\");\n};\n\nexport const generateRouterModule = (\n plan: CompilationPlan,\n model: ProjectModel,\n routerPath: string\n): GeneratedModule => {\n const routes = getRoutesForRouter(model, routerPath);\n\n const moduleId = `${plan.outputRoot}/${routerPath}.ts`;\n\n const typesModuleId = `${plan.outputRoot}/types.ts`;\n\n return {\n code: [\n `import { sValidator } from \"@hono/standard-validator\";`,\n `import type { Handler, MiddlewareHandler } from \"hono\";`,\n `import type { App } from ${JSON.stringify(\n relativeModuleSpecifier(moduleId, typesModuleId)\n )};`,\n \"\",\n ...routes.map(renderMethod),\n ].join(\"\\n\"),\n id: moduleId,\n kind: \"router\",\n };\n};\n","import type { CompilationPlan, GeneratedModule, LoadedConfig } from \"@/types\";\nimport { relativeModuleSpecifier } from \"@/utils/path\";\n\nexport const generateTypesModule = (\n plan: CompilationPlan,\n config: LoadedConfig\n): GeneratedModule => {\n const moduleId = `${plan.outputRoot}/types.ts`;\n\n const configImport = relativeModuleSpecifier(moduleId, config.source);\n\n const code = [\n `import type config from \"${configImport}\";`,\n \"\",\n \"type ExtractEnvironment<T> = T extends {\",\n \" readonly __nooh_env: infer Environment;\",\n \"}\",\n \" ? Environment\",\n \" : never;\",\n \"\",\n \"export type App = ExtractEnvironment<typeof config>;\",\n \"\",\n ].join(\"\\n\");\n\n return {\n code,\n id: moduleId,\n kind: \"types\",\n };\n};\n","import { generateAppModule } from \"@/generate/app\";\nimport { generateGroupModule } from \"@/generate/group\";\nimport { generateMiddlewareModule } from \"@/generate/middleware\";\nimport { generateRouterModule } from \"@/generate/router\";\nimport { generateTypesModule } from \"@/generate/types\";\n\nimport type {\n CompilationPlan,\n GeneratedModule,\n GeneratedOutput,\n ProjectModel,\n} from \"@/types\";\n\nexport const generate = (\n plan: CompilationPlan,\n model: ProjectModel\n): GeneratedOutput => {\n const modules: GeneratedModule[] = [];\n\n modules.push(generateTypesModule(plan, model.config));\n\n modules.push(generateMiddlewareModule(plan));\n\n const routerPaths = [\n ...new Set(model.routes.map((route) => route.routerPath)),\n ].sort();\n\n for (const routerPath of routerPaths) {\n modules.push(generateRouterModule(plan, model, routerPath));\n }\n\n for (const group of model.groups) {\n modules.push(generateGroupModule(plan, model, group));\n }\n\n modules.push(generateAppModule(plan, model));\n\n modules.sort((a, b) => a.id.localeCompare(b.id));\n\n return {\n modules,\n };\n};\n","import type {\n Diagnostic,\n LoadedConfig,\n ParsedProject,\n ParsedRoute,\n ProjectModel,\n RouteGroup,\n RouteModel,\n RouteSegment,\n} from \"@/types\";\nimport { ensureLeadingSlash, normalizePath } from \"@/utils/path\";\n\nconst segmentToHono = (segment: RouteSegment): string => {\n // biome-ignore lint/style/useDefaultSwitchClause: ...\n switch (segment.kind) {\n case \"static\":\n return segment.value;\n\n case \"param\":\n return `:${segment.name}`;\n\n case \"splat\":\n return \"*\";\n }\n};\n\nconst segmentsToPath = (segments: readonly RouteSegment[]): string => {\n if (segments.length === 0) {\n return \"\";\n }\n\n return segments.map(segmentToHono).join(\"/\");\n};\n\nconst combinePaths = (groupPath: string, localPath: string): string => {\n const group = normalizePath(groupPath).replace(/^\\/+|\\/+$/g, \"\");\n const local = normalizePath(localPath).replace(/^\\/+|\\/+$/g, \"\");\n\n if (!(group || local)) {\n return \"/\";\n }\n\n if (!group) {\n return `/${local}`;\n }\n\n if (!local) {\n return `/${group}`;\n }\n\n return `/${group}/${local}`;\n};\n\nconst routeSegmentsToRouterPath = (\n groupPath: string,\n rawSegments: readonly string[]\n): string => {\n const parts = [\n ...groupPath.split(\"/\").filter(Boolean),\n ...rawSegments.filter(Boolean),\n ];\n\n return parts.length === 0 ? \"router/index\" : `router/${parts.join(\"/\")}`;\n};\n\nconst routeId = (route: ParsedRoute): string =>\n `${route.method.toUpperCase()} ${combinePaths(\n route.groupPath,\n segmentsToPath(route.segments)\n )}`;\n\nconst sortRoutes = (a: RouteModel, b: RouteModel): number => {\n const pathDifference = a.fullPath.localeCompare(b.fullPath);\n\n if (pathDifference !== 0) {\n return pathDifference;\n }\n\n const methodDifference = a.method.localeCompare(b.method);\n\n if (methodDifference !== 0) {\n return methodDifference;\n }\n\n return a.source.localeCompare(b.source);\n};\n\nconst parentGroupPath = (groupPath: string): string | null => {\n const normalized = normalizePath(groupPath);\n\n if (!normalized) {\n return null;\n }\n\n const parts = normalized.split(\"/\").filter(Boolean);\n\n if (parts.length <= 1) {\n return \"\";\n }\n\n return parts.slice(0, -1).join(\"/\");\n};\n\nconst getAncestorGroupPaths = (groupPath: string): readonly string[] => {\n const parts = normalizePath(groupPath).split(\"/\").filter(Boolean);\n\n return Array.from({ length: parts.length + 1 }, (_, index) =>\n parts.slice(0, index).join(\"/\")\n );\n};\n\nconst groupId = (path: string): string => path || \"root\";\n\nconst buildGroups = (\n parsed: ParsedProject,\n routeModels: readonly RouteModel[],\n diagnostics: Diagnostic[]\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: ...\n): readonly RouteGroup[] => {\n const configSources = new Map<string, string>();\n\n for (const group of parsed.groups) {\n const path = normalizePath(group.groupPath);\n const previous = configSources.get(path);\n\n if (previous) {\n diagnostics.push({\n code: \"NOOH006\",\n file: group.source,\n message: [\n `Duplicate group definition for \"${path || \"/\"}\".`,\n \"\",\n `First declaration: ${previous}`,\n `Second declaration: ${group.source}`,\n ].join(\"\\n\"),\n severity: \"error\",\n });\n\n continue;\n }\n\n configSources.set(path, group.source);\n }\n\n const groupPaths = new Set<string>([\"\"]);\n\n for (const group of parsed.groups) {\n for (const ancestor of getAncestorGroupPaths(group.groupPath)) {\n groupPaths.add(ancestor);\n }\n }\n\n for (const route of routeModels) {\n for (const ancestor of getAncestorGroupPaths(route.groupPath)) {\n groupPaths.add(ancestor);\n }\n }\n\n const routesByGroup = new Map<string, string[]>();\n\n for (const route of routeModels) {\n const routes = routesByGroup.get(route.groupPath);\n\n if (routes) {\n routes.push(route.id);\n } else {\n routesByGroup.set(route.groupPath, [route.id]);\n }\n }\n\n const childrenByGroup = new Map<string, string[]>();\n\n for (const path of groupPaths) {\n if (!path) {\n continue;\n }\n\n const parent = parentGroupPath(path) ?? \"\";\n\n const children = childrenByGroup.get(parent);\n\n if (children) {\n children.push(path);\n } else {\n childrenByGroup.set(parent, [path]);\n }\n }\n\n return [...groupPaths]\n .sort((a, b) => {\n if (!a && b) {\n return -1;\n }\n\n if (a && !b) {\n return 1;\n }\n\n return a.localeCompare(b);\n })\n .map((path) => {\n const children = [...(childrenByGroup.get(path) ?? [])].sort();\n const configSource = configSources.get(path);\n const parent = parentGroupPath(path);\n\n return {\n children: children.map(groupId),\n id: groupId(path),\n ...(parent !== null && {\n parentId: groupId(parent),\n }),\n path: path ? ensureLeadingSlash(path) : \"/\",\n routes: [...(routesByGroup.get(path) ?? [])].sort(),\n ...(configSource !== undefined && {\n configSource,\n }),\n };\n });\n};\n\nexport const analyze = (\n parsed: ParsedProject,\n config: LoadedConfig\n): {\n model: ProjectModel;\n diagnostics: readonly Diagnostic[];\n} => {\n const diagnostics: Diagnostic[] = [...parsed.diagnostics];\n\n const routeModels: RouteModel[] = [];\n const seen = new Map<string, string>();\n\n for (const route of parsed.routes) {\n const localPath = segmentsToPath(route.segments);\n const fullPath = combinePaths(route.groupPath, localPath);\n const id = routeId(route);\n\n const previousSource = seen.get(id);\n\n if (previousSource) {\n diagnostics.push({\n code: \"NOOH005\",\n file: route.source,\n message: [\n `Duplicate route \"${id}\".`,\n \"\",\n `First declaration: ${previousSource}`,\n `Second declaration: ${route.source}`,\n ].join(\"\\n\"),\n severity: \"error\",\n });\n\n continue;\n }\n\n seen.set(id, route.source);\n\n routeModels.push({\n fullPath,\n groupPath: normalizePath(route.groupPath),\n id,\n localPath,\n method: route.method,\n routerPath: routeSegmentsToRouterPath(route.groupPath, route.rawSegments),\n routeSegments: route.segments,\n source: route.source,\n });\n }\n\n routeModels.sort(sortRoutes);\n\n const groups = buildGroups(parsed, routeModels, diagnostics);\n\n return {\n diagnostics,\n model: {\n config,\n groups,\n routes: routeModels,\n },\n };\n};\n","import type { CompileInput, ConfigLoadResult, RuntimeConfig } from \"@/types\";\nimport { normalizePath, toProjectPath } from \"@/utils/path\";\n\nconst DEFAULT_ROUTES_ROOT = \"src/routes\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst isString = (value: unknown): value is string => typeof value === \"string\";\n\nexport const loadConfig = async (\n input: CompileInput\n): Promise<ConfigLoadResult> => {\n let defaultExport: unknown;\n\n try {\n defaultExport = await input.loader.loadDefault(input.config);\n } catch (error) {\n return {\n diagnostics: [\n {\n code: \"NOOH010\",\n file: input.config,\n message:\n error instanceof Error\n ? `Failed to load config: ${error.message}`\n : \"Failed to load config.\",\n severity: \"error\",\n },\n ],\n };\n }\n\n if (!isRecord(defaultExport)) {\n return {\n diagnostics: [\n {\n code: \"NOOH011\",\n file: input.config,\n message: \"The Nooh config default export must be an object.\",\n severity: \"error\",\n },\n ],\n };\n }\n\n const routesValue = defaultExport.routes;\n\n if (routesValue !== undefined && !isString(routesValue)) {\n return {\n diagnostics: [\n {\n code: \"NOOH012\",\n file: input.config,\n message: 'The Nooh config \"routes\" option must be a string.',\n severity: \"error\",\n },\n ],\n };\n }\n\n const root = normalizePath(input.root ?? \"\");\n const source = toProjectPath(input.config, root);\n const routes = routesValue ?? DEFAULT_ROUTES_ROOT;\n const routesRoot = toProjectPath(routes, root);\n\n const value: RuntimeConfig = {\n routes: routesValue,\n };\n\n return {\n config: {\n root,\n routesRoot,\n source,\n value,\n },\n diagnostics: [],\n };\n};\n","import type {\n DiscoveredEndpoint,\n DiscoveredGroup,\n DiscoveredProject,\n LoadedConfig,\n SourceFile,\n SourceSnapshot,\n} from \"@/types\";\nimport {\n isPathInside,\n normalizePath,\n relativePath,\n toProjectPath,\n} from \"@/utils/path\";\n\nconst isTypeScriptFile = (file: SourceFile): boolean =>\n file.path.endsWith(\".ts\") || file.path.endsWith(\".tsx\");\n\nconst isGroupFile = (filePath: string): boolean => {\n const filename = filePath.split(\"/\").at(-1);\n\n return filename === \"$.ts\" || filename === \"$.tsx\";\n};\n\nconst discoverGroup = (\n config: LoadedConfig,\n file: SourceFile\n): DiscoveredGroup | null => {\n const source = toProjectPath(file.path, config.root);\n\n if (!isPathInside(source, config.routesRoot)) {\n return null;\n }\n\n const relative = relativePath(config.routesRoot, source);\n const parts = relative.split(\"/\").filter(Boolean);\n\n if (!isGroupFile(source)) {\n return null;\n }\n\n const directoryParts = parts.slice(0, -1);\n\n if (directoryParts.includes(\"endpoints\")) {\n return null;\n }\n\n return {\n groupPath: normalizePath(directoryParts.join(\"/\")),\n source,\n };\n};\n\nexport const findEndpointsDirectory = (\n routesRoot: string,\n filePath: string,\n root = \"\"\n): string | null => {\n const normalizedRoutesRoot = toProjectPath(routesRoot, root);\n\n const normalizedFilePath = toProjectPath(filePath, root);\n\n if (!isPathInside(normalizedFilePath, normalizedRoutesRoot)) {\n return null;\n }\n\n const relative = relativePath(normalizedRoutesRoot, normalizedFilePath);\n\n const parts = relative.split(\"/\").filter(Boolean);\n\n const endpointIndex = parts.lastIndexOf(\"endpoints\");\n\n if (endpointIndex === -1) {\n return null;\n }\n\n const groupParts = parts.slice(0, endpointIndex);\n\n return normalizePath(\n [normalizedRoutesRoot, ...groupParts, \"endpoints\"].filter(Boolean).join(\"/\")\n );\n};\n\nconst discoverEndpoint = (\n config: LoadedConfig,\n file: SourceFile\n): DiscoveredEndpoint | null => {\n const source = toProjectPath(file.path, config.root);\n\n if (!isPathInside(source, config.routesRoot)) {\n return null;\n }\n\n const relative = relativePath(config.routesRoot, source);\n\n const parts = relative.split(\"/\").filter(Boolean);\n\n const endpointIndex = parts.lastIndexOf(\"endpoints\");\n\n if (endpointIndex === -1) {\n return null;\n }\n\n const endpointsRoot = normalizePath(\n [config.routesRoot, ...parts.slice(0, endpointIndex), \"endpoints\"].join(\"/\")\n );\n\n const groupParts = parts.slice(0, endpointIndex);\n const localParts = parts.slice(endpointIndex + 1);\n\n if (localParts.length === 0) {\n return null;\n }\n\n return {\n endpointsRoot,\n groupPath: normalizePath(groupParts.join(\"/\")),\n localPath: normalizePath(localParts.join(\"/\")),\n source,\n };\n};\n\nexport const discover = (\n snapshot: SourceSnapshot,\n config: LoadedConfig\n): DiscoveredProject => {\n const endpoints: DiscoveredEndpoint[] = [];\n const groups: DiscoveredGroup[] = [];\n\n for (const file of snapshot.files) {\n if (!isTypeScriptFile(file)) {\n continue;\n }\n\n const source = toProjectPath(file.path, config.root);\n\n if (!isPathInside(source, config.routesRoot)) {\n continue;\n }\n\n const group = discoverGroup(config, file);\n\n if (group) {\n groups.push(group);\n continue;\n }\n\n const endpoint = discoverEndpoint(config, file);\n\n if (endpoint) {\n endpoints.push(endpoint);\n }\n }\n\n endpoints.sort((a, b) => a.source.localeCompare(b.source));\n groups.sort((a, b) => a.source.localeCompare(b.source));\n\n return {\n config,\n endpoints,\n groups,\n };\n};\n","export const ROUTE_METHODS = [\n \"get\",\n \"post\",\n \"put\",\n \"patch\",\n \"delete\",\n \"options\",\n \"head\",\n \"all\",\n] as const;\n\nexport type RouteMethod = (typeof ROUTE_METHODS)[number];\n\nexport interface SourceFile {\n readonly content: string;\n readonly path: string;\n}\n\nexport interface SourceSnapshot {\n readonly files: readonly SourceFile[];\n}\n\nexport interface ModuleLoader {\n loadDefault: (modulePath: string) => Promise<unknown>;\n}\n\nexport interface CompileOptions {\n readonly outputRoot?: string;\n}\n\nexport interface CompileInput {\n readonly config: string;\n readonly loader: ModuleLoader;\n readonly options?: CompileOptions;\n readonly root?: string;\n readonly sources: SourceSnapshot;\n}\n\nexport interface RuntimeConfig {\n readonly routes?: string | undefined;\n}\n\nexport interface LoadedConfig {\n readonly root: string;\n readonly routesRoot: string;\n readonly source: string;\n readonly value: RuntimeConfig;\n}\n\nexport interface ConfigLoadResult {\n readonly config?: LoadedConfig;\n readonly diagnostics: readonly Diagnostic[];\n}\n\nexport interface DiscoveredEndpoint {\n readonly endpointsRoot: string;\n readonly groupPath: string;\n readonly localPath: string;\n readonly source: string;\n}\n\nexport interface DiscoveredGroup {\n readonly groupPath: string;\n readonly source: string;\n}\n\nexport interface DiscoveredProject {\n readonly config: LoadedConfig;\n readonly endpoints: readonly DiscoveredEndpoint[];\n readonly groups: readonly DiscoveredGroup[];\n}\n\nexport type RouteSegment =\n | {\n readonly kind: \"static\";\n readonly value: string;\n }\n | {\n readonly kind: \"param\";\n readonly name: string;\n }\n | {\n readonly kind: \"splat\";\n readonly name?: string;\n };\n\nexport interface ParsedRoute {\n readonly groupPath: string;\n readonly method: RouteMethod;\n readonly rawSegments: readonly string[];\n readonly segments: readonly RouteSegment[];\n readonly source: string;\n}\n\nexport interface ParsedGroup {\n readonly groupPath: string;\n readonly source: string;\n}\n\nexport interface ParsedProject {\n readonly diagnostics: readonly Diagnostic[];\n readonly groups: readonly ParsedGroup[];\n readonly routes: readonly ParsedRoute[];\n}\n\nexport interface RouteModel {\n readonly fullPath: string;\n\n readonly groupPath: string;\n readonly id: string;\n\n readonly localPath: string;\n\n readonly method: RouteMethod;\n\n readonly routerPath: string;\n readonly routeSegments: readonly RouteSegment[];\n readonly source: string;\n}\n\nexport interface RouteGroup {\n readonly children: readonly string[];\n readonly configSource?: string;\n readonly id: string;\n readonly parentId?: string;\n readonly path: string;\n readonly routes: readonly string[];\n}\n\nexport interface ProjectModel {\n readonly config: LoadedConfig;\n readonly groups: readonly RouteGroup[];\n readonly routes: readonly RouteModel[];\n}\n\nexport type DiagnosticSeverity = \"error\" | \"warning\" | \"info\";\n\nexport interface Diagnostic {\n readonly code: string;\n readonly file?: string;\n readonly message: string;\n readonly severity: DiagnosticSeverity;\n}\n\nexport type ModuleKind = \"types\" | \"router\" | \"middleware\" | \"group\" | \"app\";\n\nexport interface ModulePlan {\n readonly groupId?: string;\n readonly id: string;\n readonly kind: ModuleKind;\n readonly routeId?: string;\n}\n\nexport interface CompilationPlan {\n readonly modules: readonly ModulePlan[];\n readonly outputRoot: string;\n}\n\nexport interface GeneratedModule {\n readonly code: string;\n readonly id: string;\n readonly kind: ModuleKind;\n}\n\nexport interface GeneratedOutput {\n readonly modules: readonly GeneratedModule[];\n}\n\nexport interface Compilation {\n readonly diagnostics: readonly Diagnostic[];\n readonly model: ProjectModel;\n readonly output: GeneratedOutput | null;\n readonly plan: CompilationPlan | null;\n}\n\nexport interface NoohCompiler {\n analyze: (\n parsed: ParsedProject,\n config: LoadedConfig\n ) => {\n model: ProjectModel;\n diagnostics: readonly Diagnostic[];\n };\n compile: (input: CompileInput) => Promise<Compilation>;\n discover: (\n sources: CompileInput[\"sources\"],\n config: LoadedConfig\n ) => DiscoveredProject;\n generate: (plan: CompilationPlan, model: ProjectModel) => GeneratedOutput;\n loadConfig: (input: CompileInput) => Promise<ConfigLoadResult>;\n parse: (project: DiscoveredProject) => ParsedProject;\n plan: (model: ProjectModel, outputRoot?: string) => CompilationPlan;\n}\n\nexport interface OutputDiff {\n readonly added: readonly GeneratedModule[];\n readonly changed: readonly GeneratedModule[];\n readonly removed: readonly string[];\n readonly unchanged: readonly GeneratedModule[];\n}\n\nexport interface RecompileInput {\n readonly config?: string;\n readonly loader: CompileInput[\"loader\"];\n readonly options?: CompileInput[\"options\"];\n readonly previous: Compilation;\n readonly snapshot: SourceSnapshot;\n}\n","import type {\n Diagnostic,\n DiscoveredEndpoint,\n ParsedRoute,\n RouteSegment,\n} from \"@/types\";\nimport { ROUTE_METHODS } from \"@/types\";\nimport { normalizePath } from \"@/utils/path\";\n\nconst METHOD_PATTERN =\n /^(.*)\\.(get|post|put|patch|delete|options|head|all)\\.(?:ts|tsx)$/;\n\nconst PARAM_PATTERN = /^\\[([A-Za-z0-9_]+)\\]$/;\nconst SPLAT_PATTERN = /^\\[\\.\\.\\.([A-Za-z0-9_]+)\\]$/;\n\nconst methodSet = new Set<string>(ROUTE_METHODS);\n\nconst parseSegment = (\n segment: string\n):\n | { segment: RouteSegment; error?: undefined }\n | {\n segment?: undefined;\n error: Diagnostic;\n } => {\n const parameter = segment.match(PARAM_PATTERN);\n\n if (parameter?.[1]) {\n return {\n segment: {\n kind: \"param\",\n name: parameter[1],\n },\n };\n }\n\n const splat = segment.match(SPLAT_PATTERN);\n\n if (splat?.[1]) {\n return {\n segment: {\n kind: \"splat\",\n name: splat[1],\n },\n };\n }\n\n if (segment.includes(\"[\") || segment.includes(\"]\")) {\n return {\n error: {\n code: \"NOOH003\",\n message: `Invalid route segment \"${segment}\".`,\n severity: \"error\",\n },\n };\n }\n\n if (!segment) {\n return {\n error: {\n code: \"NOOH004\",\n message: \"Route segments cannot be empty.\",\n severity: \"error\",\n },\n };\n }\n\n return {\n segment: {\n kind: \"static\",\n value: segment,\n },\n };\n};\n\nexport const parseEndpoint = (\n endpoint: DiscoveredEndpoint\n): {\n route?: ParsedRoute;\n diagnostics: readonly Diagnostic[];\n} => {\n const localParts = endpoint.localPath.split(\"/\").filter(Boolean);\n const filename = localParts.pop();\n\n if (!filename) {\n return {\n diagnostics: [\n {\n code: \"NOOH001\",\n file: endpoint.source,\n message: \"Invalid empty endpoint filename.\",\n severity: \"error\",\n },\n ],\n };\n }\n\n const match = filename.match(METHOD_PATTERN);\n\n if (!match) {\n return {\n diagnostics: [\n {\n code: \"NOOH001\",\n file: endpoint.source,\n message: 'Invalid endpoint filename. Expected \"<name>.<method>.ts\".',\n severity: \"error\",\n },\n ],\n };\n }\n\n const [_match, routeFile, method] = match;\n\n if (!(method && methodSet.has(method))) {\n return {\n diagnostics: [\n {\n code: \"NOOH002\",\n file: endpoint.source,\n message: `Unsupported HTTP method \"${method}\".`,\n severity: \"error\",\n },\n ],\n };\n }\n\n const validMethod = method as ParsedRoute[\"method\"];\n\n const routeFileSegments = routeFile?.split(\"/\").filter(Boolean) || [];\n const rawSegments = [...localParts, ...routeFileSegments];\n\n const last = rawSegments.at(-1);\n const effectiveSegments =\n last === \"index\" ? rawSegments.slice(0, -1) : rawSegments;\n\n const diagnostics: Diagnostic[] = [];\n const segments: RouteSegment[] = [];\n\n for (const rawSegment of effectiveSegments) {\n const result = parseSegment(rawSegment);\n\n if (result.error) {\n diagnostics.push({\n ...result.error,\n file: endpoint.source,\n });\n\n continue;\n }\n\n segments.push(result.segment);\n }\n\n if (diagnostics.length > 0) {\n return {\n diagnostics,\n };\n }\n\n return {\n diagnostics: [],\n route: {\n groupPath: normalizePath(endpoint.groupPath),\n method: validMethod,\n rawSegments: effectiveSegments,\n segments,\n source: endpoint.source,\n },\n };\n};\n","import { parseEndpoint } from \"@/pipeline/route-parser\";\nimport type {\n Diagnostic,\n DiscoveredProject,\n ParsedGroup,\n ParsedProject,\n ParsedRoute,\n} from \"@/types\";\n\nexport const parse = (project: DiscoveredProject): ParsedProject => {\n const routes: ParsedRoute[] = [];\n const diagnostics: Diagnostic[] = [];\n\n for (const endpoint of project.endpoints) {\n const result = parseEndpoint(endpoint);\n\n diagnostics.push(...result.diagnostics);\n\n if (result.route) {\n routes.push(result.route);\n }\n }\n\n const groups: ParsedGroup[] = project.groups.map((group) => ({\n groupPath: group.groupPath,\n source: group.source,\n }));\n\n routes.sort((a, b) => a.source.localeCompare(b.source));\n groups.sort((a, b) => a.source.localeCompare(b.source));\n\n return {\n diagnostics,\n groups,\n routes,\n };\n};\n","import type { CompilationPlan, ModulePlan, ProjectModel } from \"@/types\";\nimport { normalizePath } from \"@/utils/path\";\n\nconst DEFAULT_OUTPUT_ROOT = \".nooh\";\n\nconst modulePath = (outputRoot: string, value: string): string =>\n normalizePath(`${outputRoot}/${value}.ts`);\n\nexport const plan = (\n model: ProjectModel,\n outputRoot: string = DEFAULT_OUTPUT_ROOT\n): CompilationPlan => {\n const normalizedOutputRoot = normalizePath(outputRoot);\n\n const modules: ModulePlan[] = [\n {\n id: modulePath(normalizedOutputRoot, \"types\"),\n kind: \"types\",\n },\n {\n id: modulePath(normalizedOutputRoot, \"router/middleware\"),\n kind: \"middleware\",\n },\n ];\n\n const routerPaths = [\n ...new Set(model.routes.map((route) => route.routerPath)),\n ].sort();\n\n for (const routerPath of routerPaths) {\n const route = model.routes.find(\n (candidate) => candidate.routerPath === routerPath\n );\n\n if (!route) {\n continue;\n }\n\n modules.push({\n id: modulePath(normalizedOutputRoot, routerPath),\n kind: \"router\",\n routeId: route.routerPath,\n });\n }\n\n for (const group of model.groups) {\n const groupPath =\n group.id === \"root\" ? \"groups/root\" : `groups/${group.id}`;\n\n modules.push({\n groupId: group.id,\n id: modulePath(normalizedOutputRoot, groupPath),\n kind: \"group\",\n });\n }\n\n modules.push({\n id: modulePath(normalizedOutputRoot, \"app\"),\n kind: \"app\",\n });\n\n modules.sort((a, b) => a.id.localeCompare(b.id));\n\n return {\n modules,\n outputRoot: normalizedOutputRoot,\n };\n};\n","import { generate } from \"@/generate\";\n\nimport { analyze } from \"@/pipeline/analyze\";\nimport { loadConfig } from \"@/pipeline/config\";\nimport { discover } from \"@/pipeline/discover\";\nimport { parse } from \"@/pipeline/parse\";\nimport { plan } from \"@/pipeline/plan\";\n\nimport type { NoohCompiler } from \"@/types\";\n\nexport const createCompiler = (): NoohCompiler => ({\n analyze,\n\n compile: async (input) => {\n const configResult = await loadConfig(input);\n\n if (!configResult.config) {\n return {\n diagnostics: configResult.diagnostics,\n model: {\n config: {\n root: input.root ? input.root.replaceAll(\"\\\\\", \"/\") : \"\",\n routesRoot: \"\",\n source: input.config,\n value: {},\n },\n groups: [],\n routes: [],\n },\n output: null,\n plan: null,\n };\n }\n\n const discovered = discover(input.sources, configResult.config);\n const parsed = parse(discovered);\n const analyzed = analyze(parsed, configResult.config);\n const diagnostics = [...configResult.diagnostics, ...analyzed.diagnostics];\n\n const hasErrors = diagnostics.some(\n (diagnostic) => diagnostic.severity === \"error\"\n );\n\n if (hasErrors) {\n return {\n diagnostics,\n model: analyzed.model,\n output: null,\n plan: null,\n };\n }\n\n const compilationPlan = plan(analyzed.model, input.options?.outputRoot);\n\n const output = generate(compilationPlan, analyzed.model);\n\n return {\n diagnostics,\n model: analyzed.model,\n output,\n plan: compilationPlan,\n };\n },\n\n discover,\n generate,\n loadConfig,\n parse,\n plan,\n});\n","import { createCompiler } from \"@/compiler\";\nimport type { Compilation, CompileInput } from \"@/types\";\n\nexport const compile = (input: CompileInput): Promise<Compilation> =>\n createCompiler().compile(input);\n","import type { GeneratedModule, GeneratedOutput, OutputDiff } from \"@/types\";\n\nexport const diff = (\n previous: GeneratedOutput,\n next: GeneratedOutput\n): OutputDiff => {\n const previousMap = new Map(\n previous.modules.map((module) => [module.id, module])\n );\n\n const nextMap = new Map(next.modules.map((module) => [module.id, module]));\n\n const added: GeneratedModule[] = [];\n const changed: GeneratedModule[] = [];\n const unchanged: GeneratedModule[] = [];\n const removed: string[] = [];\n\n for (const module of next.modules) {\n const previousModule = previousMap.get(module.id);\n\n if (!previousModule) {\n added.push(module);\n continue;\n }\n\n if (previousModule.code !== module.code) {\n changed.push(module);\n continue;\n }\n\n unchanged.push(module);\n }\n\n for (const module of previous.modules) {\n if (!nextMap.has(module.id)) {\n removed.push(module.id);\n }\n }\n\n return {\n added,\n changed,\n removed,\n unchanged,\n };\n};\n","import { createCompiler } from \"@/compiler\";\nimport type { Compilation, RecompileInput } from \"@/types\";\n\nexport const recompile = (input: RecompileInput): Promise<Compilation> => {\n const compiler = createCompiler();\n\n const config = input.config ?? input.previous.model.config.source;\n\n return compiler.compile({\n config,\n loader: input.loader,\n root: input.previous.model.config.root,\n\n ...(input.options !== undefined && {\n options: input.options,\n }),\n\n sources: input.snapshot,\n });\n};\n"],"mappings":"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IAAI,CAAC;GACnE;GACA;GACA;EACF,CAAC,CAAC,KAAK,IAGF;EACH,IAAI;EACJ,MAAM;CACR;AACF;;;ACrKA,MAAa,4BACX,SACoB;CACpB,MAAM,WAAW,GAAG,KAAK,WAAW;CAEpC,OAAO;EACL,MAAM;GACJ;GACA;GACA;GACA;GACA;EACF,CAAC,CAAC,KAAK,IAAI;EACX,IAAI;EACJ,MAAM;CACR;AACF;;;ACVA,MAAM,qBAAqB;CACzB;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,wBAA8D;CAClE,KAAK;CACL,QAAQ;CACR,KAAK;CACL,MAAM;CACN,SAAS;CACT,OAAO;CACP,MAAM;CACN,KAAK;AACP;AAEA,MAAM,sBACJ,OACA,eAEA,MAAM,OACH,QAAQ,UAAU,MAAM,eAAe,UAAU,CAAC,CAClD,MAAM,GAAG,MAAM,EAAE,OAAO,cAAc,EAAE,MAAM,CAAC;AAEpD,MAAM,gBAAgB,UAA8B;CAClD,MAAM,EAAE,WAAW;CACnB,MAAM,eAAe,sBAAsB;CAI3C,OAAO;EACL,eAHW,KAAK,UAAU,mBAAmB,MAAM,SAAS,CAG1C,EAAE;EACpB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,GAAG,mBAAmB,KACnB,WACC,QAAQ,KAAK,UAAU,MAAM,EAAE,oDAAoD,KAAK,UAAU,MAAM,EAAE,kBAAkB,KAAK,UAAU,MAAM,EAAE,WACvJ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,mBAAmB,aAAa;EAChC;EACA;EACA;EACA,mBAAmB,aAAa;EAChC;EACA;EACA;EACA;EACA;EACA;EACA;EACA,mBAAmB,aAAa;EAChC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,GAAG,mBAAmB,KACnB,WACC,6BAA6B,OAAO,+BAA+B,KAAK,UAAU,MAAM,EAAE,qBAAqB,OAA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"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@nooh-ts/compiler",
3
+ "version": "0.1.0",
4
+ "description": "The compiler core for Nooh.",
5
+ "keywords": [
6
+ "nooh",
7
+ "hono",
8
+ "compiler",
9
+ "typescript",
10
+ "codegen"
11
+ ],
12
+ "homepage": "https://nooh-ts.pages.dev",
13
+ "bugs": {
14
+ "url": "https://github.com/nehu3n/nooh/issues"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/nehu3n/nooh.git",
19
+ "directory": "packages/compiler"
20
+ },
21
+ "license": "MIT",
22
+ "type": "module",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.mts",
26
+ "import": "./dist/index.mjs"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "readme.md"
32
+ ],
33
+ "devDependencies": {
34
+ "tsdown": "^0.23.0",
35
+ "typescript": "^7.0.2",
36
+ "vitest": "^5.0.0"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "scripts": {
42
+ "build": "tsdown",
43
+ "dev": "tsdown --watch",
44
+ "test": "vitest",
45
+ "typecheck": "tsc --noEmit"
46
+ }
47
+ }
package/readme.md ADDED
File without changes