@saltcorn/plugins-loader 1.6.0-alpha.8 → 1.6.0-beta.1
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/package.json +2 -2
- package/plugin_installer.js +58 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/plugins-loader",
|
|
3
|
-
"version": "1.6.0-
|
|
3
|
+
"version": "1.6.0-beta.1",
|
|
4
4
|
"description": "Saltcorn plugin loader",
|
|
5
5
|
"homepage": "https://saltcorn.com",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"clean": "echo 'No TypeScript build'"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@saltcorn/data": "1.6.0-
|
|
12
|
+
"@saltcorn/data": "1.6.0-beta.1",
|
|
13
13
|
"env-paths": "^2.2.1",
|
|
14
14
|
"npm-registry-fetch": "17.1.0",
|
|
15
15
|
"https-proxy-agent": "^7.0.6"
|
package/plugin_installer.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { join, normalize, dirname } = require("path");
|
|
2
2
|
const { writeFile, mkdir, pathExists, copy, symlink } = require("fs-extra");
|
|
3
|
+
const { readdirSync } = require("fs");
|
|
3
4
|
const { spawn } = require("child_process");
|
|
4
5
|
const {
|
|
5
6
|
downloadFromNpm,
|
|
@@ -55,6 +56,30 @@ const npmInstallNeeded = (oldPckJSON, newPckJSON) => {
|
|
|
55
56
|
|
|
56
57
|
const defaultRootFolder = envPaths("saltcorn", { suffix: "plugins" }).data;
|
|
57
58
|
|
|
59
|
+
// tracks local plugins already copied in this process
|
|
60
|
+
// is only checked in the master process
|
|
61
|
+
const installedLocalPlugins = new Set();
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Find the most recently created localversion_<timestamp> directory for a local plugin.
|
|
65
|
+
* Falls back to "localversion" if none exist yet.
|
|
66
|
+
*/
|
|
67
|
+
const findLatestLocalversionDir = (parentDir) => {
|
|
68
|
+
try {
|
|
69
|
+
const dirs = readdirSync(parentDir).filter((e) =>
|
|
70
|
+
/^localversion_\d+$/.test(e)
|
|
71
|
+
);
|
|
72
|
+
if (dirs.length === 0) return "localversion";
|
|
73
|
+
return dirs.reduce((latest, d) => {
|
|
74
|
+
return parseInt(d.split("_")[1]) > parseInt(latest.split("_")[1])
|
|
75
|
+
? d
|
|
76
|
+
: latest;
|
|
77
|
+
});
|
|
78
|
+
} catch {
|
|
79
|
+
return "localversion";
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
58
83
|
/**
|
|
59
84
|
* PluginInstaller class
|
|
60
85
|
*/
|
|
@@ -62,6 +87,8 @@ class PluginInstaller {
|
|
|
62
87
|
constructor(plugin, opts = Object.create(null)) {
|
|
63
88
|
this.plugin = plugin;
|
|
64
89
|
this.rootFolder = opts.rootFolder || defaultRootFolder;
|
|
90
|
+
this.reloadModule = !!opts.reloadModule;
|
|
91
|
+
this.force = !!opts.force;
|
|
65
92
|
this.tempRootFolder =
|
|
66
93
|
opts.tempRootFolder || envPaths("saltcorn", { suffix: "tmp" }).temp;
|
|
67
94
|
const tokens =
|
|
@@ -69,11 +96,24 @@ class PluginInstaller {
|
|
|
69
96
|
? plugin.location.split("/")
|
|
70
97
|
: plugin.name.split("/");
|
|
71
98
|
this.name = tokens[tokens.length - 1];
|
|
99
|
+
const localPluginParentDir = join(
|
|
100
|
+
opts.rootFolder || defaultRootFolder,
|
|
101
|
+
"plugins_folder",
|
|
102
|
+
...tokens
|
|
103
|
+
);
|
|
104
|
+
const localversionDir =
|
|
105
|
+
opts.reloadModule && plugin.source === "local"
|
|
106
|
+
? opts.force
|
|
107
|
+
? `localversion_${Date.now()}`
|
|
108
|
+
: findLatestLocalversionDir(localPluginParentDir)
|
|
109
|
+
: "localversion";
|
|
72
110
|
this.pluginDir = join(
|
|
73
111
|
this.rootFolder,
|
|
74
112
|
plugin.source === "git" ? "git_plugins" : "plugins_folder",
|
|
75
113
|
...tokens,
|
|
76
|
-
plugin.
|
|
114
|
+
plugin.source === "local"
|
|
115
|
+
? localversionDir
|
|
116
|
+
: plugin.version || "unknownversion"
|
|
77
117
|
);
|
|
78
118
|
this.pckJsonPath = join(this.pluginDir, "package.json");
|
|
79
119
|
this.tempDir = join(this.tempRootFolder, "temp_install", ...tokens);
|
|
@@ -89,11 +129,10 @@ class PluginInstaller {
|
|
|
89
129
|
|
|
90
130
|
/**
|
|
91
131
|
*
|
|
92
|
-
* @param {boolean} force
|
|
93
132
|
* @param {boolean} preInstall Only npm install without loading the module
|
|
94
133
|
* @returns
|
|
95
134
|
*/
|
|
96
|
-
async install(
|
|
135
|
+
async install(preInstall = false) {
|
|
97
136
|
getState().log(5, `loading plugin ${this.plugin.name}`);
|
|
98
137
|
await this._ensurePluginsRootFolders();
|
|
99
138
|
if (Plugin.is_fixed_plugin(this.plugin.location))
|
|
@@ -104,13 +143,13 @@ class PluginInstaller {
|
|
|
104
143
|
const msgs = [];
|
|
105
144
|
let module = null;
|
|
106
145
|
let loadedWithReload = false;
|
|
107
|
-
let pckJSON = await this._installHelper(
|
|
146
|
+
let pckJSON = await this._installHelper(msgs);
|
|
108
147
|
if (!preInstall) {
|
|
109
148
|
try {
|
|
110
149
|
module = await this._loadMainFile(pckJSON);
|
|
111
150
|
} catch (e) {
|
|
112
151
|
if (e.code === "MODULE_NOT_FOUND") await this._dumpNodeMoules();
|
|
113
|
-
if (force) {
|
|
152
|
+
if (this.force) {
|
|
114
153
|
// remove and try again
|
|
115
154
|
// could happen when there is a directory with a package.json
|
|
116
155
|
// but without a valid node modules folder
|
|
@@ -119,7 +158,7 @@ class PluginInstaller {
|
|
|
119
158
|
`Error loading plugin ${this.plugin.name}. Removing and trying again.`
|
|
120
159
|
);
|
|
121
160
|
await this.remove();
|
|
122
|
-
pckJSON = await this._installHelper(
|
|
161
|
+
pckJSON = await this._installHelper(msgs);
|
|
123
162
|
} else {
|
|
124
163
|
getState().log(
|
|
125
164
|
2,
|
|
@@ -154,15 +193,13 @@ class PluginInstaller {
|
|
|
154
193
|
|
|
155
194
|
/**
|
|
156
195
|
* prepare the plugin folder and npm install if needed
|
|
157
|
-
* @param {*} force
|
|
158
|
-
* @param {*} pckJSON
|
|
159
196
|
* @param {*} msgs
|
|
160
197
|
*/
|
|
161
|
-
async _installHelper(
|
|
198
|
+
async _installHelper(msgs) {
|
|
162
199
|
const airgap = getState().getConfig("airgap", false);
|
|
163
200
|
let pckJSON = await readPackageJson(this.pckJsonPath);
|
|
164
201
|
if (airgap) return pckJSON;
|
|
165
|
-
else if (await this._prepPluginsFolder(
|
|
202
|
+
else if (await this._prepPluginsFolder(pckJSON)) {
|
|
166
203
|
const tmpPckJSON = await this._removeDependencies(
|
|
167
204
|
await readPackageJson(this.tempPckJsonPath),
|
|
168
205
|
true
|
|
@@ -206,17 +243,16 @@ class PluginInstaller {
|
|
|
206
243
|
|
|
207
244
|
/**
|
|
208
245
|
* helper to prepare the plugin folder
|
|
209
|
-
* @param {*} force
|
|
210
246
|
* @param {*} pckJSON
|
|
211
247
|
* @returns
|
|
212
248
|
*/
|
|
213
|
-
async _prepPluginsFolder(
|
|
249
|
+
async _prepPluginsFolder(pckJSON) {
|
|
214
250
|
let wasLoaded = false;
|
|
215
251
|
const folderExists = await pathExists(this.pluginDir);
|
|
216
252
|
switch (this.plugin.source) {
|
|
217
253
|
case "npm":
|
|
218
254
|
if (
|
|
219
|
-
(force && !(await this._versionIsInstalled(pckJSON))) ||
|
|
255
|
+
(this.force && !(await this._versionIsInstalled(pckJSON))) ||
|
|
220
256
|
!folderExists
|
|
221
257
|
) {
|
|
222
258
|
getState().log(6, "downloading from npm");
|
|
@@ -229,24 +265,28 @@ class PluginInstaller {
|
|
|
229
265
|
}
|
|
230
266
|
break;
|
|
231
267
|
case "github":
|
|
232
|
-
if (force || !folderExists) {
|
|
268
|
+
if (this.force || !folderExists) {
|
|
233
269
|
getState().log(6, "downloading from github");
|
|
234
270
|
await downloadFromGithub(this.plugin, this.rootFolder, this.tempDir);
|
|
235
271
|
wasLoaded = true;
|
|
236
272
|
}
|
|
237
273
|
break;
|
|
238
274
|
case "local":
|
|
239
|
-
if (
|
|
275
|
+
if (
|
|
276
|
+
(this.force || !folderExists) &&
|
|
277
|
+
(!installedLocalPlugins.has(this.pluginDir) || this.reloadModule)
|
|
278
|
+
) {
|
|
240
279
|
getState().log(6, "copying from local");
|
|
241
280
|
await copy(this.plugin.location, this.tempDir);
|
|
242
281
|
// if tempdir has a node_modules folder, remove it
|
|
243
282
|
if (await pathExists(join(this.tempDir, "node_modules")))
|
|
244
283
|
await rm(join(this.tempDir, "node_modules"), { recursive: true });
|
|
284
|
+
installedLocalPlugins.add(this.pluginDir);
|
|
245
285
|
wasLoaded = true;
|
|
246
286
|
}
|
|
247
287
|
break;
|
|
248
288
|
case "git":
|
|
249
|
-
if (force || !folderExists) {
|
|
289
|
+
if (this.force || !folderExists) {
|
|
250
290
|
getState().log(6, "downloading from git");
|
|
251
291
|
await gitPullOrClone(this.plugin, this.tempDir);
|
|
252
292
|
this.pckJsonPath = join(this.pluginDir, "package.json");
|
|
@@ -294,8 +334,8 @@ class PluginInstaller {
|
|
|
294
334
|
return require(normalize(join(this.pluginDir, pckJSON.main)));
|
|
295
335
|
} else {
|
|
296
336
|
const url = `${isWindows ? `file://` : ""}${normalize(
|
|
297
|
-
join(this.pluginDir, pckJSON.main
|
|
298
|
-
)}`;
|
|
337
|
+
join(this.pluginDir, pckJSON.main)
|
|
338
|
+
)}${reload ? `?reload=${Date.now()}` : ""}`;
|
|
299
339
|
const res = await import(url);
|
|
300
340
|
return res.default;
|
|
301
341
|
}
|