@saltcorn/plugins-loader 0.9.5-beta.4 → 0.9.5-beta.5
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/download_utils.js +2 -1
- package/package.json +1 -5
- package/plugin_installer.js +72 -79
package/download_utils.js
CHANGED
|
@@ -28,13 +28,14 @@ const downloadFromNpm = async (plugin, pluginDir, pckJson) => {
|
|
|
28
28
|
const vToInstall =
|
|
29
29
|
plugin.version && plugin.version !== "latest" ? plugin.version : latest;
|
|
30
30
|
|
|
31
|
-
if (pckJson && pckJson.version === vToInstall) return
|
|
31
|
+
if (pckJson && pckJson.version === vToInstall) return false;
|
|
32
32
|
else {
|
|
33
33
|
const tarballUrl = pkgInfo.versions[vToInstall].dist.tarball;
|
|
34
34
|
const fileName = plugin.name.split("/").pop();
|
|
35
35
|
const filePath = await loadTarball(tarballUrl, fileName);
|
|
36
36
|
await mkdir(pluginDir, { recursive: true });
|
|
37
37
|
await extractTarball(filePath, pluginDir);
|
|
38
|
+
return true;
|
|
38
39
|
}
|
|
39
40
|
};
|
|
40
41
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/plugins-loader",
|
|
3
|
-
"version": "0.9.5-beta.
|
|
3
|
+
"version": "0.9.5-beta.5",
|
|
4
4
|
"description": "Saltcorn plugin loader",
|
|
5
5
|
"homepage": "https://saltcorn.com",
|
|
6
6
|
"scripts": {
|
|
@@ -11,10 +11,6 @@
|
|
|
11
11
|
"author": "Christian Hugo",
|
|
12
12
|
"license": "MIT",
|
|
13
13
|
"main": "index.js",
|
|
14
|
-
"dependencies": {
|
|
15
|
-
"semver": "^7.6.0",
|
|
16
|
-
"resolve-global": "^1.0.0"
|
|
17
|
-
},
|
|
18
14
|
"repository": "github:saltcorn/saltcorn",
|
|
19
15
|
"publishConfig": {
|
|
20
16
|
"access": "public"
|
package/plugin_installer.js
CHANGED
|
@@ -8,12 +8,8 @@ const {
|
|
|
8
8
|
tarballExists,
|
|
9
9
|
removeTarball,
|
|
10
10
|
} = require("./download_utils");
|
|
11
|
-
const
|
|
12
|
-
const fs = require("fs");
|
|
13
|
-
const { rm } = require("fs").promises;
|
|
14
|
-
const resolveGlobal = require("resolve-global");
|
|
11
|
+
const { rm, rename, cp, readFile } = require("fs").promises;
|
|
15
12
|
|
|
16
|
-
const rootFolder = process.cwd();
|
|
17
13
|
const staticDeps = ["@saltcorn/markup", "@saltcorn/data", "jest"];
|
|
18
14
|
const fixedPlugins = ["@saltcorn/base-plugin", "@saltcorn/sbadmin2"];
|
|
19
15
|
|
|
@@ -22,74 +18,85 @@ const isGitCheckout = async () => {
|
|
|
22
18
|
return await pathExists(gitPath);
|
|
23
19
|
};
|
|
24
20
|
|
|
21
|
+
const readPackageJson = async (filePath) => {
|
|
22
|
+
if (await pathExists(filePath)) return JSON.parse(await readFile(filePath));
|
|
23
|
+
else return null;
|
|
24
|
+
};
|
|
25
|
+
|
|
25
26
|
class PluginInstaller {
|
|
26
|
-
constructor(plugin) {
|
|
27
|
+
constructor(plugin, opts = {}) {
|
|
27
28
|
this.plugin = plugin;
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
29
|
+
this.rootFolder = opts.rootFolder || process.cwd();
|
|
30
|
+
this.tempRootFolder = opts.tempRootFolder || process.cwd();
|
|
30
31
|
const tokens =
|
|
31
32
|
plugin.source === "npm"
|
|
32
33
|
? plugin.location.split("/")
|
|
33
34
|
: plugin.name.split("/");
|
|
35
|
+
this.name = tokens[tokens.length - 1];
|
|
34
36
|
this.pluginDir = join(
|
|
35
|
-
rootFolder,
|
|
37
|
+
this.rootFolder,
|
|
36
38
|
plugin.source === "git" ? "git_plugins" : "plugins_folder",
|
|
37
39
|
...tokens
|
|
38
40
|
);
|
|
39
41
|
this.pckJsonPath = join(this.pluginDir, "package.json");
|
|
40
|
-
this.
|
|
42
|
+
this.tempDir = join(this.tempRootFolder, "temp_install", ...tokens);
|
|
43
|
+
this.tempPckJsonPath = join(this.tempDir, "package.json");
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
async install(force) {
|
|
44
47
|
await this.ensurePluginsRootFolders();
|
|
45
48
|
if (fixedPlugins.includes(this.plugin.location))
|
|
46
49
|
return { plugin_module: require(this.plugin.location) };
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
|
|
51
|
+
let pckJSON = await readPackageJson(this.pckJsonPath);
|
|
52
|
+
if (await this.prepPluginsFolder(force, pckJSON)) {
|
|
53
|
+
const tmpPckJSON = await this.removeDependencies(
|
|
54
|
+
await readPackageJson(this.tempPckJsonPath)
|
|
55
|
+
);
|
|
56
|
+
await this.npmInstall(tmpPckJSON);
|
|
57
|
+
await this.movePlugin();
|
|
52
58
|
if (await tarballExists(this.plugin)) await removeTarball(this.plugin);
|
|
53
59
|
}
|
|
60
|
+
pckJSON = await readPackageJson(this.pckJsonPath);
|
|
54
61
|
return {
|
|
55
|
-
version:
|
|
56
|
-
plugin_module: await this.loadMainFile(),
|
|
62
|
+
version: pckJSON.version,
|
|
63
|
+
plugin_module: await this.loadMainFile(pckJSON),
|
|
57
64
|
location: this.pluginDir,
|
|
58
65
|
name: this.name,
|
|
59
66
|
};
|
|
60
67
|
}
|
|
61
68
|
|
|
62
|
-
async
|
|
69
|
+
async remove() {
|
|
70
|
+
if (await pathExists(this.pluginDir))
|
|
71
|
+
await rm(this.pluginDir, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async prepPluginsFolder(force, pckJSON) {
|
|
63
75
|
let wasLoaded = false;
|
|
64
76
|
switch (this.plugin.source) {
|
|
65
77
|
case "npm":
|
|
66
78
|
if (
|
|
67
|
-
(force && !(await this.versionIsInstalled())) ||
|
|
79
|
+
(force && !(await this.versionIsInstalled(pckJSON))) ||
|
|
68
80
|
!(await pathExists(this.pluginDir))
|
|
69
81
|
) {
|
|
70
|
-
|
|
71
|
-
this.plugin,
|
|
72
|
-
this.pluginDir,
|
|
73
|
-
this.pckJson
|
|
74
|
-
);
|
|
75
|
-
wasLoaded = true;
|
|
82
|
+
wasLoaded = await downloadFromNpm(this.plugin, this.tempDir, pckJSON);
|
|
76
83
|
}
|
|
77
84
|
break;
|
|
78
85
|
case "github":
|
|
79
86
|
if (force || !(await pathExists(this.pluginDir))) {
|
|
80
|
-
|
|
87
|
+
await downloadFromGithub(this.plugin, this.tempDir);
|
|
81
88
|
wasLoaded = true;
|
|
82
89
|
}
|
|
83
90
|
break;
|
|
84
91
|
case "local":
|
|
85
92
|
if (force || !(await pathExists(this.pluginDir))) {
|
|
86
|
-
await copy(this.plugin.location, this.
|
|
93
|
+
await copy(this.plugin.location, this.tempDir);
|
|
87
94
|
wasLoaded = true;
|
|
88
95
|
}
|
|
89
96
|
break;
|
|
90
97
|
case "git":
|
|
91
98
|
if (force || !(await pathExists(this.pluginDir))) {
|
|
92
|
-
await gitPullOrClone(this.plugin, this.
|
|
99
|
+
await gitPullOrClone(this.plugin, this.tempDir);
|
|
93
100
|
this.pckJsonPath = join(this.pluginDir, "package.json");
|
|
94
101
|
wasLoaded = true;
|
|
95
102
|
}
|
|
@@ -99,15 +106,16 @@ class PluginInstaller {
|
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
async ensurePluginsRootFolders() {
|
|
109
|
+
const isWindows = process.platform === "win32";
|
|
102
110
|
const ensureFn = async (folder) => {
|
|
103
|
-
const pluginsFolder = join(rootFolder, folder);
|
|
111
|
+
const pluginsFolder = join(this.rootFolder, folder);
|
|
104
112
|
if (!(await pathExists(pluginsFolder))) await mkdir(pluginsFolder);
|
|
105
113
|
const symLinkDst = join(pluginsFolder, "node_modules");
|
|
106
114
|
const symLinkSrc = (await isGitCheckout())
|
|
107
115
|
? join(__dirname, "..", "..", "node_modules")
|
|
108
|
-
: join(dirname(
|
|
116
|
+
: join(dirname(require.resolve("@saltcorn/cli")), "..", "node_modules");
|
|
109
117
|
if (!(await pathExists(symLinkDst)))
|
|
110
|
-
await symlink(symLinkSrc, symLinkDst, "dir");
|
|
118
|
+
await symlink(symLinkSrc, symLinkDst, !isWindows ? "dir" : "junction");
|
|
111
119
|
};
|
|
112
120
|
for (const folder of ["plugins_folder", "git_plugins"])
|
|
113
121
|
await ensureFn(folder);
|
|
@@ -117,77 +125,61 @@ class PluginInstaller {
|
|
|
117
125
|
return !!this.plugin.version && this.plugin.version !== "latest";
|
|
118
126
|
}
|
|
119
127
|
|
|
120
|
-
async versionIsInstalled() {
|
|
121
|
-
if (!
|
|
128
|
+
async versionIsInstalled(pckJSON) {
|
|
129
|
+
if (!pckJSON || !this.isFixedVersion()) return false;
|
|
122
130
|
else {
|
|
123
|
-
const vInstalled =
|
|
131
|
+
const vInstalled = pckJSON.version;
|
|
124
132
|
if (vInstalled === this.plugin.version) return true;
|
|
125
133
|
else return false;
|
|
126
134
|
}
|
|
127
135
|
}
|
|
128
136
|
|
|
129
|
-
async
|
|
130
|
-
if (await pathExists(this.pluginDir))
|
|
131
|
-
await rm(this.pluginDir, { recursive: true });
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async loadMainFile() {
|
|
137
|
+
async loadMainFile(pckJSON) {
|
|
135
138
|
const isWindows = process.platform === "win32";
|
|
136
139
|
if (process.env.NODE_ENV === "test") {
|
|
137
140
|
// in jest, downgrad to require
|
|
138
|
-
return require(normalize(join(this.pluginDir,
|
|
141
|
+
return require(normalize(join(this.pluginDir, pckJSON.main)));
|
|
139
142
|
} else {
|
|
140
143
|
const res = await import(
|
|
141
144
|
`${isWindows ? `file://` : ""}${normalize(
|
|
142
|
-
join(this.pluginDir,
|
|
145
|
+
join(this.pluginDir, pckJSON.main)
|
|
143
146
|
)}`
|
|
144
147
|
);
|
|
145
148
|
return res.default;
|
|
146
149
|
}
|
|
147
150
|
}
|
|
148
151
|
|
|
149
|
-
async removeDependencies() {
|
|
150
|
-
const
|
|
151
|
-
const oldDepsLength = Object.keys(
|
|
152
|
-
const oldDevDepsLength = Object.keys(
|
|
153
|
-
|
|
154
|
-
const satisfiedRemover = (deps) => {
|
|
155
|
-
for (const [name, version] of Object.entries(deps)) {
|
|
156
|
-
try {
|
|
157
|
-
const vInstalled = require(`${name}/package.json`).version;
|
|
158
|
-
if (semver.satisfies(vInstalled, version)) {
|
|
159
|
-
delete deps[name];
|
|
160
|
-
}
|
|
161
|
-
} catch (e) {} // continue, npm installs it
|
|
162
|
-
}
|
|
163
|
-
};
|
|
152
|
+
async removeDependencies(tmpPckJSON) {
|
|
153
|
+
const pckJSON = { ...tmpPckJSON };
|
|
154
|
+
const oldDepsLength = Object.keys(pckJSON.dependencies || {}).length;
|
|
155
|
+
const oldDevDepsLength = Object.keys(pckJSON.devDependencies || {}).length;
|
|
164
156
|
const staticsRemover = (deps) => {
|
|
165
157
|
for (const staticDep of staticDeps) {
|
|
166
158
|
if (deps[staticDep]) delete deps[staticDep];
|
|
167
159
|
}
|
|
168
160
|
};
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
staticsRemover(pckJson.dependencies);
|
|
172
|
-
}
|
|
173
|
-
if (pckJson.devDependencies) {
|
|
174
|
-
satisfiedRemover(pckJson.devDependencies);
|
|
175
|
-
staticsRemover(pckJson.devDependencies);
|
|
176
|
-
}
|
|
161
|
+
if (pckJSON.dependencies) staticsRemover(pckJSON.dependencies);
|
|
162
|
+
if (pckJSON.devDependencies) staticsRemover(pckJSON.devDependencies);
|
|
177
163
|
if (
|
|
178
|
-
Object.keys(
|
|
179
|
-
Object.keys(
|
|
164
|
+
Object.keys(pckJSON.dependencies || {}).length !== oldDepsLength ||
|
|
165
|
+
Object.keys(pckJSON.devDependencies || {}).length !== oldDevDepsLength
|
|
180
166
|
)
|
|
181
|
-
await writeFile(
|
|
167
|
+
await writeFile(
|
|
168
|
+
join(this.tempDir, "package.json"),
|
|
169
|
+
JSON.stringify(pckJSON, null, 2)
|
|
170
|
+
);
|
|
171
|
+
return pckJSON;
|
|
182
172
|
}
|
|
183
173
|
|
|
184
|
-
async npmInstall() {
|
|
174
|
+
async npmInstall(pckJSON) {
|
|
175
|
+
const isWindows = process.platform === "win32";
|
|
185
176
|
if (
|
|
186
|
-
Object.keys(
|
|
187
|
-
Object.keys(
|
|
177
|
+
Object.keys(pckJSON.dependencies || {}).length > 0 ||
|
|
178
|
+
Object.keys(pckJSON.devDependencies || {}).length > 0
|
|
188
179
|
) {
|
|
189
180
|
const child = spawn("npm", ["install"], {
|
|
190
|
-
cwd: this.
|
|
181
|
+
cwd: this.tempDir,
|
|
182
|
+
...(isWindows ? { shell: true } : {}),
|
|
191
183
|
});
|
|
192
184
|
return new Promise((resolve, reject) => {
|
|
193
185
|
child.on("exit", (exitCode, signal) => {
|
|
@@ -201,13 +193,14 @@ class PluginInstaller {
|
|
|
201
193
|
}
|
|
202
194
|
}
|
|
203
195
|
|
|
204
|
-
async
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
196
|
+
async movePlugin() {
|
|
197
|
+
const isWindows = process.platform === "win32";
|
|
198
|
+
if (await pathExists(this.pluginDir))
|
|
199
|
+
await rm(this.pluginDir, { recursive: true });
|
|
200
|
+
await mkdir(this.pluginDir, { recursive: true });
|
|
201
|
+
if (!isWindows) await rename(this.tempDir, this.pluginDir);
|
|
202
|
+
else
|
|
203
|
+
await cp(this.tempDir, this.pluginDir, { recursive: true, force: true });
|
|
211
204
|
}
|
|
212
205
|
}
|
|
213
206
|
|