This repository has been archived by the owner on Sep 13, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
plugin-loader.js
129 lines (116 loc) · 5.1 KB
/
plugin-loader.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
const fs = require('fs');
const path = require('path');
const models = require('@datawrapper/orm/models');
const get = require('lodash/get');
const { promisify } = require('util');
const { addScope } = require('@datawrapper/service-utils/l10n');
const symlink = promisify(fs.symlink);
const unlink = promisify(fs.unlink);
const readFile = promisify(fs.readFile);
const readDir = promisify(fs.readdir);
module.exports = {
name: 'plugin-loader',
version: '1.0.0',
register: async (server, options) => {
const config = server.methods.config();
const pluginRoot = config.general.localPluginRoot || path.join(process.cwd(), 'plugins');
const plugins = Object.keys(config.plugins || [])
.reduce(getPluginPath, [])
.map(registerPlugin);
function getPluginPath(plugins, name) {
// If available, use .cjs file (ES Module plugin):
const cjsConfig = path.join(pluginRoot, name, 'frontend.cjs');
if (fs.existsSync(cjsConfig)) {
plugins.push({ name, pluginPath: cjsConfig });
return plugins;
}
// Else, use .js file (legacy plugin):
const jsConfig = path.join(pluginRoot, name, 'frontend.js');
if (fs.existsSync(jsConfig)) {
plugins.push({ name, pluginPath: jsConfig });
return plugins;
}
// No plugin file — don't add anything:
return plugins;
}
function registerPlugin({ name, pluginPath }) {
try {
const { options = {}, ...plugin } = require(pluginPath);
const { routes, ...opts } = options;
return [
{
name,
plugin,
options: {
models,
config: get(config, ['plugins', name], {}),
tarball: `https://api.github.com/repos/datawrapper/plugin-${name}/tarball`,
...opts
}
},
{ routes }
];
} catch (error) {
return [{ name, error }];
}
}
if (plugins.length) {
for (const [{ plugin, options, error, name }, pluginOptions] of plugins) {
if (error) {
server.logger.warn(`[Plugin] ${name}\n\n${logError(pluginRoot, name, error)}`);
} else {
const version = get(plugin, ['pkg', 'version'], plugin.version);
server.logger.info(`[Plugin] ${name}@${version}`);
// symlink plugin views
const pluginViews = path.join(pluginRoot, name, 'src/frontend/views');
if (fs.existsSync(pluginViews)) {
const target = path.join(__dirname, `../views/plugins/${name}`);
if (fs.existsSync(target)) {
await unlink(target);
}
await symlink(pluginViews, target);
server.logger.info(
`[Plugin] ${name}: created symlink from ${pluginViews} to ${target}`
);
}
// @todo: try to load locales
try {
const localePath = path.join(pluginRoot, name, 'locale');
const locales = await readDir(localePath);
options.locales = {};
for (let i = 0; i < locales.length; i++) {
const file = locales[i];
if (file === 'chart-translations.json') {
// ignore chart translations
} else if (/[a-z]+_[a-z]+\.json/i.test(file)) {
options.locales[file.split('.')[0]] = JSON.parse(
await readFile(path.join(localePath, file))
);
}
}
addScope(name, options.locales);
} catch (e) {}
await server.register({ plugin, options }, pluginOptions);
}
}
}
// emit PLUGINS_LOADED event so plugins who depend on other
// plugins can safely initialize
await server.app.events.emit(server.app.event.PLUGINS_LOADED, { plugins });
}
};
function logError(pluginRoot, name, error) {
if (error.code === 'MODULE_NOT_FOUND') {
return `- skipped plugin ${name}
Reason: \`frontend.[cjs,js]\` doesn't exist or a dependency is not installed.\n Error: ${error.message}\n`;
}
return `
Loading plugin [${name}] failed! Maybe it is not properly installed.
Is it available in "plugins/"?
Tip: run "ls ${pluginRoot} | grep "${name}"
Possible mistakes:
* Plugin config key doesn't match the plugin folder.
* Plugin is missing from ${pluginRoot}.
Maybe this error is helpful:
${error.stack}`;
}