@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 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 null;
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.4",
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"
@@ -8,12 +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 { 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.pckJson = null;
29
- this.tarFile = null;
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.name = tokens[tokens.length - 1];
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
- this.pckJson = await this.readPackageJson();
48
- if (await this.prepPluginsFolder(force)) {
49
- await this.removeDependencies();
50
- this.pckJson = await this.readPackageJson();
51
- 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();
52
58
  if (await tarballExists(this.plugin)) await removeTarball(this.plugin);
53
59
  }
60
+ pckJSON = await readPackageJson(this.pckJsonPath);
54
61
  return {
55
- version: this.pckJson.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 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) {
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
- this.tarFile = await downloadFromNpm(
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
- this.tarFile = await downloadFromGithub(this.plugin, this.pluginDir);
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.pluginDir);
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.pluginDir);
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(resolveGlobal("@saltcorn/cli")), "..", "node_modules");
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 (!this.pckJson || !this.isFixedVersion()) return false;
128
+ async versionIsInstalled(pckJSON) {
129
+ if (!pckJSON || !this.isFixedVersion()) return false;
122
130
  else {
123
- const vInstalled = this.pckJson.version;
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 remove() {
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, this.pckJson.main)));
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, this.pckJson.main)
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 pckJson = await this.readPackageJson();
151
- const oldDepsLength = Object.keys(pckJson.dependencies || {}).length;
152
- const oldDevDepsLength = Object.keys(pckJson.devDependencies || {}).length;
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 (pckJson.dependencies) {
170
- satisfiedRemover(pckJson.dependencies);
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(pckJson.dependencies || {}).length !== oldDepsLength ||
179
- Object.keys(pckJson.devDependencies || {}).length !== oldDevDepsLength
164
+ Object.keys(pckJSON.dependencies || {}).length !== oldDepsLength ||
165
+ Object.keys(pckJSON.devDependencies || {}).length !== oldDevDepsLength
180
166
  )
181
- 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;
182
172
  }
183
173
 
184
- async npmInstall() {
174
+ async npmInstall(pckJSON) {
175
+ const isWindows = process.platform === "win32";
185
176
  if (
186
- Object.keys(this.pckJson.dependencies || {}).length > 0 ||
187
- Object.keys(this.pckJson.devDependencies || {}).length > 0
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.pluginDir,
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 readPackageJson() {
205
- if (await pathExists(this.pckJsonPath)) {
206
- const str = await fs.promises.readFile(this.pckJsonPath);
207
- return JSON.parse(str);
208
- } else {
209
- return null;
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