From a3bf7e3b3e774632b5a3298c6fe2634d26eb26c8 Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Fri, 14 Jul 2023 15:43:08 +0200 Subject: [PATCH] feat: support `ignore` to ignore scanned files (#1430) * feat: support `ignore` patterns * fix scanPlugins --- src/options.ts | 1 + src/scan.ts | 110 ++++++++++++++-------------- src/types/nitro.ts | 1 + test/fixture/api/_ignored.ts | 3 + test/fixture/middleware/_ignored.ts | 3 + test/fixture/nitro.config.ts | 1 + test/fixture/routes/_ignored.ts | 3 + test/tests.ts | 7 ++ 8 files changed, 73 insertions(+), 56 deletions(-) create mode 100644 test/fixture/api/_ignored.ts create mode 100644 test/fixture/middleware/_ignored.ts create mode 100644 test/fixture/routes/_ignored.ts diff --git a/src/options.ts b/src/options.ts index 02769fd985..d2f992d117 100644 --- a/src/options.ts +++ b/src/options.ts @@ -63,6 +63,7 @@ const NitroDefaults: NitroConfig = { }, virtual: {}, compressPublicAssets: false, + ignore: [], // Dev dev: false, diff --git a/src/scan.ts b/src/scan.ts index 4597c0130c..3e4c11468b 100644 --- a/src/scan.ts +++ b/src/scan.ts @@ -1,45 +1,53 @@ -import { resolve, join } from "pathe"; +import { relative, join } from "pathe"; import { globby } from "globby"; - import { withBase, withLeadingSlash, withoutTrailingSlash } from "ufo"; -import type { Nitro, NitroEventHandler } from "./types"; +import type { Nitro } from "./types"; export const GLOB_SCAN_PATTERN = "**/*.{js,mjs,cjs,ts,mts,cts,tsx,jsx}"; -type FileInfo = { dir: string; path: string; fullPath: string }; +type FileInfo = { path: string; fullPath: string }; const httpMethodRegex = /\.(connect|delete|get|head|options|patch|post|put|trace)/; export async function scanHandlers(nitro: Nitro) { + const middleware = await scanMiddleware(nitro); + const handlers = await Promise.all([ - scanMiddleware(nitro), - scanRoutes(nitro, "api", "/api"), - scanRoutes(nitro, "routes", "/"), + scanServerRoutes(nitro, "api", "/api"), + scanServerRoutes(nitro, "routes", "/"), ]).then((r) => r.flat()); - nitro.scannedHandlers = handlers - .flatMap((h) => h.handlers) - .filter((h, index, array) => { + nitro.scannedHandlers = [ + ...middleware, + ...handlers.filter((h, index, array) => { return ( - h.middleware || array.findIndex( (h2) => h.route === h2.route && h.method === h2.method ) === index ); - }); + }), + ]; return handlers; } -export function scanMiddleware(nitro: Nitro) { - return scanServerDir(nitro, "middleware", (file) => ({ - middleware: true, - handler: file.fullPath, - })); +export async function scanMiddleware(nitro: Nitro) { + const files = await scanFiles(nitro, "middleware"); + return files.map((file) => { + return { + middleware: true, + handler: file.fullPath, + }; + }); } -export function scanRoutes(nitro: Nitro, dir: string, prefix = "/") { - return scanServerDir(nitro, dir, (file) => { +export async function scanServerRoutes( + nitro: Nitro, + dir: "routes" | "api", + prefix = "/" +) { + const files = await scanFiles(nitro, dir); + return files.map((file) => { let route = file.path .replace(/\.[A-Za-z]+$/, "") .replace(/\[\.{3}]/g, "**") @@ -59,52 +67,42 @@ export function scanRoutes(nitro: Nitro, dir: string, prefix = "/") { return { handler: file.fullPath, lazy: true, + middlweware: false, route, method, }; }); } -async function scanServerDir( - nitro: Nitro, - name: string, - mapper: (file: FileInfo) => NitroEventHandler -) { - const dirs = nitro.options.scanDirs.map((dir) => join(dir, name)); - const files = await scanDirs(dirs); - const handlers: NitroEventHandler[] = files.map((f) => mapper(f)); - return { dirs, files, handlers }; +export async function scanPlugins(nitro: Nitro) { + const files = await scanFiles(nitro, "plugins"); + return files.map((f) => f.fullPath); } -export async function scanPlugins(nitro: Nitro) { - const plugins = []; - for (const dir of nitro.options.scanDirs) { - const pluginDir = join(dir, "plugins"); - const pluginFiles = await globby(GLOB_SCAN_PATTERN, { - cwd: pluginDir, - absolute: true, - }); - plugins.push(...pluginFiles.sort()); - } - return plugins; +async function scanFiles(nitro: Nitro, name: string): Promise { + const files = await Promise.all( + nitro.options.scanDirs.map((dir) => scanDir(nitro, dir, name)) + ).then((r) => r.flat()); + return files; } -function scanDirs(dirs: string[]): Promise { - return Promise.all( - dirs.map(async (dir) => { - const fileNames = await globby(GLOB_SCAN_PATTERN, { - cwd: dir, - dot: true, - }); - return fileNames - .map((fileName) => { - return { - dir, - path: fileName, - fullPath: resolve(dir, fileName), - }; - }) - .sort((a, b) => a.path.localeCompare(b.path)); +async function scanDir( + nitro: Nitro, + dir: string, + name: string +): Promise { + const fileNames = await globby(join(name, GLOB_SCAN_PATTERN), { + cwd: dir, + dot: true, + ignore: nitro.options.ignore, + absolute: true, + }); + return fileNames + .map((fullPath) => { + return { + fullPath, + path: relative(join(dir, name), fullPath), + }; }) - ).then((r) => r.flat()); + .sort((a, b) => a.path.localeCompare(b.path)); } diff --git a/src/types/nitro.ts b/src/types/nitro.ts index b2cbfbb6b6..32b39caa87 100644 --- a/src/types/nitro.ts +++ b/src/types/nitro.ts @@ -230,6 +230,7 @@ export interface NitroOptions extends PresetOptions { plugins: string[]; virtual: Record string | Promise)>; compressPublicAssets: boolean | CompressOptions; + ignore: string[]; // Dev dev: boolean; diff --git a/test/fixture/api/_ignored.ts b/test/fixture/api/_ignored.ts new file mode 100644 index 0000000000..c2c27dadee --- /dev/null +++ b/test/fixture/api/_ignored.ts @@ -0,0 +1,3 @@ +export default eventHandler((event) => { + throw createError("This file should be ignored!"); +}); diff --git a/test/fixture/middleware/_ignored.ts b/test/fixture/middleware/_ignored.ts new file mode 100644 index 0000000000..c2c27dadee --- /dev/null +++ b/test/fixture/middleware/_ignored.ts @@ -0,0 +1,3 @@ +export default eventHandler((event) => { + throw createError("This file should be ignored!"); +}); diff --git a/test/fixture/nitro.config.ts b/test/fixture/nitro.config.ts index a5448cea95..c62e5f7ee0 100644 --- a/test/fixture/nitro.config.ts +++ b/test/fixture/nitro.config.ts @@ -26,6 +26,7 @@ export default defineNitroConfig({ dir: "files", }, ], + ignore: ["api/**/_*", "middleware/_ignored.ts", "routes/_*.ts"], appConfig: { "nitro-config": true, dynamic: "initial", diff --git a/test/fixture/routes/_ignored.ts b/test/fixture/routes/_ignored.ts new file mode 100644 index 0000000000..c2c27dadee --- /dev/null +++ b/test/fixture/routes/_ignored.ts @@ -0,0 +1,3 @@ +export default eventHandler((event) => { + throw createError("This file should be ignored!"); +}); diff --git a/test/tests.ts b/test/tests.ts index 65630732fd..5ca247db28 100644 --- a/test/tests.ts +++ b/test/tests.ts @@ -394,4 +394,11 @@ export function testNitro( const res = await callHandler({ url: "/wait-until" }); expect(res.data).toBe("done"); }); + + describe("ignore", () => { + it("server routes should be ignored", async () => { + expect((await callHandler({ url: "/api/_ignored" })).status).toBe(404); + expect((await callHandler({ url: "/_ignored" })).status).toBe(404); + }); + }); }