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