Skip to content

Commit

Permalink
perf(app-autoload): plugin-autoload support symlink
Browse files Browse the repository at this point in the history
  • Loading branch information
cxtom committed Aug 12, 2021
1 parent 953df08 commit 544b22d
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = async function (fastify, opts) {
fastify.decorate('realPlugin', opts);
};

module.exports.autoConfig = {
realPlugin: 1,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = async function (fastify, opts) {
fastify.decorate('foo', opts);
};

module.exports.autoConfig = {
foo: 1,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
exports.default = async function (fastify, opts) {
fastify.decorate('test', opts);
};

exports.autoConfig = {
test: 1,
};
66 changes: 66 additions & 0 deletions packages/app-autoload/__tests__/pluginLoader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import fastify, {FastifyInstance} from 'fastify';
import path from 'path';
import {promises} from 'fs';
import pluginLoader from '../src/pluginLoader';

const {symlink, unlink} = promises;

declare module 'fastify' {
interface FastifyInstance {
foo?: Record<string, unknown>;
test?: Record<string, unknown>;
realPlugin?: Record<string, unknown>;
}
};


describe('@hoth/app-autoload pluginLoader', () => {

let fastifyInstance: FastifyInstance;

beforeEach(() => {
fastifyInstance = fastify({
logger: false,
});
});

it('simple dir & file', async () => {
await fastifyInstance.register(pluginLoader, {
dir: path.join(__dirname, 'fixtures/plugins/simple'),
options: {
init: 1,
}
});

expect(fastifyInstance.foo).toEqual({
foo: 1,
init: 1,
});

expect(fastifyInstance.test).toEqual({
test: 1,
init: 1,
});
});

it('symlink file', async () => {
await symlink(
path.join(__dirname, 'fixtures/plugins/realPlugin.js'),
path.join(__dirname, 'fixtures/plugins/empty/realPlugin.js')
);

await fastifyInstance.register(pluginLoader, {
dir: path.join(__dirname, 'fixtures/plugins/empty'),
options: {
init: 1,
}
});

expect(fastifyInstance.realPlugin).toEqual({
realPlugin: 1,
init: 1,
});
await unlink(path.join(__dirname, 'fixtures/plugins/empty/realPlugin.js'));
});

});
1 change: 0 additions & 1 deletion packages/app-autoload/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"@hoth/molecule": "^1.1.2",
"@hoth/utils": "^1.1.4",
"config": "^3.3.3",
"fastify-autoload": "^3.4.2",
"fastify-plugin": "^3.0.0",
"fastify-warmup": "0.1.1",
"resolve-from": "^5.0.0",
Expand Down
11 changes: 3 additions & 8 deletions packages/app-autoload/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ process.env.ALLOW_CONFIG_MUTATIONS = 'false';

import Config from 'config';
import {resolve, join, isAbsolute} from 'path';
import autoload from 'fastify-autoload';
import {existsSync, readdirSync} from 'fs';
import {FastifyInstance, FastifyPluginAsync} from 'fastify';
import fp from 'fastify-plugin';
Expand All @@ -23,6 +22,7 @@ import preValidation from './hook/preValidation';
import {preHandler as loggerMiddleware} from '@hoth/logger';
import {molecule} from '@hoth/molecule';
import {loadConfig} from './configLoader';
import pluginLoader from './pluginLoader';
import {loadMoleculeApp} from './loadMoleculeApp';
import type {WarmupConf} from 'fastify-warmup';
interface AppAutoload {
Expand Down Expand Up @@ -113,14 +113,9 @@ async function load(appConfig: AppConfig, childInstance: FastifyInstance) {
await childInstance.register(appEntryModule, {...appConfig});

if (existsSync(pluginAppConfig.pluginPath)) {
await childInstance.register(autoload, {
await childInstance.register(pluginLoader, {
dir: pluginAppConfig.pluginPath,
dirNameRoutePrefix: false,
ignorePattern: /.*(test|spec).js/,
maxDepth: 2,
options: {
...appConfig,
},
options: {...appConfig},
});
}

Expand Down
105 changes: 105 additions & 0 deletions packages/app-autoload/src/pluginLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import fp from 'fastify-plugin';
import {promises} from 'fs';
import path from 'path';
import type {FastifyInstance} from 'fastify';
import {pathToFileURL} from 'url';

const {readdir, realpath, stat} = promises;

interface PluginLoaderOptions {
dir: string;
options?: Record<string, unknown>;
dirNameRoutePrefix?: boolean;
}

interface PluginItem {
file: string;
}

type PluginTree = Record<string, PluginItem[]>;

const ingnoreRegexp = /\.(map|d\.ts)$/;

async function findPlugins(dir: string, pluginTree: PluginTree = {}, prefix: string = '/', depth: number = 0) {
const list = await readdir(dir, {
withFileTypes: true
});

pluginTree[prefix] = pluginTree[prefix] || [];

for await (const dirent of list) {

if (ingnoreRegexp.test(dirent.name)) {
continue;
}

const atMaxDepth = 2 <= depth;
const file = path.join(dir, dirent.name);

if (dirent.isDirectory() && !atMaxDepth) {
await findPlugins(file, pluginTree, `${prefix}${prefix.endsWith('/') ? '' : '/'}${dirent.name}`, depth + 1);
}
else if (dirent.isFile()) {
pluginTree[prefix].push({
file,
});
}
else if (dirent.isSymbolicLink()) {
const finalFile = await realpath(file);
const fileStat = await stat(finalFile);

if (fileStat.isFile()) {
pluginTree[prefix].push({
file: finalFile,
});
}
}
}

return pluginTree;
}

function loadModule(file: string) {
if (typeof require !== 'undefined') {
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports */
return require(file);
}
return import(pathToFileURL(file).toString());
}

export default fp(async function pluginAutoLoad(fastify: FastifyInstance, opts: PluginLoaderOptions) {

const pluginTree = await findPlugins(opts.dir);

for await (const [prefix, pluginArray] of Object.entries(pluginTree)) {
if (pluginArray.length <= 0) {
continue;
}

for await (const {file} of pluginArray) {
const content = await loadModule(file);
const plugin = content.default || content;

if (plugin.autoload === false || content.autoload === false) {
continue;
}

const pluginConfig = (content.default && content.default.autoConfig) || content.autoConfig || {};
const pluginOptions = {
...pluginConfig,
...opts.options
};

if (opts.dirNameRoutePrefix) {
pluginOptions.prefix = prefix;
}

plugin[Symbol.for('skip-override')] = true;

await fastify.register(plugin, pluginOptions);
}
}

}, {
name: '@hoth/app-plugin-autoload'
});
15 changes: 0 additions & 15 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4530,14 +4530,6 @@ fast-safe-stringify@^2.0.7:
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==

fastify-autoload@^3.4.2:
version "3.7.1"
resolved "https://registry.yarnpkg.com/fastify-autoload/-/fastify-autoload-3.7.1.tgz#64dd843c5fe340d4c42d3d9353521e70c4c28b4f"
integrity sha512-Bgq+62s9aZu3VEKV/fZA+BTxqvEZ/2LrdvXhf6BztEtBuC5AWWrgaTBl63OJ2WdqVr0BZvLldGOouAj3U7OrDA==
dependencies:
pkg-up "^3.1.0"
semver "^7.3.2"

fastify-decorators@~3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/fastify-decorators/-/fastify-decorators-3.8.0.tgz#212b52ef1795a54ae65d336d35cea152210fe719"
Expand Down Expand Up @@ -8669,13 +8661,6 @@ pkg-up@^2.0.0:
dependencies:
find-up "^2.1.0"

pkg-up@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
dependencies:
find-up "^3.0.0"

please-upgrade-node@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
Expand Down

0 comments on commit 544b22d

Please sign in to comment.