@efebia/fastify-auto-import 1.0.3 → 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/README.md CHANGED
@@ -69,8 +69,41 @@ A `fastify-plugin` compatible plugin. Register it with `fastify.register()`.
69
69
  | `directory` | `string` | `'src/plugins'` | Directory to scan for route subdirectories, relative to `startingDirectory`. |
70
70
  | `routeFile` | `string` | `'routes'` | File name (without extension) to import from each subdirectory. |
71
71
  | `excludedDirectories` | `string[]` | `[]` | Subdirectory names to skip. |
72
+ | `recursive` | `boolean` | `false` | Scan nested subdirectories for route files, not just direct children. |
72
73
  | `log` | `pino.BaseLogger` | Fastify logger | Logger instance for import errors. |
73
74
 
75
+ ## Recursive mode
76
+
77
+ By default the plugin only looks at **direct subdirectories** of `directory`. Set `recursive: true` to walk the entire tree and register every subdirectory that contains a route file, at any depth.
78
+
79
+ ```ts
80
+ await fastify.register(autoImport, {
81
+ startingDirectory: import.meta.url,
82
+ directory: 'src/plugins',
83
+ recursive: true,
84
+ });
85
+ ```
86
+
87
+ Given this structure:
88
+
89
+ ```
90
+ src/plugins/
91
+ users/
92
+ routes.ts ← registered
93
+ profile/
94
+ routes.ts ← registered (nested)
95
+ orders/
96
+ routes.ts ← registered
97
+ items/
98
+ routes.ts ← registered (nested)
99
+ _shared/
100
+ utils.ts ← skipped (no routes file)
101
+ ```
102
+
103
+ Without `recursive`, only `users/routes.ts` and `orders/routes.ts` would be registered. With `recursive: true`, `users/profile/routes.ts` and `orders/items/routes.ts` are picked up as well.
104
+
105
+ `excludedDirectories` still applies at every depth — a matching directory name is skipped along with all its descendants.
106
+
74
107
  ## License
75
108
 
76
109
  MIT
@@ -5,7 +5,8 @@ export type Globber = {
5
5
  startingDirectory: string;
6
6
  log?: pino.BaseLogger;
7
7
  excludedDirectories?: string[];
8
+ recursive?: boolean;
8
9
  };
9
10
  export declare const globFiles: (opts: Required<Globber> & {
10
11
  log?: pino.BaseLogger;
11
- }) => Promise<never[]>;
12
+ }) => Promise<any[]>;
@@ -52,10 +52,57 @@ const getPackageType = async (directory) => {
52
52
  return JSON.parse(file).type;
53
53
  }
54
54
  };
