Repository navigation
Expand file tree
/
Copy pathbuild.js
More file actions
169 lines (153 loc) · 5.92 KB
/
Copy pathbuild.js
File metadata and controls
169 lines (153 loc) · 5.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// build.js
//
// Builds the Gophish frontend assets: concatenates + minifies the vendor JS
// bundle, transpiles + minifies each app script individually (since
// different pages load different combinations of them), and concatenates +
// minifies the CSS bundle.
//
// Replaces the previous Gulp + Webpack setup, which independently processed
// some of the same app files (passwords.js, users.js, webhooks.js) into the
// same output path.
const esbuild = require("esbuild");
const fs = require("fs");
const path = require("path");
const jsSrcDir = path.join("static", "js", "src");
const vendorDir = path.join(jsSrcDir, "vendor");
const appDir = path.join(jsSrcDir, "app");
const cssDir = path.join("static", "css");
const jsDistDir = path.join("static", "js", "dist");
const cssDistDir = path.join("static", "css", "dist");
// A single ordered list, since load order matters
const vendorFiles = [
path.join("node_modules", "jquery", "dist", "jquery.min.js"),
path.join("node_modules", "bootstrap", "dist", "js", "bootstrap.bundle.min.js"),
path.join("node_modules", "moment", "min", "moment.min.js"),
path.join("node_modules", "datatables.net", "js", "dataTables.min.js"),
path.join("node_modules", "datatables.net-bs5", "js", "dataTables.bootstrap5.min.js"),
"datetime-moment.js",
path.join("node_modules", "echarts", "dist", "echarts.min.js"),
path.join("node_modules", "select2", "dist", "js", "select2.min.js"),
path.join("node_modules", "papaparse", "papaparse.min.js"),
path.join("node_modules", "bowser", "es5.js"),
path.join("node_modules", "sweetalert2", "dist", "sweetalert2.min.js"),
path.join("node_modules", "ckeditor5", "dist", "browser", "ckeditor5.umd.js"),
];
function resolveVendorFile(f) {
return f.startsWith("node_modules" + path.sep) ? f : path.join(vendorDir, f);
}
// These app scripts don't import anything - they're loaded via plain
// <script> tags (no type="module") and rely on defining top-level `var`s
// and functions as real globals for other scripts/inline handlers to use.
// They must NOT be bundled: esbuild's bundler wraps the file (breaking
// that global-scope contract) and tree-shakes away anything it can't see
// used from within the file itself, which silently guts files like
// autocomplete.js down to nothing.
const appFiles = [
"ckeditor-setup.js",
"campaign_report.js",
"campaign_results.js",
"campaigns.js",
"content_packs.js",
"dashboard.js",
"groups.js",
"landing_pages.js",
"sending_profiles.js",
"settings.js",
"tags.js",
"templates.js",
"gophish.js",
"users.js",
"webhooks.js",
];
// passwords.js is the one app script with a real import (zxcvbn from
// node_modules), so it genuinely needs bundling to resolve that.
const bundledAppFiles = ["passwords.js"];
// A single ordered list, since cascade order matters (e.g. main.css must
// follow bootstrap.min.css to override it)
const cssFiles = [
path.join("node_modules", "bootstrap", "dist", "css", "bootstrap.min.css"),
"main.css",
"dashboard.css",
path.join("node_modules", "datatables.net-bs5", "css", "dataTables.bootstrap5.min.css"),
"font-awesome.min.css",
"checkbox.css",
path.join("node_modules", "select2-bootstrap-5-theme", "dist", "select2-bootstrap-5-theme.min.css"),
path.join("node_modules", "select2", "dist", "css", "select2.min.css"),
path.join("node_modules", "sweetalert2", "dist", "sweetalert2.min.css"),
path.join("node_modules", "ckeditor5", "dist", "browser", "ckeditor5.css"),
"theme.css",
];
function resolveCssFile(f) {
return f.startsWith("node_modules" + path.sep) ? f : path.join(cssDir, f);
}
async function buildVendor() {
const combined = vendorFiles
.map((f) => fs.readFileSync(resolveVendorFile(f), "utf8"))
.join("\n;\n");
const result = await esbuild.transform(combined, {
loader: "js",
minify: true,
});
fs.mkdirSync(jsDistDir, { recursive: true });
fs.writeFileSync(path.join(jsDistDir, "vendor.min.js"), result.code);
}
async function buildApp() {
const outDir = path.join(jsDistDir, "app");
fs.mkdirSync(outDir, { recursive: true });
for (const file of appFiles) {
const name = path.basename(file, ".js");
const source = fs.readFileSync(path.join(appDir, file), "utf8");
const result = await esbuild.transform(source, {
loader: "js",
target: "es2018",
minify: true,
});
fs.writeFileSync(path.join(outDir, `${name}.min.js`), result.code);
}
for (const file of bundledAppFiles) {
const name = path.basename(file, ".js");
await esbuild.build({
entryPoints: [path.join(appDir, file)],
bundle: true,
minify: true,
format: "iife",
target: "es2018",
outfile: path.join(outDir, `${name}.min.js`),
logLevel: "warning",
});
}
}
// world-atlas's TopoJSON (converted via topojson-client) stores landmasses
// like Russia/Fiji/Antarctica as rings that cross the antimeridian
// unsplit, relying on d3's projection step to clip them - ECharts' `geo`
// component does no such clipping and renders those as stripes straight
// across the map. world-map-geojson ships pre-split, ready-to-use GeoJSON
// instead (verified: no antimeridian-crossing rings, no empty geometries).
async function buildWorldMap() {
const geoJson = require("world-map-geojson");
fs.mkdirSync(jsDistDir, { recursive: true });
fs.writeFileSync(
path.join(jsDistDir, "world-map.js"),
"window.GophishWorldMap = " + JSON.stringify(geoJson) + ";"
);
}
async function buildCSS() {
const combined = cssFiles.map((f) => fs.readFileSync(resolveCssFile(f), "utf8")).join("\n");
const result = await esbuild.transform(combined, {
loader: "css",
minify: true,
});
fs.mkdirSync(cssDistDir, { recursive: true });
fs.writeFileSync(path.join(cssDistDir, "gophish.css"), result.code);
}
async function main() {
await buildVendor();
await buildApp();
await buildWorldMap();
await buildCSS();
console.log("Build complete.");
}
main().catch((err) => {
console.error(err);
process.exit(1);
});