@mokup/cli 1.0.9 → 1.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.
- package/dist/index.cjs +153 -47
- package/dist/index.d.cts +35 -17
- package/dist/index.d.mts +35 -17
- package/dist/index.d.ts +35 -17
- package/dist/index.mjs +146 -42
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -7,6 +7,7 @@ const node_buffer = require('node:buffer');
|
|
|
7
7
|
const node_module = require('node:module');
|
|
8
8
|
const node_url = require('node:url');
|
|
9
9
|
const esbuild = require('@mokup/shared/esbuild');
|
|
10
|
+
const pathUtils = require('@mokup/shared/path-utils');
|
|
10
11
|
const runtime = require('@mokup/runtime');
|
|
11
12
|
const jsoncParser = require('@mokup/shared/jsonc-parser');
|
|
12
13
|
const node = require('@mokup/server/node');
|
|
@@ -130,12 +131,16 @@ async function loadConfig(file) {
|
|
|
130
131
|
if (!mod) {
|
|
131
132
|
return null;
|
|
132
133
|
}
|
|
133
|
-
const
|
|
134
|
+
const raw = mod?.default ?? mod;
|
|
135
|
+
const value = isPromise$1(raw) ? await raw : raw;
|
|
134
136
|
if (!value || typeof value !== "object") {
|
|
135
137
|
return null;
|
|
136
138
|
}
|
|
137
139
|
return value;
|
|
138
140
|
}
|
|
141
|
+
function isPromise$1(value) {
|
|
142
|
+
return !!value && typeof value.then === "function";
|
|
143
|
+
}
|
|
139
144
|
function normalizeMiddlewares(value, source, log, position) {
|
|
140
145
|
if (!value) {
|
|
141
146
|
return [];
|
|
@@ -264,10 +269,6 @@ async function resolveDirectoryConfig(params) {
|
|
|
264
269
|
};
|
|
265
270
|
}
|
|
266
271
|
|
|
267
|
-
function toPosix(value) {
|
|
268
|
-
return value.replace(/\\/g, "/");
|
|
269
|
-
}
|
|
270
|
-
|
|
271
272
|
const supportedExtensions = /* @__PURE__ */ new Set([
|
|
272
273
|
".json",
|
|
273
274
|
".jsonc",
|
|
@@ -318,34 +319,10 @@ function resolveDirs(dir, root) {
|
|
|
318
319
|
);
|
|
319
320
|
return Array.from(new Set(normalized));
|
|
320
321
|
}
|
|
321
|
-
function testPatterns(patterns, value) {
|
|
322
|
-
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
323
|
-
return list.some((pattern) => pattern.test(value));
|
|
324
|
-
}
|
|
325
|
-
function matchesFilter(file, include, exclude) {
|
|
326
|
-
const normalized = toPosix(file);
|
|
327
|
-
if (exclude && testPatterns(exclude, normalized)) {
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
if (include) {
|
|
331
|
-
return testPatterns(include, normalized);
|
|
332
|
-
}
|
|
333
|
-
return true;
|
|
334
|
-
}
|
|
335
322
|
function normalizeIgnorePrefix(value, fallback = ["."]) {
|
|
336
323
|
const list = typeof value === "undefined" ? fallback : Array.isArray(value) ? value : [value];
|
|
337
324
|
return list.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
338
325
|
}
|
|
339
|
-
function hasIgnoredPrefix(file, rootDir, prefixes) {
|
|
340
|
-
if (prefixes.length === 0) {
|
|
341
|
-
return false;
|
|
342
|
-
}
|
|
343
|
-
const relativePath = toPosix(pathe.relative(rootDir, file));
|
|
344
|
-
const segments = relativePath.split("/");
|
|
345
|
-
return segments.some(
|
|
346
|
-
(segment) => prefixes.some((prefix) => segment.startsWith(prefix))
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
326
|
function isSupportedFile(file) {
|
|
350
327
|
if (file.endsWith(".d.ts")) {
|
|
351
328
|
return false;
|
|
@@ -363,7 +340,7 @@ function getHandlerModulePath(file, handlersDir, root) {
|
|
|
363
340
|
const relNoExt = `${relFromRoot.slice(0, relFromRoot.length - ext.length)}.mjs`;
|
|
364
341
|
const outputPath = pathe.join(handlersDir, relNoExt);
|
|
365
342
|
const relFromOutDir = pathe.relative(pathe.dirname(handlersDir), outputPath);
|
|
366
|
-
const normalized = toPosix(relFromOutDir);
|
|
343
|
+
const normalized = pathUtils.toPosix(relFromOutDir);
|
|
367
344
|
return normalized.startsWith(".") ? normalized : `./${normalized}`;
|
|
368
345
|
}
|
|
369
346
|
async function writeHandlerIndex(handlerModuleMap, handlersDir, outDir) {
|
|
@@ -375,7 +352,7 @@ async function writeHandlerIndex(handlerModuleMap, handlersDir, outDir) {
|
|
|
375
352
|
const entries = [];
|
|
376
353
|
modulePaths.forEach((modulePath, index) => {
|
|
377
354
|
const absolutePath = pathe.resolve(outDir, modulePath);
|
|
378
|
-
const relImport = toPosix(pathe.relative(handlersDir, absolutePath));
|
|
355
|
+
const relImport = pathUtils.toPosix(pathe.relative(handlersDir, absolutePath));
|
|
379
356
|
const importPath = relImport.startsWith(".") ? relImport : `./${relImport}`;
|
|
380
357
|
const name = `module${index}`;
|
|
381
358
|
imports.push(`import * as ${name} from '${importPath}'`);
|
|
@@ -509,7 +486,7 @@ function stripMethodSuffix(base) {
|
|
|
509
486
|
};
|
|
510
487
|
}
|
|
511
488
|
function deriveRouteFromFile(file, rootDir, log) {
|
|
512
|
-
const rel = toPosix(pathe.relative(rootDir, file));
|
|
489
|
+
const rel = pathUtils.toPosix(pathe.relative(rootDir, file));
|
|
513
490
|
const ext = pathe.extname(rel);
|
|
514
491
|
const withoutExt = rel.slice(0, rel.length - ext.length);
|
|
515
492
|
const dir = pathe.dirname(withoutExt);
|
|
@@ -525,7 +502,7 @@ function deriveRouteFromFile(file, rootDir, log) {
|
|
|
525
502
|
return null;
|
|
526
503
|
}
|
|
527
504
|
const joined = dir === "." ? name : pathe.join(dir, name);
|
|
528
|
-
const segments = toPosix(joined).split("/");
|
|
505
|
+
const segments = pathUtils.toPosix(joined).split("/");
|
|
529
506
|
if (segments.at(-1) === "index") {
|
|
530
507
|
segments.pop();
|
|
531
508
|
}
|
|
@@ -683,7 +660,7 @@ async function buildManifest(options = {}) {
|
|
|
683
660
|
continue;
|
|
684
661
|
}
|
|
685
662
|
const effectiveIgnorePrefix = typeof config.ignorePrefix !== "undefined" ? normalizeIgnorePrefix(config.ignorePrefix, []) : globalIgnorePrefix;
|
|
686
|
-
if (hasIgnoredPrefix(fileInfo.file, fileInfo.rootDir, effectiveIgnorePrefix)) {
|
|
663
|
+
if (pathUtils.hasIgnoredPrefix(fileInfo.file, fileInfo.rootDir, effectiveIgnorePrefix)) {
|
|
687
664
|
continue;
|
|
688
665
|
}
|
|
689
666
|
if (!isSupportedFile(fileInfo.file)) {
|
|
@@ -691,7 +668,7 @@ async function buildManifest(options = {}) {
|
|
|
691
668
|
}
|
|
692
669
|
const effectiveInclude = typeof config.include !== "undefined" ? config.include : options.include;
|
|
693
670
|
const effectiveExclude = typeof config.exclude !== "undefined" ? config.exclude : options.exclude;
|
|
694
|
-
if (!matchesFilter(fileInfo.file, effectiveInclude, effectiveExclude)) {
|
|
671
|
+
if (!pathUtils.matchesFilter(fileInfo.file, effectiveInclude, effectiveExclude)) {
|
|
695
672
|
continue;
|
|
696
673
|
}
|
|
697
674
|
const derived = deriveRouteFromFile(fileInfo.file, fileInfo.rootDir, options.log);
|
|
@@ -753,7 +730,7 @@ async function buildManifest(options = {}) {
|
|
|
753
730
|
if (!response) {
|
|
754
731
|
continue;
|
|
755
732
|
}
|
|
756
|
-
const source = toPosix(pathe.relative(root, fileInfo.file));
|
|
733
|
+
const source = pathUtils.toPosix(pathe.relative(root, fileInfo.file));
|
|
757
734
|
const middlewareRefs = options.handlers === false ? [] : config.middlewares.map((entry) => {
|
|
758
735
|
handlerSources.add(entry.file);
|
|
759
736
|
const modulePath = getHandlerModulePath(entry.file, handlersDir, root);
|
|
@@ -817,12 +794,90 @@ async function buildManifest(options = {}) {
|
|
|
817
794
|
}
|
|
818
795
|
|
|
819
796
|
const middlewareSymbol = Symbol.for("mokup.config.middlewares");
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
797
|
+
const contextStack = [];
|
|
798
|
+
function getActiveContext() {
|
|
799
|
+
const context = contextStack[contextStack.length - 1];
|
|
800
|
+
if (!context) {
|
|
801
|
+
throw new Error("onBeforeAll/onAfterAll must be called inside defineConfig()");
|
|
802
|
+
}
|
|
803
|
+
return context;
|
|
804
|
+
}
|
|
805
|
+
function runWithContext(context, fn) {
|
|
806
|
+
contextStack.push(context);
|
|
807
|
+
try {
|
|
808
|
+
const result = fn();
|
|
809
|
+
if (isPromise(result)) {
|
|
810
|
+
return result.finally(() => {
|
|
811
|
+
contextStack.pop();
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
contextStack.pop();
|
|
815
|
+
return result;
|
|
816
|
+
} catch (error) {
|
|
817
|
+
contextStack.pop();
|
|
818
|
+
throw error;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
function isPromise(value) {
|
|
822
|
+
return !!value && typeof value.then === "function";
|
|
823
|
+
}
|
|
824
|
+
function normalizeHookError(policy) {
|
|
825
|
+
if (policy === "throw" || policy === "silent") {
|
|
826
|
+
return policy;
|
|
827
|
+
}
|
|
828
|
+
return "warn";
|
|
829
|
+
}
|
|
830
|
+
function reportHookError(error, policy) {
|
|
831
|
+
if (policy === "silent") {
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
if (policy === "warn") {
|
|
835
|
+
console.warn("[@mokup/cli] defineConfig hook failed:", error);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
function runHookSequence(stage, hooks, policy, setStage) {
|
|
839
|
+
if (hooks.length === 0) {
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
setStage(stage);
|
|
843
|
+
let chain = null;
|
|
844
|
+
const runHook = (hook) => {
|
|
845
|
+
try {
|
|
846
|
+
const result = hook();
|
|
847
|
+
if (isPromise(result)) {
|
|
848
|
+
return result.catch((error) => {
|
|
849
|
+
if (policy === "throw") {
|
|
850
|
+
throw error;
|
|
851
|
+
}
|
|
852
|
+
reportHookError(error, policy);
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
return void 0;
|
|
856
|
+
} catch (error) {
|
|
857
|
+
if (policy === "throw") {
|
|
858
|
+
throw error;
|
|
859
|
+
}
|
|
860
|
+
reportHookError(error, policy);
|
|
861
|
+
return void 0;
|
|
824
862
|
}
|
|
825
863
|
};
|
|
864
|
+
for (const hook of hooks) {
|
|
865
|
+
if (chain) {
|
|
866
|
+
chain = chain.then(() => runHook(hook));
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
const result = runHook(hook);
|
|
870
|
+
if (isPromise(result)) {
|
|
871
|
+
chain = result;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (!chain) {
|
|
875
|
+
setStage("normal");
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
return chain.finally(() => {
|
|
879
|
+
setStage("normal");
|
|
880
|
+
});
|
|
826
881
|
}
|
|
827
882
|
function attachMetadata(config, meta) {
|
|
828
883
|
Object.defineProperty(config, middlewareSymbol, {
|
|
@@ -831,21 +886,70 @@ function attachMetadata(config, meta) {
|
|
|
831
886
|
});
|
|
832
887
|
return config;
|
|
833
888
|
}
|
|
889
|
+
function normalizeConfig(value) {
|
|
890
|
+
return value && typeof value === "object" ? value : {};
|
|
891
|
+
}
|
|
892
|
+
function onBeforeAll(handler) {
|
|
893
|
+
if (typeof handler !== "function") {
|
|
894
|
+
throw new TypeError("onBeforeAll expects a function");
|
|
895
|
+
}
|
|
896
|
+
const context = getActiveContext();
|
|
897
|
+
context.hooks.pre.push(handler);
|
|
898
|
+
}
|
|
899
|
+
function onAfterAll(handler) {
|
|
900
|
+
if (typeof handler !== "function") {
|
|
901
|
+
throw new TypeError("onAfterAll expects a function");
|
|
902
|
+
}
|
|
903
|
+
const context = getActiveContext();
|
|
904
|
+
context.hooks.post.push(handler);
|
|
905
|
+
}
|
|
834
906
|
function defineConfig(input) {
|
|
835
907
|
if (typeof input === "function") {
|
|
836
908
|
const pre = [];
|
|
837
909
|
const normal = [];
|
|
838
910
|
const post = [];
|
|
911
|
+
let stage = "normal";
|
|
912
|
+
const app = {
|
|
913
|
+
use: (...handlers) => {
|
|
914
|
+
if (stage === "pre") {
|
|
915
|
+
pre.push(...handlers);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
if (stage === "post") {
|
|
919
|
+
post.push(...handlers);
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
normal.push(...handlers);
|
|
923
|
+
}
|
|
924
|
+
};
|
|
839
925
|
const context = {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
926
|
+
app,
|
|
927
|
+
hooks: { pre: [], post: [] },
|
|
928
|
+
setStage: (next) => {
|
|
929
|
+
stage = next;
|
|
930
|
+
}
|
|
843
931
|
};
|
|
844
|
-
const result =
|
|
845
|
-
const
|
|
846
|
-
|
|
932
|
+
const result = runWithContext(context, () => input({ app }));
|
|
933
|
+
const finalize = (value) => {
|
|
934
|
+
const config2 = normalizeConfig(value);
|
|
935
|
+
const policy = normalizeHookError(config2.hookError);
|
|
936
|
+
const preResult = runHookSequence("pre", context.hooks.pre, policy, context.setStage);
|
|
937
|
+
const runPost = () => runHookSequence("post", context.hooks.post, policy, context.setStage);
|
|
938
|
+
if (isPromise(preResult)) {
|
|
939
|
+
return preResult.then(runPost).then(() => attachMetadata(config2, { pre, normal, post }));
|
|
940
|
+
}
|
|
941
|
+
const postResult = runPost();
|
|
942
|
+
if (isPromise(postResult)) {
|
|
943
|
+
return postResult.then(() => attachMetadata(config2, { pre, normal, post }));
|
|
944
|
+
}
|
|
945
|
+
return attachMetadata(config2, { pre, normal, post });
|
|
946
|
+
};
|
|
947
|
+
if (isPromise(result)) {
|
|
948
|
+
return result.then(finalize);
|
|
949
|
+
}
|
|
950
|
+
return finalize(result);
|
|
847
951
|
}
|
|
848
|
-
const config = input
|
|
952
|
+
const config = normalizeConfig(input);
|
|
849
953
|
return attachMetadata(config, { pre: [], normal: [], post: [] });
|
|
850
954
|
}
|
|
851
955
|
|
|
@@ -992,4 +1096,6 @@ async function runCli(argv = process__default.argv) {
|
|
|
992
1096
|
exports.buildManifest = buildManifest;
|
|
993
1097
|
exports.createCli = createCli;
|
|
994
1098
|
exports.defineConfig = defineConfig;
|
|
1099
|
+
exports.onAfterAll = onAfterAll;
|
|
1100
|
+
exports.onBeforeAll = onBeforeAll;
|
|
995
1101
|
exports.runCli = runCli;
|
package/dist/index.d.cts
CHANGED
|
@@ -164,6 +164,12 @@ interface RouteDirectoryConfig {
|
|
|
164
164
|
* @default undefined
|
|
165
165
|
*/
|
|
166
166
|
middleware?: MiddlewareHandler | MiddlewareHandler[];
|
|
167
|
+
/**
|
|
168
|
+
* Error handling policy for defineConfig hooks.
|
|
169
|
+
*
|
|
170
|
+
* @default "warn"
|
|
171
|
+
*/
|
|
172
|
+
hookError?: HookErrorPolicy;
|
|
167
173
|
}
|
|
168
174
|
/**
|
|
169
175
|
* Middleware execution position.
|
|
@@ -174,6 +180,10 @@ interface RouteDirectoryConfig {
|
|
|
174
180
|
* const position: MiddlewarePosition = 'pre'
|
|
175
181
|
*/
|
|
176
182
|
type MiddlewarePosition = 'pre' | 'normal' | 'post';
|
|
183
|
+
/**
|
|
184
|
+
* Error handling policy for config hooks.
|
|
185
|
+
*/
|
|
186
|
+
type HookErrorPolicy = 'throw' | 'warn' | 'silent';
|
|
177
187
|
/**
|
|
178
188
|
* Middleware registry used by defineConfig.
|
|
179
189
|
*
|
|
@@ -202,39 +212,47 @@ declare function buildManifest(options?: BuildOptions): Promise<{
|
|
|
202
212
|
manifestPath: string;
|
|
203
213
|
}>;
|
|
204
214
|
|
|
215
|
+
type HookHandler = () => void | Promise<void>;
|
|
216
|
+
interface ConfigApp {
|
|
217
|
+
use: (...handlers: MiddlewareHandler[]) => void;
|
|
218
|
+
}
|
|
205
219
|
type DefineConfigFactory = (context: {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
220
|
+
app: ConfigApp;
|
|
221
|
+
}) => RouteDirectoryConfig | void | Promise<RouteDirectoryConfig | void>;
|
|
222
|
+
declare function onBeforeAll(handler: HookHandler): void;
|
|
223
|
+
declare function onAfterAll(handler: HookHandler): void;
|
|
210
224
|
/**
|
|
211
|
-
* Define a directory config with
|
|
225
|
+
* Define a directory config with hook-based middleware registration.
|
|
212
226
|
*
|
|
213
227
|
* @param input - Config object or factory callback.
|
|
214
228
|
* @returns Route directory config with middleware metadata.
|
|
215
229
|
*
|
|
216
230
|
* @example
|
|
217
|
-
* import { defineConfig } from '@mokup/cli'
|
|
231
|
+
* import { defineConfig, onBeforeAll, onAfterAll } from '@mokup/cli'
|
|
218
232
|
*
|
|
219
|
-
* export default defineConfig(({
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
233
|
+
* export default defineConfig(({ app }) => {
|
|
234
|
+
* onBeforeAll(() => {
|
|
235
|
+
* app.use(async (c, next) => {
|
|
236
|
+
* c.header('x-before', '1')
|
|
237
|
+
* await next()
|
|
238
|
+
* })
|
|
223
239
|
* })
|
|
224
240
|
*
|
|
225
|
-
*
|
|
241
|
+
* app.use(async (_c, next) => {
|
|
226
242
|
* await next()
|
|
227
243
|
* })
|
|
228
244
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
245
|
+
* onAfterAll(() => {
|
|
246
|
+
* app.use(async (c, next) => {
|
|
247
|
+
* await next()
|
|
248
|
+
* c.header('x-after', '1')
|
|
249
|
+
* })
|
|
232
250
|
* })
|
|
233
251
|
*
|
|
234
252
|
* return { delay: 120 }
|
|
235
253
|
* })
|
|
236
254
|
*/
|
|
237
|
-
declare function defineConfig(input: RouteDirectoryConfig | DefineConfigFactory): RouteDirectoryConfig
|
|
255
|
+
declare function defineConfig(input: RouteDirectoryConfig | DefineConfigFactory): RouteDirectoryConfig | Promise<RouteDirectoryConfig>;
|
|
238
256
|
|
|
239
257
|
/**
|
|
240
258
|
* Create the mokup CLI program instance.
|
|
@@ -260,5 +278,5 @@ declare function createCli(): Command;
|
|
|
260
278
|
*/
|
|
261
279
|
declare function runCli(argv?: string[]): Promise<void>;
|
|
262
280
|
|
|
263
|
-
export { buildManifest, createCli, defineConfig, runCli };
|
|
264
|
-
export type { BuildOptions, MiddlewarePosition, MiddlewareRegistry, RouteDirectoryConfig, RouteRule };
|
|
281
|
+
export { buildManifest, createCli, defineConfig, onAfterAll, onBeforeAll, runCli };
|
|
282
|
+
export type { BuildOptions, HookErrorPolicy, MiddlewarePosition, MiddlewareRegistry, RouteDirectoryConfig, RouteRule };
|
package/dist/index.d.mts
CHANGED
|
@@ -164,6 +164,12 @@ interface RouteDirectoryConfig {
|
|
|
164
164
|
* @default undefined
|
|
165
165
|
*/
|
|
166
166
|
middleware?: MiddlewareHandler | MiddlewareHandler[];
|
|
167
|
+
/**
|
|
168
|
+
* Error handling policy for defineConfig hooks.
|
|
169
|
+
*
|
|
170
|
+
* @default "warn"
|
|
171
|
+
*/
|
|
172
|
+
hookError?: HookErrorPolicy;
|
|
167
173
|
}
|
|
168
174
|
/**
|
|
169
175
|
* Middleware execution position.
|
|
@@ -174,6 +180,10 @@ interface RouteDirectoryConfig {
|
|
|
174
180
|
* const position: MiddlewarePosition = 'pre'
|
|
175
181
|
*/
|
|
176
182
|
type MiddlewarePosition = 'pre' | 'normal' | 'post';
|
|
183
|
+
/**
|
|
184
|
+
* Error handling policy for config hooks.
|
|
185
|
+
*/
|
|
186
|
+
type HookErrorPolicy = 'throw' | 'warn' | 'silent';
|
|
177
187
|
/**
|
|
178
188
|
* Middleware registry used by defineConfig.
|
|
179
189
|
*
|
|
@@ -202,39 +212,47 @@ declare function buildManifest(options?: BuildOptions): Promise<{
|
|
|
202
212
|
manifestPath: string;
|
|
203
213
|
}>;
|
|
204
214
|
|
|
215
|
+
type HookHandler = () => void | Promise<void>;
|
|
216
|
+
interface ConfigApp {
|
|
217
|
+
use: (...handlers: MiddlewareHandler[]) => void;
|
|
218
|
+
}
|
|
205
219
|
type DefineConfigFactory = (context: {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
220
|
+
app: ConfigApp;
|
|
221
|
+
}) => RouteDirectoryConfig | void | Promise<RouteDirectoryConfig | void>;
|
|
222
|
+
declare function onBeforeAll(handler: HookHandler): void;
|
|
223
|
+
declare function onAfterAll(handler: HookHandler): void;
|
|
210
224
|
/**
|
|
211
|
-
* Define a directory config with
|
|
225
|
+
* Define a directory config with hook-based middleware registration.
|
|
212
226
|
*
|
|
213
227
|
* @param input - Config object or factory callback.
|
|
214
228
|
* @returns Route directory config with middleware metadata.
|
|
215
229
|
*
|
|
216
230
|
* @example
|
|
217
|
-
* import { defineConfig } from '@mokup/cli'
|
|
231
|
+
* import { defineConfig, onBeforeAll, onAfterAll } from '@mokup/cli'
|
|
218
232
|
*
|
|
219
|
-
* export default defineConfig(({
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
233
|
+
* export default defineConfig(({ app }) => {
|
|
234
|
+
* onBeforeAll(() => {
|
|
235
|
+
* app.use(async (c, next) => {
|
|
236
|
+
* c.header('x-before', '1')
|
|
237
|
+
* await next()
|
|
238
|
+
* })
|
|
223
239
|
* })
|
|
224
240
|
*
|
|
225
|
-
*
|
|
241
|
+
* app.use(async (_c, next) => {
|
|
226
242
|
* await next()
|
|
227
243
|
* })
|
|
228
244
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
245
|
+
* onAfterAll(() => {
|
|
246
|
+
* app.use(async (c, next) => {
|
|
247
|
+
* await next()
|
|
248
|
+
* c.header('x-after', '1')
|
|
249
|
+
* })
|
|
232
250
|
* })
|
|
233
251
|
*
|
|
234
252
|
* return { delay: 120 }
|
|
235
253
|
* })
|
|
236
254
|
*/
|
|
237
|
-
declare function defineConfig(input: RouteDirectoryConfig | DefineConfigFactory): RouteDirectoryConfig
|
|
255
|
+
declare function defineConfig(input: RouteDirectoryConfig | DefineConfigFactory): RouteDirectoryConfig | Promise<RouteDirectoryConfig>;
|
|
238
256
|
|
|
239
257
|
/**
|
|
240
258
|
* Create the mokup CLI program instance.
|
|
@@ -260,5 +278,5 @@ declare function createCli(): Command;
|
|
|
260
278
|
*/
|
|
261
279
|
declare function runCli(argv?: string[]): Promise<void>;
|
|
262
280
|
|
|
263
|
-
export { buildManifest, createCli, defineConfig, runCli };
|
|
264
|
-
export type { BuildOptions, MiddlewarePosition, MiddlewareRegistry, RouteDirectoryConfig, RouteRule };
|
|
281
|
+
export { buildManifest, createCli, defineConfig, onAfterAll, onBeforeAll, runCli };
|
|
282
|
+
export type { BuildOptions, HookErrorPolicy, MiddlewarePosition, MiddlewareRegistry, RouteDirectoryConfig, RouteRule };
|
package/dist/index.d.ts
CHANGED
|
@@ -164,6 +164,12 @@ interface RouteDirectoryConfig {
|
|
|
164
164
|
* @default undefined
|
|
165
165
|
*/
|
|
166
166
|
middleware?: MiddlewareHandler | MiddlewareHandler[];
|
|
167
|
+
/**
|
|
168
|
+
* Error handling policy for defineConfig hooks.
|
|
169
|
+
*
|
|
170
|
+
* @default "warn"
|
|
171
|
+
*/
|
|
172
|
+
hookError?: HookErrorPolicy;
|
|
167
173
|
}
|
|
168
174
|
/**
|
|
169
175
|
* Middleware execution position.
|
|
@@ -174,6 +180,10 @@ interface RouteDirectoryConfig {
|
|
|
174
180
|
* const position: MiddlewarePosition = 'pre'
|
|
175
181
|
*/
|
|
176
182
|
type MiddlewarePosition = 'pre' | 'normal' | 'post';
|
|
183
|
+
/**
|
|
184
|
+
* Error handling policy for config hooks.
|
|
185
|
+
*/
|
|
186
|
+
type HookErrorPolicy = 'throw' | 'warn' | 'silent';
|
|
177
187
|
/**
|
|
178
188
|
* Middleware registry used by defineConfig.
|
|
179
189
|
*
|
|
@@ -202,39 +212,47 @@ declare function buildManifest(options?: BuildOptions): Promise<{
|
|
|
202
212
|
manifestPath: string;
|
|
203
213
|
}>;
|
|
204
214
|
|
|
215
|
+
type HookHandler = () => void | Promise<void>;
|
|
216
|
+
interface ConfigApp {
|
|
217
|
+
use: (...handlers: MiddlewareHandler[]) => void;
|
|
218
|
+
}
|
|
205
219
|
type DefineConfigFactory = (context: {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
220
|
+
app: ConfigApp;
|
|
221
|
+
}) => RouteDirectoryConfig | void | Promise<RouteDirectoryConfig | void>;
|
|
222
|
+
declare function onBeforeAll(handler: HookHandler): void;
|
|
223
|
+
declare function onAfterAll(handler: HookHandler): void;
|
|
210
224
|
/**
|
|
211
|
-
* Define a directory config with
|
|
225
|
+
* Define a directory config with hook-based middleware registration.
|
|
212
226
|
*
|
|
213
227
|
* @param input - Config object or factory callback.
|
|
214
228
|
* @returns Route directory config with middleware metadata.
|
|
215
229
|
*
|
|
216
230
|
* @example
|
|
217
|
-
* import { defineConfig } from '@mokup/cli'
|
|
231
|
+
* import { defineConfig, onBeforeAll, onAfterAll } from '@mokup/cli'
|
|
218
232
|
*
|
|
219
|
-
* export default defineConfig(({
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
233
|
+
* export default defineConfig(({ app }) => {
|
|
234
|
+
* onBeforeAll(() => {
|
|
235
|
+
* app.use(async (c, next) => {
|
|
236
|
+
* c.header('x-before', '1')
|
|
237
|
+
* await next()
|
|
238
|
+
* })
|
|
223
239
|
* })
|
|
224
240
|
*
|
|
225
|
-
*
|
|
241
|
+
* app.use(async (_c, next) => {
|
|
226
242
|
* await next()
|
|
227
243
|
* })
|
|
228
244
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
245
|
+
* onAfterAll(() => {
|
|
246
|
+
* app.use(async (c, next) => {
|
|
247
|
+
* await next()
|
|
248
|
+
* c.header('x-after', '1')
|
|
249
|
+
* })
|
|
232
250
|
* })
|
|
233
251
|
*
|
|
234
252
|
* return { delay: 120 }
|
|
235
253
|
* })
|
|
236
254
|
*/
|
|
237
|
-
declare function defineConfig(input: RouteDirectoryConfig | DefineConfigFactory): RouteDirectoryConfig
|
|
255
|
+
declare function defineConfig(input: RouteDirectoryConfig | DefineConfigFactory): RouteDirectoryConfig | Promise<RouteDirectoryConfig>;
|
|
238
256
|
|
|
239
257
|
/**
|
|
240
258
|
* Create the mokup CLI program instance.
|
|
@@ -260,5 +278,5 @@ declare function createCli(): Command;
|
|
|
260
278
|
*/
|
|
261
279
|
declare function runCli(argv?: string[]): Promise<void>;
|
|
262
280
|
|
|
263
|
-
export { buildManifest, createCli, defineConfig, runCli };
|
|
264
|
-
export type { BuildOptions, MiddlewarePosition, MiddlewareRegistry, RouteDirectoryConfig, RouteRule };
|
|
281
|
+
export { buildManifest, createCli, defineConfig, onAfterAll, onBeforeAll, runCli };
|
|
282
|
+
export type { BuildOptions, HookErrorPolicy, MiddlewarePosition, MiddlewareRegistry, RouteDirectoryConfig, RouteRule };
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { promises } from 'node:fs';
|
|
2
2
|
import process, { cwd } from 'node:process';
|
|
3
|
-
import { join, normalize, dirname, resolve, isAbsolute,
|
|
3
|
+
import { join, normalize, dirname, resolve, isAbsolute, basename, extname, relative } from '@mokup/shared/pathe';
|
|
4
4
|
import { Buffer } from 'node:buffer';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
7
|
import { build } from '@mokup/shared/esbuild';
|
|
8
|
+
import { toPosix, hasIgnoredPrefix, matchesFilter } from '@mokup/shared/path-utils';
|
|
8
9
|
import { parseRouteTemplate, compareRouteScore } from '@mokup/runtime';
|
|
9
10
|
import { parse } from '@mokup/shared/jsonc-parser';
|
|
10
11
|
import { createFetchServer, serve } from '@mokup/server/node';
|
|
@@ -123,12 +124,16 @@ async function loadConfig(file) {
|
|
|
123
124
|
if (!mod) {
|
|
124
125
|
return null;
|
|
125
126
|
}
|
|
126
|
-
const
|
|
127
|
+
const raw = mod?.default ?? mod;
|
|
128
|
+
const value = isPromise$1(raw) ? await raw : raw;
|
|
127
129
|
if (!value || typeof value !== "object") {
|
|
128
130
|
return null;
|
|
129
131
|
}
|
|
130
132
|
return value;
|
|
131
133
|
}
|
|
134
|
+
function isPromise$1(value) {
|
|
135
|
+
return !!value && typeof value.then === "function";
|
|
136
|
+
}
|
|
132
137
|
function normalizeMiddlewares(value, source, log, position) {
|
|
133
138
|
if (!value) {
|
|
134
139
|
return [];
|
|
@@ -257,10 +262,6 @@ async function resolveDirectoryConfig(params) {
|
|
|
257
262
|
};
|
|
258
263
|
}
|
|
259
264
|
|
|
260
|
-
function toPosix(value) {
|
|
261
|
-
return value.replace(/\\/g, "/");
|
|
262
|
-
}
|
|
263
|
-
|
|
264
265
|
const supportedExtensions = /* @__PURE__ */ new Set([
|
|
265
266
|
".json",
|
|
266
267
|
".jsonc",
|
|
@@ -311,34 +312,10 @@ function resolveDirs(dir, root) {
|
|
|
311
312
|
);
|
|
312
313
|
return Array.from(new Set(normalized));
|
|
313
314
|
}
|
|
314
|
-
function testPatterns(patterns, value) {
|
|
315
|
-
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
316
|
-
return list.some((pattern) => pattern.test(value));
|
|
317
|
-
}
|
|
318
|
-
function matchesFilter(file, include, exclude) {
|
|
319
|
-
const normalized = toPosix(file);
|
|
320
|
-
if (exclude && testPatterns(exclude, normalized)) {
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
if (include) {
|
|
324
|
-
return testPatterns(include, normalized);
|
|
325
|
-
}
|
|
326
|
-
return true;
|
|
327
|
-
}
|
|
328
315
|
function normalizeIgnorePrefix(value, fallback = ["."]) {
|
|
329
316
|
const list = typeof value === "undefined" ? fallback : Array.isArray(value) ? value : [value];
|
|
330
317
|
return list.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
331
318
|
}
|
|
332
|
-
function hasIgnoredPrefix(file, rootDir, prefixes) {
|
|
333
|
-
if (prefixes.length === 0) {
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
const relativePath = toPosix(relative(rootDir, file));
|
|
337
|
-
const segments = relativePath.split("/");
|
|
338
|
-
return segments.some(
|
|
339
|
-
(segment) => prefixes.some((prefix) => segment.startsWith(prefix))
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
319
|
function isSupportedFile(file) {
|
|
343
320
|
if (file.endsWith(".d.ts")) {
|
|
344
321
|
return false;
|
|
@@ -810,12 +787,90 @@ async function buildManifest(options = {}) {
|
|
|
810
787
|
}
|
|
811
788
|
|
|
812
789
|
const middlewareSymbol = Symbol.for("mokup.config.middlewares");
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
790
|
+
const contextStack = [];
|
|
791
|
+
function getActiveContext() {
|
|
792
|
+
const context = contextStack[contextStack.length - 1];
|
|
793
|
+
if (!context) {
|
|
794
|
+
throw new Error("onBeforeAll/onAfterAll must be called inside defineConfig()");
|
|
795
|
+
}
|
|
796
|
+
return context;
|
|
797
|
+
}
|
|
798
|
+
function runWithContext(context, fn) {
|
|
799
|
+
contextStack.push(context);
|
|
800
|
+
try {
|
|
801
|
+
const result = fn();
|
|
802
|
+
if (isPromise(result)) {
|
|
803
|
+
return result.finally(() => {
|
|
804
|
+
contextStack.pop();
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
contextStack.pop();
|
|
808
|
+
return result;
|
|
809
|
+
} catch (error) {
|
|
810
|
+
contextStack.pop();
|
|
811
|
+
throw error;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
function isPromise(value) {
|
|
815
|
+
return !!value && typeof value.then === "function";
|
|
816
|
+
}
|
|
817
|
+
function normalizeHookError(policy) {
|
|
818
|
+
if (policy === "throw" || policy === "silent") {
|
|
819
|
+
return policy;
|
|
820
|
+
}
|
|
821
|
+
return "warn";
|
|
822
|
+
}
|
|
823
|
+
function reportHookError(error, policy) {
|
|
824
|
+
if (policy === "silent") {
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
if (policy === "warn") {
|
|
828
|
+
console.warn("[@mokup/cli] defineConfig hook failed:", error);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
function runHookSequence(stage, hooks, policy, setStage) {
|
|
832
|
+
if (hooks.length === 0) {
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
setStage(stage);
|
|
836
|
+
let chain = null;
|
|
837
|
+
const runHook = (hook) => {
|
|
838
|
+
try {
|
|
839
|
+
const result = hook();
|
|
840
|
+
if (isPromise(result)) {
|
|
841
|
+
return result.catch((error) => {
|
|
842
|
+
if (policy === "throw") {
|
|
843
|
+
throw error;
|
|
844
|
+
}
|
|
845
|
+
reportHookError(error, policy);
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
return void 0;
|
|
849
|
+
} catch (error) {
|
|
850
|
+
if (policy === "throw") {
|
|
851
|
+
throw error;
|
|
852
|
+
}
|
|
853
|
+
reportHookError(error, policy);
|
|
854
|
+
return void 0;
|
|
817
855
|
}
|
|
818
856
|
};
|
|
857
|
+
for (const hook of hooks) {
|
|
858
|
+
if (chain) {
|
|
859
|
+
chain = chain.then(() => runHook(hook));
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
const result = runHook(hook);
|
|
863
|
+
if (isPromise(result)) {
|
|
864
|
+
chain = result;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (!chain) {
|
|
868
|
+
setStage("normal");
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
return chain.finally(() => {
|
|
872
|
+
setStage("normal");
|
|
873
|
+
});
|
|
819
874
|
}
|
|
820
875
|
function attachMetadata(config, meta) {
|
|
821
876
|
Object.defineProperty(config, middlewareSymbol, {
|
|
@@ -824,21 +879,70 @@ function attachMetadata(config, meta) {
|
|
|
824
879
|
});
|
|
825
880
|
return config;
|
|
826
881
|
}
|
|
882
|
+
function normalizeConfig(value) {
|
|
883
|
+
return value && typeof value === "object" ? value : {};
|
|
884
|
+
}
|
|
885
|
+
function onBeforeAll(handler) {
|
|
886
|
+
if (typeof handler !== "function") {
|
|
887
|
+
throw new TypeError("onBeforeAll expects a function");
|
|
888
|
+
}
|
|
889
|
+
const context = getActiveContext();
|
|
890
|
+
context.hooks.pre.push(handler);
|
|
891
|
+
}
|
|
892
|
+
function onAfterAll(handler) {
|
|
893
|
+
if (typeof handler !== "function") {
|
|
894
|
+
throw new TypeError("onAfterAll expects a function");
|
|
895
|
+
}
|
|
896
|
+
const context = getActiveContext();
|
|
897
|
+
context.hooks.post.push(handler);
|
|
898
|
+
}
|
|
827
899
|
function defineConfig(input) {
|
|
828
900
|
if (typeof input === "function") {
|
|
829
901
|
const pre = [];
|
|
830
902
|
const normal = [];
|
|
831
903
|
const post = [];
|
|
904
|
+
let stage = "normal";
|
|
905
|
+
const app = {
|
|
906
|
+
use: (...handlers) => {
|
|
907
|
+
if (stage === "pre") {
|
|
908
|
+
pre.push(...handlers);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
if (stage === "post") {
|
|
912
|
+
post.push(...handlers);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
normal.push(...handlers);
|
|
916
|
+
}
|
|
917
|
+
};
|
|
832
918
|
const context = {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
919
|
+
app,
|
|
920
|
+
hooks: { pre: [], post: [] },
|
|
921
|
+
setStage: (next) => {
|
|
922
|
+
stage = next;
|
|
923
|
+
}
|
|
836
924
|
};
|
|
837
|
-
const result =
|
|
838
|
-
const
|
|
839
|
-
|
|
925
|
+
const result = runWithContext(context, () => input({ app }));
|
|
926
|
+
const finalize = (value) => {
|
|
927
|
+
const config2 = normalizeConfig(value);
|
|
928
|
+
const policy = normalizeHookError(config2.hookError);
|
|
929
|
+
const preResult = runHookSequence("pre", context.hooks.pre, policy, context.setStage);
|
|
930
|
+
const runPost = () => runHookSequence("post", context.hooks.post, policy, context.setStage);
|
|
931
|
+
if (isPromise(preResult)) {
|
|
932
|
+
return preResult.then(runPost).then(() => attachMetadata(config2, { pre, normal, post }));
|
|
933
|
+
}
|
|
934
|
+
const postResult = runPost();
|
|
935
|
+
if (isPromise(postResult)) {
|
|
936
|
+
return postResult.then(() => attachMetadata(config2, { pre, normal, post }));
|
|
937
|
+
}
|
|
938
|
+
return attachMetadata(config2, { pre, normal, post });
|
|
939
|
+
};
|
|
940
|
+
if (isPromise(result)) {
|
|
941
|
+
return result.then(finalize);
|
|
942
|
+
}
|
|
943
|
+
return finalize(result);
|
|
840
944
|
}
|
|
841
|
-
const config = input
|
|
945
|
+
const config = normalizeConfig(input);
|
|
842
946
|
return attachMetadata(config, { pre: [], normal: [], post: [] });
|
|
843
947
|
}
|
|
844
948
|
|
|
@@ -982,4 +1086,4 @@ async function runCli(argv = process.argv) {
|
|
|
982
1086
|
await program.parseAsync(argv);
|
|
983
1087
|
}
|
|
984
1088
|
|
|
985
|
-
export { buildManifest, createCli, defineConfig, runCli };
|
|
1089
|
+
export { buildManifest, createCli, defineConfig, onAfterAll, onBeforeAll, runCli };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mokup/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"description": "CLI for building mokup manifests and handlers.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://mokup.icebreaker.top",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"commander": "^14.0.0",
|
|
31
|
-
"@mokup/runtime": "1.0.
|
|
32
|
-
"@mokup/server": "1.
|
|
33
|
-
"@mokup/shared": "1.1.
|
|
31
|
+
"@mokup/runtime": "1.0.6",
|
|
32
|
+
"@mokup/server": "1.2.0",
|
|
33
|
+
"@mokup/shared": "1.1.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^25.0.10",
|