Skip to content

Commit 9cb0012

Browse files
authored
Added install state for better update of plugins (#1058)
1 parent 4883085 commit 9cb0012

File tree

2 files changed

+141
-1
lines changed

2 files changed

+141
-1
lines changed

src/lib/installPlugin.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import fsOperation from "fileSystem";
33
import JSZip from "jszip";
44
import Url from "utils/Url";
55
import constants from "./constants";
6+
import InstallState from "./installState";
67
import loadPlugin from "./loadPlugin";
78

89
/**
@@ -70,6 +71,8 @@ export default async function installPlugin(id, name, purchaseToken) {
7071
pluginDir = Url.join(PLUGIN_DIR, id);
7172
}
7273

74+
const state = await InstallState.new(id);
75+
7376
if (!(await fsOperation(pluginDir).exists())) {
7477
await fsOperation(PLUGIN_DIR).createDirectory(id);
7578
}
@@ -81,7 +84,7 @@ export default async function installPlugin(id, name, purchaseToken) {
8184
}
8285

8386
const fileUrl = Url.join(pluginDir, correctFile);
84-
if (!(await fsOperation(fileUrl).exists())) {
87+
if (!state.exists(correctFile)) {
8588
await createFileRecursive(pluginDir, correctFile);
8689
}
8790

@@ -93,11 +96,18 @@ export default async function installPlugin(id, name, purchaseToken) {
9396
data = JSON.stringify(pluginJson);
9497
}
9598

99+
if (!(await state.isUpdated(correctFile, data))) {
100+
return;
101+
}
102+
96103
await fsOperation(fileUrl).writeFile(data);
104+
return;
97105
});
98106

99107
await Promise.all(promises);
100108
await loadPlugin(id, true);
109+
await state.save();
110+
deleteRedundantFiles(pluginDir, state);
101111
}
102112
} catch (err) {
103113
try {
@@ -139,3 +149,34 @@ async function createFileRecursive(parent, dir) {
139149
await createFileRecursive(newParent, dir);
140150
}
141151
}
152+
/**
153+
*
154+
* @param {string} dir
155+
* @param {Array<string>} files
156+
*/
157+
async function listFileRecursive(dir, files) {
158+
for (const child of await fsOperation(dir).lsDir()) {
159+
const fileUrl = Url.join(dir, child.name);
160+
if (child.isDirectory) {
161+
await listFileRecursive(fileUrl, files);
162+
} else {
163+
files.push(fileUrl);
164+
}
165+
}
166+
}
167+
168+
/**
169+
*
170+
* @param {Record<string, boolean>} files
171+
*/
172+
async function deleteRedundantFiles(pluginDir, state) {
173+
/** @type string[] */
174+
let files = [];
175+
await listFileRecursive(pluginDir, files);
176+
177+
for (const file of files) {
178+
if (!state.exists(file.replace(`${pluginDir}/`, ""))) {
179+
fsOperation(file).delete();
180+
}
181+
}
182+
}

src/lib/installState.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import fsOperation from "fileSystem";
2+
import Url from "utils/Url";
3+
4+
const INSTALL_STATE_STORAGE = Url.join(DATA_STORAGE, ".install-state");
5+
6+
export default class InstallState {
7+
/** @type Record<string, string> */
8+
store;
9+
/** @type Record<string, string> */
10+
updatedStore;
11+
12+
/**
13+
*
14+
* @param {string} id
15+
* @returns
16+
*/
17+
static async new(id) {
18+
const state = new InstallState();
19+
state.id = await checksumText(id);
20+
state.updatedStore = {};
21+
22+
if (!(await fsOperation(INSTALL_STATE_STORAGE).exists())) {
23+
await fsOperation(DATA_STORAGE).createDirectory(".install-state");
24+
}
25+
26+
state.storeUrl = Url.join(INSTALL_STATE_STORAGE, state.id);
27+
if (await fsOperation(state.storeUrl).exists()) {
28+
state.store = JSON.parse(
29+
await fsOperation(state.storeUrl).readFile("utf-8"),
30+
);
31+
} else {
32+
state.store = {};
33+
await fsOperation(INSTALL_STATE_STORAGE).createFile(state.id);
34+
}
35+
36+
return state;
37+
}
38+
39+
/**
40+
*
41+
* @param {string} url
42+
* @param {ArrayBuffer | string} content
43+
* @param {boolean} isString
44+
* @returns
45+
*/
46+
async isUpdated(url, content) {
47+
const current = this.store[url];
48+
const update =
49+
typeof content === "string"
50+
? await checksumText(content)
51+
: await checksum(content);
52+
this.updatedStore[url] = update;
53+
54+
if (current === update) {
55+
return false;
56+
} else {
57+
return true;
58+
}
59+
}
60+
61+
exists(url) {
62+
if (typeof this.store[url] !== "undefined") {
63+
return true;
64+
} else {
65+
return false;
66+
}
67+
}
68+
69+
async save() {
70+
this.store = this.updatedStore;
71+
await fsOperation(this.storeUrl).writeFile(
72+
JSON.stringify(this.updatedStore),
73+
);
74+
}
75+
}
76+
77+
/**
78+
* Derives the checksum of a Buffer
79+
* @param {BufferSource} data
80+
* @returns the derived checksum
81+
*/
82+
async function checksum(data) {
83+
const hashBuffer = await window.crypto.subtle.digest("SHA-256", data);
84+
const hashArray = Array.from(new Uint8Array(hashBuffer));
85+
const hashHex = hashArray
86+
.map((byte) => byte.toString(16).padStart(2, "0"))
87+
.join("");
88+
return hashHex;
89+
}
90+
91+
/**
92+
*
93+
* @param {string} text
94+
* @returns
95+
*/
96+
async function checksumText(text) {
97+
const textUint8 = new TextEncoder().encode(text);
98+
return await checksum(textUint8);
99+
}

0 commit comments

Comments
 (0)