@getargus/build-tools 0.0.0-stage → 0.3.0
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/LICENSE +21 -0
- package/README.md +218 -2
- package/bin/argus-build.cjs +8 -0
- package/dist/cli-C6Vb06ix.d.ts +317 -0
- package/dist/cli-Poqz5E1p.d.cts +317 -0
- package/dist/cli.cjs +1283 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1254 -0
- package/dist/cli.js.map +1 -0
- package/dist/ids-T7ddcOxQ.d.cts +41 -0
- package/dist/ids-T7ddcOxQ.d.ts +41 -0
- package/dist/index.cjs +1548 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +1486 -0
- package/dist/index.js.map +1 -0
- package/dist/metro.cjs +117 -0
- package/dist/metro.cjs.map +1 -0
- package/dist/metro.d.cts +40 -0
- package/dist/metro.d.ts +40 -0
- package/dist/metro.js +92 -0
- package/dist/metro.js.map +1 -0
- package/dist/next.cjs +609 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +60 -0
- package/dist/next.d.ts +60 -0
- package/dist/next.js +582 -0
- package/dist/next.js.map +1 -0
- package/package.json +67 -3
package/dist/next.js
ADDED
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
// src/next.ts
|
|
2
|
+
import { createRequire } from "module";
|
|
3
|
+
import { join as join2 } from "path";
|
|
4
|
+
|
|
5
|
+
// src/ids.ts
|
|
6
|
+
import { randomBytes } from "crypto";
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
+
import { dirname, resolve } from "path";
|
|
9
|
+
var NEXT_PUBLIC_ENV = "NEXT_PUBLIC_ARGUS_ARTIFACT_SET_ID";
|
|
10
|
+
var NEXT_SERVER_ENV = "ARGUS_SERVER_ARTIFACT_SET_ID";
|
|
11
|
+
var RE_ARTIFACT_SET_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
12
|
+
function isValidArtifactSetId(id) {
|
|
13
|
+
return typeof id === "string" && RE_ARTIFACT_SET_ID.test(id);
|
|
14
|
+
}
|
|
15
|
+
function assertArtifactSetId(id, what = "artifact set id") {
|
|
16
|
+
if (!isValidArtifactSetId(id)) {
|
|
17
|
+
throw new Error(`Argus: invalid ${what} ${JSON.stringify(id)} \u2014 expected 1..128 chars of [A-Za-z0-9._:-]`);
|
|
18
|
+
}
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
function newArtifactSetId(prefix = "build", now = Date.now()) {
|
|
22
|
+
const clean = Array.from(String(prefix)).filter((c) => /[A-Za-z0-9._-]/.test(c)).slice(0, 80).join("");
|
|
23
|
+
const b = randomBytes(16);
|
|
24
|
+
b.writeUIntBE(now, 0, 6);
|
|
25
|
+
b[6] = b[6] & 15 | 112;
|
|
26
|
+
b[8] = b[8] & 63 | 128;
|
|
27
|
+
return `${clean || "build"}-${b.toString("hex")}`;
|
|
28
|
+
}
|
|
29
|
+
function resolveArtifactSetId(input, platform, env = process.env) {
|
|
30
|
+
const fromEnv = env[`ARGUS_ARTIFACT_SET_ID_${platform.toUpperCase()}`] || env.ARGUS_ARTIFACT_SET_ID;
|
|
31
|
+
const fromOption = typeof input === "function" ? input(platform) : typeof input === "string" ? input : input?.[platform];
|
|
32
|
+
const id = fromEnv || fromOption;
|
|
33
|
+
return id ? assertArtifactSetId(id, `artifact set id for ${platform}`) : void 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/next-upload.ts
|
|
37
|
+
import { rmSync } from "fs";
|
|
38
|
+
|
|
39
|
+
// src/collect.ts
|
|
40
|
+
import { closeSync, existsSync as existsSync2, openSync, readFileSync as readFileSync2, readSync, readdirSync, statSync } from "fs";
|
|
41
|
+
import { basename, dirname as dirname2, join, relative, resolve as resolve2, sep } from "path";
|
|
42
|
+
function classifyArtifact(path) {
|
|
43
|
+
const lower = basename(path).toLowerCase();
|
|
44
|
+
if (lower.endsWith(".map")) return "sourcemap";
|
|
45
|
+
if (lower === "mapping.txt" || lower.endsWith(".mapping.txt")) return "proguard";
|
|
46
|
+
if (lower.endsWith(".so") || lower.endsWith(".so.dbg")) return "elf";
|
|
47
|
+
const parts = path.split(/[\\/]/);
|
|
48
|
+
if (parts.includes("DWARF") && parts.some((p) => p.endsWith(".dSYM"))) return "dsym";
|
|
49
|
+
if (/\.(js|mjs|cjs|bundle|jsbundle|hbc)$/.test(lower)) return "js_bundle";
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "cache", ".cache", "Pods"]);
|
|
53
|
+
function walkFiles(dir, opts = {}) {
|
|
54
|
+
const skip = opts.skipDirs ?? SKIP_DIRS;
|
|
55
|
+
const out = [];
|
|
56
|
+
const visit = (d) => {
|
|
57
|
+
let entries;
|
|
58
|
+
try {
|
|
59
|
+
entries = readdirSync(d, { withFileTypes: true });
|
|
60
|
+
} catch {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
for (const e of entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
64
|
+
const p = join(d, e.name);
|
|
65
|
+
if (e.isDirectory()) {
|
|
66
|
+
if (!skip.has(e.name)) visit(p);
|
|
67
|
+
} else if (e.isFile()) out.push(p);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
visit(dir);
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
function describe(paths) {
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const p of paths) {
|
|
76
|
+
const files = statSync(p).isDirectory() ? walkFiles(p, { skipDirs: /* @__PURE__ */ new Set() }) : [p];
|
|
77
|
+
for (const f of files) {
|
|
78
|
+
const kind = classifyArtifact(f);
|
|
79
|
+
if (kind) out.push({ path: f, kind, size: statSync(f).size });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
function meta(m) {
|
|
85
|
+
const out = {};
|
|
86
|
+
for (const k of ["release", "dist", "nativeRelease", "nativeBuild", "runtimeVersion", "gitSha"]) {
|
|
87
|
+
if (m[k]) out[k] = m[k];
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
var RE_JS = /\.(js|mjs|cjs|bundle|jsbundle|hbc)$/;
|
|
92
|
+
var RE_JS_MAP = /\.(js|mjs|cjs|bundle|jsbundle|hbc)\.map$/;
|
|
93
|
+
function referencedMap(file) {
|
|
94
|
+
let tail = "";
|
|
95
|
+
try {
|
|
96
|
+
const size = statSync(file).size;
|
|
97
|
+
const buf = Buffer.alloc(Math.min(size, 1024));
|
|
98
|
+
const fd = openSync(file, "r");
|
|
99
|
+
try {
|
|
100
|
+
readSync(fd, buf, 0, buf.length, size - buf.length);
|
|
101
|
+
} finally {
|
|
102
|
+
closeSync(fd);
|
|
103
|
+
}
|
|
104
|
+
tail = buf.toString("utf8");
|
|
105
|
+
} catch {
|
|
106
|
+
return void 0;
|
|
107
|
+
}
|
|
108
|
+
const ref = /\/\/[#@] sourceMappingURL=([^\s'"]+)\s*$/.exec(tail)?.[1];
|
|
109
|
+
if (!ref || /^[a-z][a-z0-9+.-]*:/i.test(ref)) return void 0;
|
|
110
|
+
return resolve2(dirname2(file), ref.split(/[?#]/)[0]);
|
|
111
|
+
}
|
|
112
|
+
function jsPairs(dir, includeBundles, warnings, label) {
|
|
113
|
+
const files = walkFiles(dir);
|
|
114
|
+
const set = new Set(files);
|
|
115
|
+
const orphans = new Set(files.filter((f) => RE_JS_MAP.test(f.toLowerCase()) && !set.has(f.slice(0, -4))));
|
|
116
|
+
const generatedFor = {};
|
|
117
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
118
|
+
if (orphans.size > 0) {
|
|
119
|
+
for (const f of files) {
|
|
120
|
+
if (!RE_JS.test(f.toLowerCase()) || set.has(`${f}.map`)) continue;
|
|
121
|
+
const map = referencedMap(f);
|
|
122
|
+
if (map && orphans.has(map) && !generatedFor[map]) {
|
|
123
|
+
generatedFor[map] = f;
|
|
124
|
+
referenced.add(f);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const out = [];
|
|
129
|
+
let unmapped = 0;
|
|
130
|
+
for (const f of files) {
|
|
131
|
+
const lower = f.toLowerCase();
|
|
132
|
+
if (RE_JS_MAP.test(lower)) {
|
|
133
|
+
const generated = generatedFor[f] ?? (set.has(f.slice(0, -4)) ? f.slice(0, -4) : void 0);
|
|
134
|
+
if (!generated) warnings.push(`${label}: ${f} has no generated file next to it`);
|
|
135
|
+
out.push(f);
|
|
136
|
+
if (includeBundles && generated) out.push(generated);
|
|
137
|
+
} else if (RE_JS.test(lower) && !set.has(`${f}.map`) && !referenced.has(f)) unmapped++;
|
|
138
|
+
}
|
|
139
|
+
if (unmapped > 0) warnings.push(`${label}: ${unmapped} JS file(s) without a source map were skipped`);
|
|
140
|
+
return { paths: [...new Set(out)].sort(), generatedFor };
|
|
141
|
+
}
|
|
142
|
+
var NO_PAIRS = { paths: [], generatedFor: {} };
|
|
143
|
+
var renamed = (pairs) => Object.keys(pairs.generatedFor).length > 0 ? { generatedFor: pairs.generatedFor } : {};
|
|
144
|
+
function planNext(options) {
|
|
145
|
+
const projectDir = resolve2(options.projectDir ?? process.cwd());
|
|
146
|
+
const dist = resolve2(projectDir, options.distDir ?? ".next");
|
|
147
|
+
if (!existsSync2(dist)) throw new Error(`Argus: Next build output not found at ${dist} (run \`next build\` first)`);
|
|
148
|
+
const web = assertArtifactSetId(options.artifactSetId);
|
|
149
|
+
const node = assertArtifactSetId(options.serverArtifactSetId ?? `${web}-node`, "server artifact set id");
|
|
150
|
+
if (web === node) throw new Error("Argus: browser and server chunks must use different artifact set ids");
|
|
151
|
+
const include = options.includeBundles !== false;
|
|
152
|
+
const plans = [];
|
|
153
|
+
const notes = [];
|
|
154
|
+
const browserWarnings = [];
|
|
155
|
+
const browser = existsSync2(join(dist, "static")) ? jsPairs(join(dist, "static"), include, browserWarnings, "next:browser") : NO_PAIRS;
|
|
156
|
+
if (browser.paths.some((f) => f.endsWith(".map"))) {
|
|
157
|
+
plans.push({
|
|
158
|
+
label: "next:browser",
|
|
159
|
+
artifactSetId: web,
|
|
160
|
+
platform: "web",
|
|
161
|
+
paths: browser.paths,
|
|
162
|
+
files: describe(browser.paths),
|
|
163
|
+
stripPrefix: dist,
|
|
164
|
+
urlPrefix: options.urlPrefix ?? "/_next/",
|
|
165
|
+
...renamed(browser),
|
|
166
|
+
...meta(options),
|
|
167
|
+
warnings: browserWarnings
|
|
168
|
+
});
|
|
169
|
+
} else notes.push("next:browser: no source maps under static/ (is productionBrowserSourceMaps enabled?)");
|
|
170
|
+
const serverWarnings = [];
|
|
171
|
+
const server = existsSync2(join(dist, "server")) ? jsPairs(join(dist, "server"), include, serverWarnings, "next:server") : NO_PAIRS;
|
|
172
|
+
if (server.paths.some((f) => f.endsWith(".map"))) {
|
|
173
|
+
plans.push({
|
|
174
|
+
label: "next:server",
|
|
175
|
+
artifactSetId: node,
|
|
176
|
+
platform: "node",
|
|
177
|
+
paths: server.paths,
|
|
178
|
+
files: describe(server.paths),
|
|
179
|
+
// Server frames are filesystem paths ending in `.next/server/...`.
|
|
180
|
+
stripPrefix: projectDir,
|
|
181
|
+
urlPrefix: "",
|
|
182
|
+
...renamed(server),
|
|
183
|
+
...meta(options),
|
|
184
|
+
warnings: serverWarnings
|
|
185
|
+
});
|
|
186
|
+
} else notes.push("next:server: no source maps under server/ (server source maps are not emitted by default)");
|
|
187
|
+
if (plans.length === 0) throw new Error(`Argus: no source maps found in ${dist}. ${notes.join("; ")}`);
|
|
188
|
+
plans[0].warnings.push(...notes);
|
|
189
|
+
return plans;
|
|
190
|
+
}
|
|
191
|
+
function browserSourceMaps(projectDir, distDir = ".next") {
|
|
192
|
+
const dir = resolve2(projectDir, distDir, "static");
|
|
193
|
+
return existsSync2(dir) ? walkFiles(dir).filter((f) => f.endsWith(".map")) : [];
|
|
194
|
+
}
|
|
195
|
+
function uploadArgs(plan, opts = {}) {
|
|
196
|
+
const args = ["artifacts", "upload", "--set-id", plan.artifactSetId, "--platform", plan.platform];
|
|
197
|
+
const flag = (name, value) => {
|
|
198
|
+
if (value !== void 0 && value !== "") args.push(name, value);
|
|
199
|
+
};
|
|
200
|
+
flag("--url", opts.url);
|
|
201
|
+
flag("--release", plan.release);
|
|
202
|
+
flag("--dist", plan.dist);
|
|
203
|
+
flag("--native-release", plan.nativeRelease);
|
|
204
|
+
flag("--native-build", plan.nativeBuild);
|
|
205
|
+
flag("--runtime-version", plan.runtimeVersion);
|
|
206
|
+
if (plan.ota) args.push("--ota");
|
|
207
|
+
flag("--update-id", plan.updateId);
|
|
208
|
+
flag("--parent-set-id", plan.parentSetId);
|
|
209
|
+
flag("--git-sha", plan.gitSha);
|
|
210
|
+
flag("--strip-prefix", plan.stripPrefix);
|
|
211
|
+
flag("--url-prefix", plan.urlPrefix);
|
|
212
|
+
if (opts.dryRun) args.push("--dry-run");
|
|
213
|
+
args.push("--", ...plan.paths);
|
|
214
|
+
return args;
|
|
215
|
+
}
|
|
216
|
+
function manifestName(plan, file) {
|
|
217
|
+
const generated = file.endsWith(".map") ? plan.generatedFor?.[file] ?? file.slice(0, -4) : file;
|
|
218
|
+
const prefix = plan.stripPrefix;
|
|
219
|
+
const rel = prefix && (generated === prefix || generated.startsWith(prefix.endsWith(sep) ? prefix : prefix + sep)) ? relative(prefix, generated) : basename(generated);
|
|
220
|
+
return `${plan.urlPrefix ?? ""}${rel.split(sep).join("/").replace(/^\/+/, "")}`;
|
|
221
|
+
}
|
|
222
|
+
function formatCommand(bin, args, cwd = process.cwd()) {
|
|
223
|
+
const quote = (s) => /^[A-Za-z0-9_./:@=+-]+$/.test(s) ? s : `'${s.replace(/'/g, `'\\''`)}'`;
|
|
224
|
+
const short = (s) => {
|
|
225
|
+
if (!s.startsWith(cwd + sep)) return s;
|
|
226
|
+
return relative(cwd, s);
|
|
227
|
+
};
|
|
228
|
+
return [bin, ...args.map((a) => quote(short(a)))].join(" ");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/upload.ts
|
|
232
|
+
import { spawn } from "child_process";
|
|
233
|
+
|
|
234
|
+
// src/http-upload.ts
|
|
235
|
+
import { createHash } from "crypto";
|
|
236
|
+
import { createReadStream, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, statSync as statSync2, readFileSync as readFileSync3 } from "fs";
|
|
237
|
+
import { basename as basename2, relative as relative2, sep as sep2 } from "path";
|
|
238
|
+
var NATIVE_KINDS = /* @__PURE__ */ new Set(["dsym", "elf", "proguard"]);
|
|
239
|
+
function isJsOnlyPlan(plan) {
|
|
240
|
+
return plan.files.length > 0 && plan.files.every((f) => !NATIVE_KINDS.has(f.kind));
|
|
241
|
+
}
|
|
242
|
+
function sha256File(path) {
|
|
243
|
+
return new Promise((resolvePromise, reject) => {
|
|
244
|
+
const hash = createHash("sha256");
|
|
245
|
+
createReadStream(path).on("data", (chunk) => hash.update(chunk)).on("error", reject).on("end", () => resolvePromise(hash.digest("hex")));
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
function isHermesMap(path) {
|
|
249
|
+
const needle = "x_facebook_sources";
|
|
250
|
+
const head = Buffer.alloc(256 * 1024);
|
|
251
|
+
const fd = openSync2(path, "r");
|
|
252
|
+
let n = 0;
|
|
253
|
+
try {
|
|
254
|
+
n = readSync2(fd, head, 0, head.length, 0);
|
|
255
|
+
} finally {
|
|
256
|
+
closeSync2(fd);
|
|
257
|
+
}
|
|
258
|
+
if (head.subarray(0, n).includes(needle)) return true;
|
|
259
|
+
return statSync2(path).size > n && readFileSync3(path).includes(needle);
|
|
260
|
+
}
|
|
261
|
+
function setName(plan, file) {
|
|
262
|
+
const prefix = plan.stripPrefix;
|
|
263
|
+
const inside = prefix && (file === prefix || file.startsWith(prefix.endsWith(sep2) ? prefix : prefix + sep2));
|
|
264
|
+
return (inside ? relative2(prefix, file) : file).split(sep2).join("/").replace(/^\/+/, "");
|
|
265
|
+
}
|
|
266
|
+
async function buildManifest(plan) {
|
|
267
|
+
const found = [];
|
|
268
|
+
const paths = [...new Set(plan.files.map((f) => f.path))].sort();
|
|
269
|
+
for (const path of paths) {
|
|
270
|
+
const kind = plan.files.find((f) => f.path === path).kind;
|
|
271
|
+
if (NATIVE_KINDS.has(kind)) throw new Error(`Argus: ${basename2(path)} is a native symbol file; upload it with the argus CLI`);
|
|
272
|
+
const size = statSync2(path).size;
|
|
273
|
+
if (size === 0) throw new Error(`Argus: ${path} is empty`);
|
|
274
|
+
const sha256 = await sha256File(path);
|
|
275
|
+
if (kind === "sourcemap") {
|
|
276
|
+
found.push({
|
|
277
|
+
path,
|
|
278
|
+
file: { kind: isHermesMap(path) ? "hermes_sourcemap" : "sourcemap", name: setName(plan, path), bundle_name: manifestName(plan, path), sha256, size }
|
|
279
|
+
});
|
|
280
|
+
} else {
|
|
281
|
+
found.push({ path, file: { kind: "js_bundle", name: manifestName(plan, path), sha256, size } });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
found.sort((a, b) => a.file.name < b.file.name ? -1 : a.file.name > b.file.name ? 1 : 0);
|
|
285
|
+
const unique = found.filter((f, i) => i === 0 || found[i - 1].file.name !== f.file.name);
|
|
286
|
+
if (unique.length === 0) throw new Error("Argus: no artifacts found in the given paths");
|
|
287
|
+
const manifest = {
|
|
288
|
+
schema_version: 1,
|
|
289
|
+
artifact_set_id: plan.artifactSetId,
|
|
290
|
+
platform: plan.platform,
|
|
291
|
+
is_embedded: !plan.ota,
|
|
292
|
+
files: unique.map((f) => f.file)
|
|
293
|
+
};
|
|
294
|
+
const set = (key, value) => {
|
|
295
|
+
if (value !== void 0 && value !== "") manifest[key] = value;
|
|
296
|
+
};
|
|
297
|
+
set("release", plan.release);
|
|
298
|
+
set("dist", plan.dist);
|
|
299
|
+
set("native_release", plan.nativeRelease);
|
|
300
|
+
set("native_build", plan.nativeBuild);
|
|
301
|
+
set("runtime_version", plan.runtimeVersion);
|
|
302
|
+
set("update_id", plan.updateId);
|
|
303
|
+
set("git_sha", plan.gitSha);
|
|
304
|
+
set("parent_artifact_set_id", plan.parentSetId);
|
|
305
|
+
return { manifest, found: unique };
|
|
306
|
+
}
|
|
307
|
+
var defaultFetch = async (url, init) => {
|
|
308
|
+
const res = await fetch(url, init);
|
|
309
|
+
return { status: res.status, text: () => res.text() };
|
|
310
|
+
};
|
|
311
|
+
async function call(doFetch, method, url, key, body) {
|
|
312
|
+
const headers = { authorization: `Bearer ${key}` };
|
|
313
|
+
let payload;
|
|
314
|
+
if (body?.json !== void 0) {
|
|
315
|
+
headers["content-type"] = "application/json";
|
|
316
|
+
payload = JSON.stringify(body.json);
|
|
317
|
+
} else if (body?.bytes) {
|
|
318
|
+
headers["content-type"] = "application/octet-stream";
|
|
319
|
+
payload = body.bytes;
|
|
320
|
+
}
|
|
321
|
+
const init = { method, headers };
|
|
322
|
+
if (payload !== void 0) init.body = payload;
|
|
323
|
+
let res;
|
|
324
|
+
try {
|
|
325
|
+
res = await doFetch(url, init);
|
|
326
|
+
} catch (e) {
|
|
327
|
+
throw new Error(`Argus: cannot reach ${new URL(url).origin} (${e instanceof Error ? e.message : String(e)})`);
|
|
328
|
+
}
|
|
329
|
+
const text = await res.text();
|
|
330
|
+
let parsed;
|
|
331
|
+
try {
|
|
332
|
+
parsed = text ? JSON.parse(text) : {};
|
|
333
|
+
} catch {
|
|
334
|
+
parsed = void 0;
|
|
335
|
+
}
|
|
336
|
+
if (res.status < 200 || res.status >= 300) {
|
|
337
|
+
const detail = parsed && typeof parsed === "object" ? JSON.stringify(parsed) : text.slice(0, 300);
|
|
338
|
+
throw new Error(`Argus: ${method} ${new URL(url).pathname} \u2192 HTTP ${res.status} ${detail}`);
|
|
339
|
+
}
|
|
340
|
+
if (!parsed || typeof parsed !== "object") throw new Error(`Argus: ${method} ${new URL(url).pathname} did not return JSON (is ARGUS_URL the server's base URL?)`);
|
|
341
|
+
return parsed;
|
|
342
|
+
}
|
|
343
|
+
async function uploadPlanOverHttp(plan, options) {
|
|
344
|
+
const log = options.log ?? ((line) => console.error(line));
|
|
345
|
+
const { manifest, found } = await buildManifest(plan);
|
|
346
|
+
if (options.dryRun) {
|
|
347
|
+
log(JSON.stringify(manifest, null, 2));
|
|
348
|
+
return { status: "dry_run", uploaded: 0, files: found.length };
|
|
349
|
+
}
|
|
350
|
+
const base = options.url.replace(/\/+$/, "");
|
|
351
|
+
if (!/^https?:\/\//i.test(base)) throw new Error("Argus: ARGUS_URL must be the server base URL (https://host)");
|
|
352
|
+
const doFetch = options.fetch ?? defaultFetch;
|
|
353
|
+
const id = encodeURIComponent(plan.artifactSetId);
|
|
354
|
+
const created = await call(doFetch, "POST", `${base}/v1/artifact-sets`, options.uploadKey, { json: manifest });
|
|
355
|
+
const states = Array.isArray(created.files) ? created.files : [];
|
|
356
|
+
const done = /* @__PURE__ */ new Set();
|
|
357
|
+
let uploaded = 0;
|
|
358
|
+
for (const f of states) {
|
|
359
|
+
if (!f.sha256 || f.state === "uploaded" || done.has(f.sha256)) continue;
|
|
360
|
+
done.add(f.sha256);
|
|
361
|
+
const local = found.find((x) => x.file.sha256 === f.sha256);
|
|
362
|
+
if (!local) throw new Error(`Argus: server asks for unknown blob ${f.sha256}`);
|
|
363
|
+
log(`[argus] uploading ${local.file.name} (${local.file.size} bytes)`);
|
|
364
|
+
await call(doFetch, "PUT", `${base}/v1/artifact-sets/${id}/files/${f.sha256}`, options.uploadKey, { bytes: readFileSync3(local.path) });
|
|
365
|
+
uploaded++;
|
|
366
|
+
}
|
|
367
|
+
const final = await call(doFetch, "POST", `${base}/v1/artifact-sets/${id}/finalize`, options.uploadKey);
|
|
368
|
+
const status = typeof final.status === "string" ? final.status : "unknown";
|
|
369
|
+
const queued = typeof final.reprocess_queued === "number" ? final.reprocess_queued : 0;
|
|
370
|
+
log(`[argus] set ${plan.artifactSetId}: ${status}, ${found.length} file(s), ${uploaded} uploaded${queued > 0 ? `, ${queued} waiting event(s) queued for reprocessing` : ""}`);
|
|
371
|
+
if (status !== "ready") throw new Error(`Argus: artifact set ${plan.artifactSetId} is not ready (${status})`);
|
|
372
|
+
return { status, uploaded, files: found.length };
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// src/upload.ts
|
|
376
|
+
function defaultExec(bin, args, opts) {
|
|
377
|
+
return new Promise((resolvePromise, reject) => {
|
|
378
|
+
const child = spawn(bin, args, { stdio: "inherit", env: opts.env, cwd: opts.cwd });
|
|
379
|
+
child.on("error", (e) => {
|
|
380
|
+
reject(e.code === "ENOENT" ? new Error(`Argus: cannot run "${bin}" \u2014 install the argus CLI or set ARGUS_BIN`) : e);
|
|
381
|
+
});
|
|
382
|
+
child.on("close", (code) => resolvePromise(code ?? 1));
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
async function runUploads(plans, options = {}) {
|
|
386
|
+
const env = options.env ?? process.env;
|
|
387
|
+
const cwd = options.cwd ?? process.cwd();
|
|
388
|
+
const bin = options.bin ?? env.ARGUS_BIN ?? "argus";
|
|
389
|
+
const exec = options.exec ?? defaultExec;
|
|
390
|
+
const log = options.log ?? ((line) => console.error(line));
|
|
391
|
+
const requested = options.transport ?? env.ARGUS_UPLOAD_TRANSPORT ?? "auto";
|
|
392
|
+
if (requested !== "auto" && requested !== "http" && requested !== "cli") throw new Error(`Argus: unknown upload transport "${requested}" (auto, http or cli)`);
|
|
393
|
+
const transport = requested;
|
|
394
|
+
const results = [];
|
|
395
|
+
if (!options.dryRun && !env.ARGUS_UPLOAD_KEY) throw new Error("Argus: ARGUS_UPLOAD_KEY is not set (an upload key, never the public ingest key)");
|
|
396
|
+
for (const plan of plans) {
|
|
397
|
+
const argOpts = {};
|
|
398
|
+
if (options.dryRun) argOpts.dryRun = true;
|
|
399
|
+
if (options.url) argOpts.url = options.url;
|
|
400
|
+
const args = uploadArgs(plan, argOpts);
|
|
401
|
+
const command = formatCommand(bin, args, cwd);
|
|
402
|
+
for (const w of plan.warnings) log(`[argus] warning: ${w}`);
|
|
403
|
+
log(`[argus] ${plan.label}: ${plan.files.length} file(s) \u2192 set ${plan.artifactSetId}`);
|
|
404
|
+
const viaHttp = transport === "http" || transport === "auto" && !options.exec && !env.ARGUS_BIN && isJsOnlyPlan(plan);
|
|
405
|
+
if (viaHttp) {
|
|
406
|
+
const url = options.url ?? env.ARGUS_URL;
|
|
407
|
+
if (!url) throw new Error("Argus: ARGUS_URL is not set (the server base URL, e.g. https://argus.example.com)");
|
|
408
|
+
const http = { url, uploadKey: env.ARGUS_UPLOAD_KEY ?? "", log };
|
|
409
|
+
if (options.dryRun) http.dryRun = true;
|
|
410
|
+
if (options.fetch) http.fetch = options.fetch;
|
|
411
|
+
let code2 = 0;
|
|
412
|
+
try {
|
|
413
|
+
await uploadPlanOverHttp(plan, http);
|
|
414
|
+
} catch (e) {
|
|
415
|
+
log(`[argus] ${e instanceof Error ? e.message : String(e)}`);
|
|
416
|
+
code2 = 1;
|
|
417
|
+
}
|
|
418
|
+
results.push({ plan, command: `http upload \u2192 ${url}`, code: code2 });
|
|
419
|
+
if (code2 !== 0) break;
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
const renamedMaps = Object.keys(plan.generatedFor ?? {}).length;
|
|
423
|
+
if (renamedMaps > 0) log(`[argus] warning: ${plan.label}: ${renamedMaps} source map(s) are tied to their chunk only by its sourceMappingURL comment (Turbopack); the argus CLI names them after the map file, so their frames will not resolve \u2014 use the http transport`);
|
|
424
|
+
const code = await exec(bin, args, { env, cwd });
|
|
425
|
+
results.push({ plan, command, code });
|
|
426
|
+
if (code !== 0) break;
|
|
427
|
+
}
|
|
428
|
+
return results;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/next-upload.ts
|
|
432
|
+
async function uploadNextBuild(options) {
|
|
433
|
+
const env = options.env ?? process.env;
|
|
434
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
435
|
+
const uploaded = [];
|
|
436
|
+
let error;
|
|
437
|
+
try {
|
|
438
|
+
const missing = ["ARGUS_URL", "ARGUS_UPLOAD_KEY"].filter((k) => !env[k]);
|
|
439
|
+
if (missing.length > 0) throw new Error(`${missing.join(" and ")} not set`);
|
|
440
|
+
if (!options.artifactSetId) throw new Error("no artifact set id (set ARGUS_ARTIFACT_SET_ID or the artifactSetId option)");
|
|
441
|
+
const plan = { projectDir: options.projectDir, distDir: options.distDir, artifactSetId: options.artifactSetId };
|
|
442
|
+
if (options.serverArtifactSetId) plan.serverArtifactSetId = options.serverArtifactSetId;
|
|
443
|
+
if (options.urlPrefix !== void 0) plan.urlPrefix = options.urlPrefix;
|
|
444
|
+
if (env.ARGUS_RELEASE) plan.release = env.ARGUS_RELEASE;
|
|
445
|
+
if (env.ARGUS_GIT_SHA) plan.gitSha = env.ARGUS_GIT_SHA;
|
|
446
|
+
const run = {
|
|
447
|
+
env,
|
|
448
|
+
cwd: options.projectDir,
|
|
449
|
+
// One line per chunk would bury the build log, and every Next build has runtime files
|
|
450
|
+
// without a map; the per-set summary line stays.
|
|
451
|
+
log: (line) => {
|
|
452
|
+
if (!line.startsWith("[argus] uploading ") && !line.endsWith("without a source map were skipped")) log(line);
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
if (options.fetch) run.fetch = options.fetch;
|
|
456
|
+
const results = await runUploads(planNext(plan), run);
|
|
457
|
+
for (const r of results) if (r.code === 0) uploaded.push(r.plan.artifactSetId);
|
|
458
|
+
const failed = results.find((r) => r.code !== 0);
|
|
459
|
+
if (failed) throw new Error(`${failed.plan.label} was not uploaded (${failed.command})`);
|
|
460
|
+
} catch (e) {
|
|
461
|
+
error = (e instanceof Error ? e.message : String(e)).replace(/^Argus: /, "");
|
|
462
|
+
if (options.onUploadError === "fail") {
|
|
463
|
+
throw new Error(`Argus: source map upload failed: ${error}`);
|
|
464
|
+
}
|
|
465
|
+
log(`[argus] warning: source map upload failed, the build continues (onUploadError: 'fail' stops it): ${error}`);
|
|
466
|
+
}
|
|
467
|
+
let deletedMaps = 0;
|
|
468
|
+
if (options.deleteBrowserMaps) {
|
|
469
|
+
const maps = browserSourceMaps(options.projectDir, options.distDir);
|
|
470
|
+
for (const m of maps) rmSync(m, { force: true });
|
|
471
|
+
deletedMaps = maps.length;
|
|
472
|
+
if (deletedMaps > 0) log(`[argus] removed ${deletedMaps} browser source map(s) from the build output${error ? " (they were not uploaded: errors of this build stay unsymbolicated)" : ""}`);
|
|
473
|
+
}
|
|
474
|
+
const result = { ok: !error, uploaded, deletedMaps };
|
|
475
|
+
if (error) result.error = error;
|
|
476
|
+
return result;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/next.ts
|
|
480
|
+
function nextArtifactSetIds(options = {}) {
|
|
481
|
+
const env = options.env ?? process.env;
|
|
482
|
+
const web = resolveArtifactSetId(options.artifactSetId, "web", env);
|
|
483
|
+
const nodeExplicit = env.ARGUS_ARTIFACT_SET_ID_NODE || options.serverArtifactSetId;
|
|
484
|
+
const node = nodeExplicit ? assertArtifactSetId(nodeExplicit, "server artifact set id") : web ? assertArtifactSetId(`${web}-node`, "server artifact set id") : void 0;
|
|
485
|
+
return { web, node };
|
|
486
|
+
}
|
|
487
|
+
var PHASE_PRODUCTION_BUILD = "phase-production-build";
|
|
488
|
+
var WARNED_ENV = "__ARGUS_NEXT_UPLOAD_WARNED";
|
|
489
|
+
function installedNextVersion() {
|
|
490
|
+
try {
|
|
491
|
+
const { version } = createRequire(join2(process.cwd(), "package.json"))("next/package.json");
|
|
492
|
+
const m = /^(\d+)\.(\d+)\./.exec(version ?? "");
|
|
493
|
+
return m ? [Number(m[1]), Number(m[2])] : void 0;
|
|
494
|
+
} catch {
|
|
495
|
+
return void 0;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function nextUrlPrefix(config) {
|
|
499
|
+
let base = typeof config.assetPrefix === "string" && config.assetPrefix ? config.assetPrefix : typeof config.basePath === "string" ? config.basePath : "";
|
|
500
|
+
if (/^https?:\/\//i.test(base)) {
|
|
501
|
+
try {
|
|
502
|
+
base = new URL(base).pathname;
|
|
503
|
+
} catch {
|
|
504
|
+
base = "";
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
const path = base.replace(/^\/+|\/+$/g, "");
|
|
508
|
+
return path ? `/${path}/_next/` : "/_next/";
|
|
509
|
+
}
|
|
510
|
+
function apply(config, options, phase) {
|
|
511
|
+
const env = options.env ?? process.env;
|
|
512
|
+
const mode = options.upload ?? "auto";
|
|
513
|
+
let uploads = mode === true || mode === "auto" && !!env.ARGUS_UPLOAD_KEY && !!env.ARGUS_URL;
|
|
514
|
+
if (uploads) {
|
|
515
|
+
const version = installedNextVersion();
|
|
516
|
+
if (version && (version[0] < 15 || version[0] === 15 && version[1] < 4)) {
|
|
517
|
+
uploads = false;
|
|
518
|
+
if (!env[WARNED_ENV]) {
|
|
519
|
+
env[WARNED_ENV] = "1";
|
|
520
|
+
(options.log ?? console.warn)(
|
|
521
|
+
`[argus] source maps are not uploaded by this build: Next ${version.join(".")} has no compiler.runAfterProductionCompile (15.4+). Run \`argus-build upload next --delete-maps\` after \`next build\`.`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
let ids = nextArtifactSetIds(options);
|
|
527
|
+
const building = phase !== void 0 ? phase === PHASE_PRODUCTION_BUILD : env.NODE_ENV === "production";
|
|
528
|
+
if (uploads && building && !ids.web) {
|
|
529
|
+
env.ARGUS_ARTIFACT_SET_ID_WEB = newArtifactSetId("next");
|
|
530
|
+
ids = nextArtifactSetIds(options);
|
|
531
|
+
}
|
|
532
|
+
const out = { ...config, env: { ...config.env } };
|
|
533
|
+
if (ids.web) out.env[NEXT_PUBLIC_ENV] = ids.web;
|
|
534
|
+
if (ids.node) out.env[NEXT_SERVER_ENV] = ids.node;
|
|
535
|
+
const wantMaps = options.browserSourceMaps ?? uploads;
|
|
536
|
+
const enabledMaps = wantMaps && config.productionBrowserSourceMaps === void 0;
|
|
537
|
+
if (enabledMaps) out.productionBrowserSourceMaps = true;
|
|
538
|
+
if (options.serverSourceMaps ?? uploads) {
|
|
539
|
+
const previous = config.webpack;
|
|
540
|
+
out.webpack = (webpackConfig, context) => {
|
|
541
|
+
const next = typeof previous === "function" ? previous(webpackConfig, context) : webpackConfig;
|
|
542
|
+
if (context?.isServer && !context.dev) next.devtool = "source-map";
|
|
543
|
+
return next;
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
if (uploads) {
|
|
547
|
+
const compiler = config.compiler ?? {};
|
|
548
|
+
const previous = compiler.runAfterProductionCompile;
|
|
549
|
+
const hook = async (metadata) => {
|
|
550
|
+
if (typeof previous === "function") await previous(metadata);
|
|
551
|
+
const upload = {
|
|
552
|
+
projectDir: metadata.projectDir,
|
|
553
|
+
distDir: metadata.distDir,
|
|
554
|
+
artifactSetId: ids.web,
|
|
555
|
+
serverArtifactSetId: ids.node,
|
|
556
|
+
urlPrefix: options.urlPrefix ?? nextUrlPrefix(config),
|
|
557
|
+
onUploadError: options.onUploadError ?? "warn",
|
|
558
|
+
deleteBrowserMaps: options.deleteBrowserMapsAfterUpload ?? enabledMaps,
|
|
559
|
+
env
|
|
560
|
+
};
|
|
561
|
+
if (options.log) upload.log = options.log;
|
|
562
|
+
await uploadNextBuild(upload);
|
|
563
|
+
};
|
|
564
|
+
out.compiler = { ...compiler, runAfterProductionCompile: hook };
|
|
565
|
+
}
|
|
566
|
+
return out;
|
|
567
|
+
}
|
|
568
|
+
function withArgus(nextConfig, options = {}) {
|
|
569
|
+
if (typeof nextConfig === "function") {
|
|
570
|
+
return (phase, context) => {
|
|
571
|
+
const result = nextConfig(phase, context);
|
|
572
|
+
return result instanceof Promise ? result.then((c) => apply(c, options, phase)) : apply(result, options, phase);
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
return apply(nextConfig, options, void 0);
|
|
576
|
+
}
|
|
577
|
+
export {
|
|
578
|
+
nextArtifactSetIds,
|
|
579
|
+
nextUrlPrefix,
|
|
580
|
+
withArgus
|
|
581
|
+
};
|
|
582
|
+
//# sourceMappingURL=next.js.map
|