Skip to content

Commit 17dc3e8

Browse files
timestamped view folder and add a timestamp to the main bundle (#31)
* put the view bundles in a timestamped directory - for cache and unique name problems * add a timestamp to the main bundle
1 parent c6e250f commit 17dc3e8

6 files changed

Lines changed: 139 additions & 37 deletions

File tree

‎common.js‎

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const { spawn } = require("child_process");
1313
const path = require("path");
1414
const fs = require("fs").promises;
1515

16-
const buildViewBundle = async (buildMode, viewName) => {
16+
const buildViewBundle = async (buildMode, viewName, timestamp) => {
1717
const tenant = db.getTenantSchema() || "public";
1818
return new Promise((resolve, reject) => {
1919
const child = spawn(
@@ -26,10 +26,14 @@ const buildViewBundle = async (buildMode, viewName) => {
2626
`view_name=${viewName}`,
2727
"--env",
2828
`tenant_name=${tenant}`,
29+
"--env",
30+
`timestamp=${timestamp}`,
31+
"--env",
32+
`bundle_name=${viewName}.bundle.js`,
2933
],
3034
{
3135
cwd: __dirname,
32-
}
36+
},
3337
);
3438
child.stdout.on("data", (data) => {
3539
getState().log(5, data.toString());
@@ -50,7 +54,13 @@ const buildViewBundle = async (buildMode, viewName) => {
5054

5155
const buildSafeViewName = (viewName) => viewName.replace(/[^a-zA-Z0-9]/g, "_");
5256

53-
const handleUserCode = async (userCode, buildMode, viewName) => {
57+
const handleUserCode = async (
58+
userCode,
59+
buildMode,
60+
viewName,
61+
oldTimestamp,
62+
newTimestamp,
63+
) => {
5464
const tenant = db.getTenantSchema() || "public";
5565
const userCodeDir = path.join(__dirname, "user-code", tenant);
5666
const codeDirExists = await fs
@@ -62,11 +72,22 @@ const handleUserCode = async (userCode, buildMode, viewName) => {
6272
await fs.writeFile(
6373
path.join(userCodeDir, `${safeViewName}.js`),
6474
userCode,
65-
"utf8"
75+
"utf8",
6676
);
67-
if ((await buildViewBundle(buildMode, safeViewName)) !== 0) {
77+
if ((await buildViewBundle(buildMode, safeViewName, newTimestamp)) !== 0) {
6878
throw new Error("Build failed please check your server logs");
6979
}
80+
try {
81+
await fs.rm(
82+
path.join(__dirname, "public", tenant, `${safeViewName}_${oldTimestamp}`),
83+
{ recursive: true, force: true },
84+
);
85+
} catch (err) {
86+
getState().log(
87+
2,
88+
"Error removing old directory: " + err.message || "Unknown error",
89+
);
90+
}
7091
};
7192

7293
module.exports = {

‎index.js‎

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
const Workflow = require("@saltcorn/data/models/workflow");
22
const Form = require("@saltcorn/data/models/form");
33
const File = require("@saltcorn/data/models/file");
4+
const Plugin = require("@saltcorn/data/models/plugin");
45
const db = require("@saltcorn/data/db");
56
const { getState } = require("@saltcorn/data/db/state");
67
const { spawn } = require("child_process");
78
const fs = require("fs").promises;
89
const path = require("path");
910

10-
const buildMainBundle = async (buildMode, libPath, libMain) => {
11+
const buildMainBundle = async (buildMode, libPath, libMain, timestamp) => {
1112
getState().log(6, `spawn ${buildMode} build of main bundle`);
1213
const tenant = db.getTenantSchema() || "public";
1314
return new Promise((resolve, reject) => {
@@ -23,10 +24,12 @@ const buildMainBundle = async (buildMode, libPath, libMain) => {
2324
`user_lib_main=${libMain}`,
2425
"--env",
2526
`tenant_name=${tenant}`,
27+
"--env",
28+
`timestamp=${timestamp}`,
2629
],
2730
{
2831
cwd: __dirname,
29-
}
32+
},
3033
);
3134
child.stdout.on("data", (data) => {
3235
getState().log(5, data.toString());
@@ -45,7 +48,12 @@ const buildMainBundle = async (buildMode, libPath, libMain) => {
4548
});
4649
};
4750

48-
const prepareDirectory = async ({ codeSource, codeLocation, buildMode }) => {
51+
const prepareDirectory = async ({
52+
codeSource,
53+
codeLocation,
54+
buildMode,
55+
timestamp,
56+
}) => {
4957
const userLibPath = async (source, location) => {
5058
switch (source) {
5159
case "local":
@@ -64,12 +72,12 @@ const prepareDirectory = async ({ codeSource, codeLocation, buildMode }) => {
6472
const libPath = await userLibPath(codeSource, codeLocation);
6573
const userLibMain = async () => {
6674
const packageJson = JSON.parse(
67-
await fs.readFile(path.join(libPath, "package.json"), "utf8")
75+
await fs.readFile(path.join(libPath, "package.json"), "utf8"),
6876
);
6977
if (packageJson.main) return packageJson.main;
7078
else {
7179
throw new Error(
72-
"No main field in package.json, please specify the main file"
80+
"No main field in package.json, please specify the main file",
7381
);
7482
}
7583
};
@@ -89,27 +97,38 @@ const prepareDirectory = async ({ codeSource, codeLocation, buildMode }) => {
8997
(await buildMainBundle(
9098
buildMode,
9199
libPath,
92-
libPath ? await userLibMain() : null
100+
libPath ? await userLibMain() : null,
101+
timestamp,
93102
)) !== 0
94103
) {
95104
throw new Error("Webpack failed, please check your Server logs");
96105
}
106+
// remove all main_bundle files except the one with the current timestamp
107+
const files = await fs.readdir(tenantDir);
108+
for (const file of files) {
109+
if (file.startsWith("main_bundle_") && !file.endsWith(`${timestamp}.js`)) {
110+
await fs.unlink(path.join(tenantDir, file));
111+
}
112+
}
97113
};
98114

99115
const configuration_workflow = () =>
100116
new Workflow({
101117
onDone: async (context) => {
102118
const { app_code_source, app_code_path, sc_folder, build_mode } = context;
119+
const timestamp = new Date().valueOf();
103120
await prepareDirectory({
104121
codeSource: app_code_source,
105122
codeLocation:
106123
app_code_source === "local"
107124
? app_code_path
108125
: app_code_source === "Saltcorn folder"
109-
? sc_folder
110-
: null,
126+
? sc_folder
127+
: null,
111128
buildMode: build_mode,
129+
timestamp,
112130
});
131+
context.timestamp = timestamp;
113132
return context;
114133
},
115134
steps: [
@@ -241,17 +260,31 @@ const routes = ({ app_code_source, app_code_path, sc_folder, build_mode }) => {
241260
getState().log(
242261
6,
243262
`app_code_source: ${app_code_source}, app_code_path: ${app_code_path}, ` +
244-
`sc_folder: ${sc_folder}, build_mode: ${build_mode}, `
263+
`sc_folder: ${sc_folder}, build_mode: ${build_mode}, `,
245264
);
265+
const timestamp = new Date().valueOf();
246266
await prepareDirectory({
247267
codeSource: app_code_source,
248268
codeLocation:
249269
app_code_source === "local"
250270
? app_code_path
251271
: app_code_source === "Saltcorn folder"
252-
? sc_folder
253-
: null,
272+
? sc_folder
273+
: null,
254274
buildMode: build_mode,
275+
timestamp,
276+
});
277+
let plugin = await Plugin.findOne({ name: "react" });
278+
if (!plugin) {
279+
plugin = await Plugin.findOne({
280+
name: "@saltcorn/react",
281+
});
282+
}
283+
plugin.configuration.timestamp = timestamp;
284+
await plugin.upsert();
285+
getState().processSend({
286+
refresh_plugin_cfg: plugin.name,
287+
tenant: db.getTenantSchema(),
255288
});
256289
res.json({ notify_success: "Build successful" });
257290
},
@@ -265,26 +298,27 @@ module.exports = {
265298
configuration_workflow,
266299
routes,
267300
viewtemplates: (cfg) => [require("./react_view")],
268-
headers: () => {
301+
headers: (cfg) => {
269302
const tenant = db.getTenantSchema() || "public";
270303
return [
271304
{
272305
headerTag: `<script>var tenant_name = "${tenant}";</script>`,
273306
},
274307
{
275-
script: `/plugins/public/react/${tenant}/main_bundle.js`,
308+
script: `/plugins/public/react/${tenant}/main_bundle_${cfg.timestamp}.js`,
276309
},
277310
];
278311
},
279312
copilot_skills: (cfg) => [require("./copilot-skill")(cfg)],
280313
onLoad: async (configuration) => {
314+
if (!require("cluster").isMaster) return;
281315
try {
282316
const tenant = db.getTenantSchema() || "public";
283317
const mainBundlePath = path.join(
284318
__dirname,
285319
"public",
286320
tenant,
287-
`main_bundle.js`
321+
`main_bundle_${configuration.timestamp}.js`,
288322
);
289323
const mainBundleExists = await fs
290324
.access(mainBundlePath)
@@ -294,15 +328,29 @@ module.exports = {
294328
getState().log(5, "Main bundle does not exist, building it now");
295329
const { app_code_source, app_code_path, sc_folder, build_mode } =
296330
configuration;
331+
const timestamp = new Date().valueOf();
297332
await prepareDirectory({
298333
codeSource: app_code_source,
299334
codeLocation:
300335
app_code_source === "local"
301336
? app_code_path
302337
: app_code_source === "Saltcorn folder"
303-
? sc_folder
304-
: null,
338+
? sc_folder
339+
: null,
305340
buildMode: build_mode,
341+
timestamp,
342+
});
343+
let plugin = await Plugin.findOne({ name: "react" });
344+
if (!plugin) {
345+
plugin = await Plugin.findOne({
346+
name: "@saltcorn/react",
347+
});
348+
}
349+
plugin.configuration.timestamp = timestamp;
350+
await plugin.upsert();
351+
getState().processSend({
352+
refresh_plugin_cfg: plugin.name,
353+
tenant: db.getTenantSchema(),
306354
});
307355
}
308356
} catch (e) {

‎main-code/index.js‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,31 +33,36 @@ const initMain = async () => {
3333
const rootElements = document.getElementsByClassName("_sc_react-view");
3434
for (const rootElement of rootElements) {
3535
const viewName = rootElement.getAttribute("view-name");
36+
const timestamp = rootElement.getAttribute("timestamp");
3637
if (remotesCfg.remotes.some((r) => r.name === viewName)) continue;
3738
remotesCfg.remotes.push({
3839
name: viewName,
3940
entry: isWeb
40-
? `/plugins/public/react/${tenant}/${viewName}_remote.js`
41+
? timestamp
42+
? `/plugins/public/react/${tenant}/${viewName}_${timestamp}/${viewName}_remote.js`
43+
: `/plugins/public/react/${tenant}/${viewName}_remote.js`
4144
: // TODO get the version from the plugin, for now hardcoded in any release
42-
`http://localhost/sc_plugins/public/react@0.1.5/${tenant}/${viewName}_remote.js`,
45+
timestamp
46+
? `http://localhost/sc_plugins/public/react@0.1.5/${tenant}/${viewName}_${timestamp}/${viewName}_remote.js`
47+
: `http://localhost/sc_plugins/public/react@0.1.5/${tenant}/${viewName}_remote.js`,
4348
});
4449
}
4550

4651
init(remotesCfg);
4752
for (const rootElement of rootElements) {
4853
const viewName = rootElement.getAttribute("view-name");
4954
const state = JSON.parse(
50-
decodeURIComponent(rootElement.getAttribute("state"))
55+
decodeURIComponent(rootElement.getAttribute("state")),
5156
);
5257
const query = JSON.parse(
53-
decodeURIComponent(rootElement.getAttribute("query"))
58+
decodeURIComponent(rootElement.getAttribute("query")),
5459
);
5560
const tableName = rootElement.getAttribute("table-name");
5661
const rows = JSON.parse(
57-
decodeURIComponent(rootElement.getAttribute("rows"))
62+
decodeURIComponent(rootElement.getAttribute("rows")),
5863
);
5964
const user = JSON.parse(
60-
decodeURIComponent(rootElement.getAttribute("user"))
65+
decodeURIComponent(rootElement.getAttribute("user")),
6166
);
6267
try {
6368
const remote = await loadRemote(`${viewName}/${viewName}`);

‎main-code/webpack.config.js‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = async (env) => {
99
? path.join(env.user_lib_path, userLibMainFile)
1010
: null;
1111
const tenantName = env.tenant_name || "public";
12+
const timestamp = env.timestamp;
1213
return {
1314
entry: path.join(__dirname, "./index.js"),
1415
output: {
@@ -39,7 +40,7 @@ module.exports = async (env) => {
3940
plugins: [
4041
new ModuleFederationPlugin({
4142
name: "main",
42-
filename: "main_bundle.js",
43+
filename: `main_bundle_${timestamp}.js`,
4344
exposes: indexPath
4445
? {
4546
"./index": indexPath,
@@ -78,15 +79,15 @@ module.exports = async (env) => {
7879
"react-dom": path.resolve(__dirname, "../node_modules/react-dom"),
7980
"@saltcorn/react-lib": path.resolve(
8081
__dirname,
81-
"../node_modules/@saltcorn/react-lib/dist/"
82+
"../node_modules/@saltcorn/react-lib/dist/",
8283
),
8384
"@saltcorn/react-lib/hooks": path.resolve(
8485
__dirname,
85-
"../node_modules/@saltcorn/react-lib/dist/hooks"
86+
"../node_modules/@saltcorn/react-lib/dist/hooks",
8687
),
8788
"@saltcorn/react-lib/components": path.resolve(
8889
__dirname,
89-
"../node_modules/@saltcorn/react-lib/dist/components"
90+
"../node_modules/@saltcorn/react-lib/dist/components",
9091
),
9192
},
9293
extensions: [".js", ".jsx"],

0 commit comments

Comments
 (0)