55
+ const collectRecursivePlugins = async (dir, opts) => {
56
+ var _a;
57
+ const entries = await (0, promises_1.readdir)(dir, { withFileTypes: true });
58
+ const results = [];
59
+ for (const entry of entries) {
60
+ if (!entry.isDirectory())
61
+ continue;
62
+ if (opts.excludedDirectories.includes(entry.name))
63
+ continue;
64
+ const subDir = node_path_1.default.join(dir, entry.name);
65
+ const subEntries = await (0, promises_1.readdir)(subDir);
66
+ const routeFileName = opts.routeFile.replace('.js', '');
67
+ const hasRouteFile = subEntries.some((f) => f === `${routeFileName}.js` || f === `${routeFileName}.ts`);
68
+ if (hasRouteFile) {
69
+ const osPath = node_path_1.default.join(subDir, opts.packageType === 'module' ? `${routeFileName}.js` : routeFileName);
70
+ const href = node_url_1.default.pathToFileURL(osPath).href;
71
+ try {
72
+ if (opts.packageType === 'module') {
73
+ const imported = await Promise.resolve(`${href}`).then(s => __importStar(require(s)));
74
+ if (imported.default)
75
+ results.push(imported.default);
76
+ }
77
+ else {
78
+ const imported = require(osPath);
79
+ if (imported)
80
+ results.push(imported);
81
+ }
82
+ }
83
+ catch (e) {
84
+ (_a = opts.log) === null || _a === void 0 ? void 0 : _a.error({ error: e, path: osPath }, '@efebia/fastify-auto-import error on importing plugin');
85
+ throw e;
86
+ }
87
+ }
88
+ const nested = await collectRecursivePlugins(subDir, opts);
89
+ results.push(...nested);
90
+ }
91
+ return results;
92
+ };
55
93
  const globFiles = async (opts) => {
56
94
  const packageType = await getPackageType(opts.directory);
57
95
  const startingDirectory = packageType === 'module' ? node_path_1.default.dirname((0, node_url_1.fileURLToPath)(opts.startingDirectory)) : opts.startingDirectory;
58
96
  const pluginsDirectory = node_path_1.default.join(startingDirectory, opts.directory);
97
+ if (opts.recursive) {
98
+ const plugins = await collectRecursivePlugins(pluginsDirectory, {
99
+ routeFile: opts.routeFile,
100
+ packageType,
101
+ excludedDirectories: opts.excludedDirectories,
102
+ log: opts.log,
103
+ });
104
+ return plugins.filter(Boolean);
105
+ }
59
106
  const routesDirectories = await (0, promises_1.readdir)(pluginsDirectory);
60
107
  const filteredDirectories = routesDirectories.filter(dir => !opts.excludedDirectories.includes(dir));
61
108
  const importedFiles = await (0, serial_js_1.serial)(filteredDirectories.map((dir) => async () => {
package/lib/cjs/index.js CHANGED
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
7
7
  const globber_js_1 = require("./globber.js");
8
8
  const plugin = (0, fastify_plugin_1.default)(async (fastify, opts) => {
9
- var _a, _b, _c;
10
- const globbedFiles = await (0, globber_js_1.globFiles)(Object.assign(Object.assign({}, opts), { routeFile: (_a = opts.routeFile) !== null && _a !== void 0 ? _a : 'routes', directory: (_b = opts.directory) !== null && _b !== void 0 ? _b : 'src/plugins', log: opts.log || fastify.log, excludedDirectories: (_c = opts.excludedDirectories) !== null && _c !== void 0 ? _c : [] }));
9
+ var _a, _b, _c, _d;
10
+ const globbedFiles = await (0, globber_js_1.globFiles)(Object.assign(Object.assign({}, opts), { routeFile: (_a = opts.routeFile) !== null && _a !== void 0 ? _a : 'routes', directory: (_b = opts.directory) !== null && _b !== void 0 ? _b : 'src/plugins', log: opts.log || fastify.log, excludedDirectories: (_c = opts.excludedDirectories) !== null && _c !== void 0 ? _c : [], recursive: (_d = opts.recursive) !== null && _d !== void 0 ? _d : false }));
11
11
  await Promise.all(globbedFiles.map(async (globbedFile) => {
12
12
  await fastify.register(globbedFile);
13
13
  }));
@@ -5,7 +5,8 @@ export type Globber = {
5
5
  startingDirectory: string;
6
6
  log?: pino.BaseLogger;
7
7
  excludedDirectories?: string[];
8
+ recursive?: boolean;
8
9
  };
9
10
  export declare const globFiles: (opts: Required<Globber> & {
10
11
  log?: pino.BaseLogger;
11
- }) => Promise<never[]>;
12
+ }) => Promise<any[]>;
@@ -13,10 +13,57 @@ const getPackageType = async (directory) => {
13
13
  return JSON.parse(file).type;
14
14
  }
15
15
  };
16
+ const collectRecursivePlugins = async (dir, opts) => {
17
+ var _a;
18
+ const entries = await readdir(dir, { withFileTypes: true });
19
+ const results = [];
20
+ for (const entry of entries) {
21
+ if (!entry.isDirectory())
22
+ continue;
23
+ if (opts.excludedDirectories.includes(entry.name))
24
+ continue;
25
+ const subDir = path.join(dir, entry.name);
26
+ const subEntries = await readdir(subDir);
27
+ const routeFileName = opts.routeFile.replace('.js', '');
28
+ const hasRouteFile = subEntries.some((f) => f === `${routeFileName}.js` || f === `${routeFileName}.ts`);
29
+ if (hasRouteFile) {
30
+ const osPath = path.join(subDir, opts.packageType === 'module' ? `${routeFileName}.js` : routeFileName);
31
+ const href = url.pathToFileURL(osPath).href;
32
+ try {
33
+ if (opts.packageType === 'module') {
34
+ const imported = await import(href);
35
+ if (imported.default)
36
+ results.push(imported.default);
37
+ }
38
+ else {
39
+ const imported = require(osPath);
40
+ if (imported)
41
+ results.push(imported);
42
+ }
43
+ }
44
+ catch (e) {
45
+ (_a = opts.log) === null || _a === void 0 ? void 0 : _a.error({ error: e, path: osPath }, '@efebia/fastify-auto-import error on importing plugin');
46
+ throw e;
47
+ }
48
+ }
49
+ const nested = await collectRecursivePlugins(subDir, opts);
50
+ results.push(...nested);
51
+ }
52
+ return results;
53
+ };
16
54
  export const globFiles = async (opts) => {
17
55
  const packageType = await getPackageType(opts.directory);
18
56
  const startingDirectory = packageType === 'module' ? path.dirname(fileURLToPath(opts.startingDirectory)) : opts.startingDirectory;
19
57
  const pluginsDirectory = path.join(startingDirectory, opts.directory);
58
+ if (opts.recursive) {
59
+ const plugins = await collectRecursivePlugins(pluginsDirectory, {
60
+ routeFile: opts.routeFile,
61
+ packageType,
62
+ excludedDirectories: opts.excludedDirectories,
63
+ log: opts.log,
64
+ });
65
+ return plugins.filter(Boolean);
66
+ }
20
67
  const routesDirectories = await readdir(pluginsDirectory);
21
68
  const filteredDirectories = routesDirectories.filter(dir => !opts.excludedDirectories.includes(dir));
22
69
  const importedFiles = await serial(filteredDirectories.map((dir) => async () => {
package/lib/esm/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import fp from 'fastify-plugin';
2
2
  import { globFiles } from './globber.js';
3
3
  const plugin = fp(async (fastify, opts) => {
4
- var _a, _b, _c;
5
- const globbedFiles = await globFiles(Object.assign(Object.assign({}, opts), { routeFile: (_a = opts.routeFile) !== null && _a !== void 0 ? _a : 'routes', directory: (_b = opts.directory) !== null && _b !== void 0 ? _b : 'src/plugins', log: opts.log || fastify.log, excludedDirectories: (_c = opts.excludedDirectories) !== null && _c !== void 0 ? _c : [] }));
4
+ var _a, _b, _c, _d;
5
+ const globbedFiles = await globFiles(Object.assign(Object.assign({}, opts), { routeFile: (_a = opts.routeFile) !== null && _a !== void 0 ? _a : 'routes', directory: (_b = opts.directory) !== null && _b !== void 0 ? _b : 'src/plugins', log: opts.log || fastify.log, excludedDirectories: (_c = opts.excludedDirectories) !== null && _c !== void 0 ? _c : [], recursive: (_d = opts.recursive) !== null && _d !== void 0 ? _d : false }));
6
6
  await Promise.all(globbedFiles.map(async (globbedFile) => {
7
7
  await fastify.register(globbedFile);
8
8
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@efebia/fastify-auto-import",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "license": "MIT",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -12,20 +12,19 @@
12
12
  "pkg-up": "^3.1.0"
13
13
  },
14
14
  "devDependencies": {
15
- "@types/jest": "^29.2.5",
16
15
  "@types/node": "^18.11.18",
17
- "jest": "^29.3.1",
18
- "ts-jest": "^29.0.3",
16
+ "tsx": "^4.20.3",
19
17
  "typescript": "~5.8.3"
20
18
  },
21
19
  "files": [
22
20
  "/lib"
23
21
  ],
24
22
  "scripts": {
25
- "test": "yarn node $(yarn bin jest)",
26
- "build:esm": "tsc --module nodenext --moduleResolution nodenext --outDir lib/esm",
27
- "build:cjs": "tsc --module commonjs --moduleResolution node --outDir lib/cjs",
28
- "build": "yarn build:esm && yarn build:cjs"
23
+ "build:esm": "tsc -p ./tsconfig.build.json --module nodenext --moduleResolution nodenext --outDir lib/esm",
24
+ "build:cjs": "tsc -p ./tsconfig.build.json --module commonjs --moduleResolution node --outDir lib/cjs",
25
+ "build": "yarn build:esm && yarn build:cjs",
26
+ "test": "node --import tsx --test ./**/*.test.ts",
27
+ "test:only": "node --import tsx --test-only ./**/*.test.ts"
29
28
  },
30
29
  "exports": {
31
30
  ".": {