@arcote.tech/arc-cli 0.8.2 → 0.8.4

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/dist/index.js CHANGED
@@ -31692,6 +31692,52 @@ function workspaceSourcePlugin(srcByName) {
31692
31692
  }
31693
31693
  };
31694
31694
  }
31695
+ var SERVER_ONLY_NODE_BUILTINS = new Set([
31696
+ "crypto",
31697
+ "stream",
31698
+ "zlib",
31699
+ "fs",
31700
+ "fs/promises",
31701
+ "net",
31702
+ "tls",
31703
+ "http",
31704
+ "https",
31705
+ "http2",
31706
+ "dns",
31707
+ "dgram",
31708
+ "child_process",
31709
+ "worker_threads",
31710
+ "cluster",
31711
+ "os",
31712
+ "v8",
31713
+ "vm",
31714
+ "readline",
31715
+ "repl",
31716
+ "tty",
31717
+ "perf_hooks",
31718
+ "inspector",
31719
+ "async_hooks",
31720
+ "diagnostics_channel",
31721
+ "module",
31722
+ "constants"
31723
+ ]);
31724
+ function nodeServerBuiltinStubPlugin() {
31725
+ return {
31726
+ name: "node-server-builtin-stub",
31727
+ setup(build2) {
31728
+ build2.onResolve({ filter: /^(node:)?[a-z_/]+$/ }, (args) => {
31729
+ const bare = args.path.replace(/^node:/, "");
31730
+ if (!SERVER_ONLY_NODE_BUILTINS.has(bare))
31731
+ return;
31732
+ return { path: args.path, namespace: "node-server-stub" };
31733
+ });
31734
+ build2.onLoad({ filter: /.*/, namespace: "node-server-stub" }, (args) => ({
31735
+ contents: `const e=()=>{throw new Error("Node builtin '${args.path}' jest server-only \u2014 niedost\u0119pny w przegl\u0105darce");};` + `module.exports=new Proxy({},{get:(_,p)=>p==="__esModule"?false:e});`,
31736
+ loader: "js"
31737
+ }));
31738
+ }
31739
+ };
31740
+ }
31695
31741
  function jsxDevShimPlugin() {
31696
31742
  return {
31697
31743
  name: "jsx-dev-runtime-shim",
@@ -31813,7 +31859,10 @@ async function buildContextClient(pkg, rootDir, client, cache, noCache) {
31813
31859
  format: "esm",
31814
31860
  naming: "index.[ext]",
31815
31861
  external: externals,
31816
- plugins: [jsxDevShimPlugin()],
31862
+ plugins: [
31863
+ jsxDevShimPlugin(),
31864
+ ...client.target === "browser" ? [nodeServerBuiltinStubPlugin()] : []
31865
+ ],
31817
31866
  define: client.defines
31818
31867
  });
31819
31868
  if (!result.success) {
@@ -31926,7 +31975,6 @@ async function buildServerApp(rootDir, serverDir, packages, cache, noCache) {
31926
31975
  try {
31927
31976
  result = await Bun.build({
31928
31977
  entrypoints: [entrySrc],
31929
- outdir: serverDir,
31930
31978
  target: "bun",
31931
31979
  format: "esm",
31932
31980
  splitting: true,
@@ -31955,6 +32003,9 @@ async function buildServerApp(rootDir, serverDir, packages, cache, noCache) {
31955
32003
  if (basename3(entryOut.path) !== SERVER_ENTRY_FILE) {
31956
32004
  throw new Error(`Server app build: unexpected entry name ${basename3(entryOut.path)} (wanted ${SERVER_ENTRY_FILE})`);
31957
32005
  }
32006
+ for (const out of result.outputs) {
32007
+ writeFileSync7(join9(serverDir, basename3(out.path)), new Uint8Array(await out.arrayBuffer()));
32008
+ }
31958
32009
  const externals = [...recorded].filter(([name]) => !FRAMEWORK_PEER_SET.has(name)).map(([name, version]) => ({ name, version })).sort((a, b) => a.name.localeCompare(b.name));
31959
32010
  writeFileSync7(externalsFileAbs, JSON.stringify(externals, null, 2) + `
31960
32011
  `);
@@ -31972,9 +32023,141 @@ function readServerExternals(path4) {
31972
32023
  return [];
31973
32024
  }
31974
32025
  }
32026
+ function buildStatsEnabled() {
32027
+ return process.env.ARC_BUILD_STATS === "1";
32028
+ }
32029
+ function classifySource(src) {
32030
+ const nm = src.lastIndexOf("node_modules/");
32031
+ if (nm >= 0) {
32032
+ const rest = src.slice(nm + "node_modules/".length);
32033
+ const parts = rest.split("/");
32034
+ const pkg = parts[0].startsWith("@") ? `${parts[0]}/${parts[1]}` : parts[0];
32035
+ const external = !pkg.startsWith("@ndt/") && !pkg.startsWith("@arcote.tech/");
32036
+ return { pkg, external };
32037
+ }
32038
+ const m = src.match(/(?:^|\/)packages\/([^/]+)\//);
32039
+ if (m)
32040
+ return { pkg: `packages/${m[1]}`, external: false };
32041
+ return { pkg: src.split("/").slice(0, 2).join("/") || src, external: false };
32042
+ }
32043
+ function chunkPkgWeights(map) {
32044
+ const acc = new Map;
32045
+ const sources = map.sources ?? [];
32046
+ const contents = map.sourcesContent ?? [];
32047
+ for (let i = 0;i < sources.length; i++) {
32048
+ const bytes = contents[i]?.length ?? 0;
32049
+ const { pkg, external } = classifySource(sources[i]);
32050
+ const cur = acc.get(pkg) ?? { pkg, bytes: 0, external };
32051
+ cur.bytes += bytes;
32052
+ acc.set(pkg, cur);
32053
+ }
32054
+ return [...acc.values()].sort((a, b) => b.bytes - a.bytes);
32055
+ }
32056
+ var kb = (n) => `${Math.round(n / 1024)} KB`;
32057
+ function stripTrailingSourcemapComment(bytes) {
32058
+ let text = Buffer.from(bytes).toString("utf8");
32059
+ text = text.replace(/\n*\/\/# (?:debugId|sourceMappingURL)=[^\n]*\s*$/, "");
32060
+ if (!text.endsWith(`
32061
+ `))
32062
+ text += `
32063
+ `;
32064
+ return new Uint8Array(Buffer.from(text, "utf8"));
32065
+ }
32066
+ function reportBrowserBuildStats(opts) {
32067
+ const { label, entries, jsByName, mapsByJs } = opts;
32068
+ const staticEdge = /(?:^|[;}\s])import\s*["'](\.\/[^"']+\.js)["']|from\s*["'](\.\/[^"']+\.js)["']/g;
32069
+ const staticImports = (name) => {
32070
+ const bytes = jsByName.get(name);
32071
+ if (!bytes)
32072
+ return [];
32073
+ const s = Buffer.from(bytes).toString("utf8");
32074
+ const out = [];
32075
+ let m;
32076
+ staticEdge.lastIndex = 0;
32077
+ while (m = staticEdge.exec(s))
32078
+ out.push(basename3(m[1] ?? m[2]));
32079
+ return out;
32080
+ };
32081
+ const closure = (start) => {
32082
+ const seen = new Set;
32083
+ const stack = [start];
32084
+ while (stack.length) {
32085
+ const f = stack.pop();
32086
+ if (seen.has(f) || !jsByName.has(f))
32087
+ continue;
32088
+ seen.add(f);
32089
+ stack.push(...staticImports(f));
32090
+ }
32091
+ return seen;
32092
+ };
32093
+ const gz = (bytes) => Bun.gzipSync(bytes, { level: 6 }).length;
32094
+ console.log(`
32095
+ \u2501\u2501\u2501\u2501\u2501 Build stats: ${label} \u2501\u2501\u2501\u2501\u2501`);
32096
+ const globalExt = new Map;
32097
+ for (const entry of entries) {
32098
+ const files = closure(entry.file);
32099
+ let raw = 0;
32100
+ let gzip = 0;
32101
+ const pkgAcc = new Map;
32102
+ for (const f of files) {
32103
+ const bytes = jsByName.get(f);
32104
+ raw += bytes.length;
32105
+ gzip += gz(bytes);
32106
+ const map = mapsByJs.get(f);
32107
+ if (!map)
32108
+ continue;
32109
+ for (const w of chunkPkgWeights(map)) {
32110
+ const cur = pkgAcc.get(w.pkg) ?? { pkg: w.pkg, bytes: 0, external: w.external };
32111
+ cur.bytes += w.bytes;
32112
+ pkgAcc.set(w.pkg, cur);
32113
+ if (w.external) {
32114
+ const g = globalExt.get(w.pkg) ?? { bytes: 0, chunks: new Set, entries: new Set };
32115
+ g.bytes = Math.max(g.bytes, w.bytes);
32116
+ g.chunks.add(f);
32117
+ g.entries.add(entry.name);
32118
+ globalExt.set(w.pkg, g);
32119
+ }
32120
+ }
32121
+ }
32122
+ const top = [...pkgAcc.values()].sort((a, b) => b.bytes - a.bytes).slice(0, 10);
32123
+ console.log(`
32124
+ \u25B8 ${entry.name}: pierwsze wczytanie ${kb(raw)} raw / ${kb(gzip)} gzip (${files.size} plik\xF3w)`);
32125
+ for (const w of top) {
32126
+ const tag = w.external ? "ext" : "wewn";
32127
+ console.log(` ${kb(w.bytes).padStart(8)} [${tag}] ${w.pkg}`);
32128
+ }
32129
+ }
32130
+ const heaviest = [...globalExt.entries()].sort((a, b) => b[1].bytes - a[1].bytes).slice(0, 15);
32131
+ if (heaviest.length) {
32132
+ console.log(`
32133
+ \u25B8 Najci\u0119\u017Csze biblioteki ZEWN\u0118TRZNE (\u017Ar\xF3d\u0142a) i gdzie l\u0105duj\u0105:`);
32134
+ for (const [pkg, g] of heaviest) {
32135
+ console.log(` ${kb(g.bytes).padStart(8)} ${pkg} \u2192 ${[...g.entries].join(", ")}`);
32136
+ }
32137
+ }
32138
+ console.log(`
32139
+ \u2501\u2501\u2501\u2501\u2501 koniec stats: ${label} \u2501\u2501\u2501\u2501\u2501
32140
+ `);
32141
+ }
32142
+ function looksMinified(files) {
32143
+ for (const f of files) {
32144
+ if (!f.name.endsWith(".js") || f.bytes.length < 16 * 1024)
32145
+ continue;
32146
+ const sample = f.bytes.subarray(0, 64 * 1024);
32147
+ let newlines = 0;
32148
+ for (const b of sample)
32149
+ if (b === 10)
32150
+ newlines++;
32151
+ const avgLineLen = sample.length / Math.max(1, newlines);
32152
+ if (avgLineLen < 200)
32153
+ return false;
32154
+ }
32155
+ return true;
32156
+ }
31975
32157
  async function buildBrowserApp(rootDir, outDir, plan, cache, noCache, i18nCollector, opts) {
31976
32158
  const federated = opts?.federated ?? false;
31977
32159
  const exposeRuntime = opts?.exposeRuntime ?? false;
32160
+ const minify = opts?.minify ?? true;
31978
32161
  mkdirSync7(outDir, { recursive: true });
31979
32162
  const publicMembers = plan.groups.get("public") ?? [];
31980
32163
  const protectedGroups = plan.chunks.filter((c) => c !== "public").map((c) => ({ name: c, members: plan.groups.get(c) ?? [] })).filter((g) => g.members.length > 0);
@@ -31997,10 +32180,11 @@ async function buildBrowserApp(rootDir, outDir, plan, cache, noCache, i18nCollec
31997
32180
  define: { ONLY_SERVER: "false", ONLY_BROWSER: "true", ONLY_CLIENT: "true" },
31998
32181
  federated,
31999
32182
  exposeRuntime,
32183
+ minify,
32000
32184
  externals: federated ? [...SHELL_EXTERNALS] : [],
32001
32185
  builderRev: 3
32002
32186
  });
32003
- if (!noCache && isCacheHit(cache, unitId, inputHash)) {
32187
+ if (!noCache && !buildStatsEnabled() && isCacheHit(cache, unitId, inputHash)) {
32004
32188
  const cached = cache.units[unitId]?.outputHashes;
32005
32189
  if (cached?._manifest) {
32006
32190
  try {
@@ -32024,24 +32208,27 @@ async function buildBrowserApp(rootDir, outDir, plan, cache, noCache, i18nCollec
32024
32208
  rmSync3(join9(outDir, f), { force: true });
32025
32209
  }
32026
32210
  }
32027
- const tmpDir = join9(outDir, "_entries");
32028
- mkdirSync7(tmpDir, { recursive: true });
32211
+ const tmpDir = join9(rootDir, ".arc", "tmp", `entries-${unitId}`);
32029
32212
  const importLines = (pkgs) => pkgs.map((m) => `import "${m.pkg.name}";`).join(`
32030
32213
  `);
32031
32214
  const initialEntry = join9(tmpDir, "initial.ts");
32032
- writeFileSync7(initialEntry, (exposeRuntime ? runtimeExposePrelude() + `
32033
- ` : "") + `${importLines(publicMembers)}
32034
- export { startApp } from "@arcote.tech/platform";
32035
- `);
32036
32215
  const entryPaths = [initialEntry];
32037
32216
  const groupModuleMap = new Map;
32038
32217
  for (const g of protectedGroups) {
32039
- const entry = join9(tmpDir, `${g.name}.ts`);
32040
- writeFileSync7(entry, `${importLines(g.members)}
32041
- `);
32042
- entryPaths.push(entry);
32218
+ entryPaths.push(join9(tmpDir, `${g.name}.ts`));
32043
32219
  groupModuleMap.set(g.name, g.members.map((m) => m.moduleName));
32044
32220
  }
32221
+ const writeEntryFiles = () => {
32222
+ mkdirSync7(tmpDir, { recursive: true });
32223
+ writeFileSync7(initialEntry, (exposeRuntime ? runtimeExposePrelude() + `
32224
+ ` : "") + `${importLines(publicMembers)}
32225
+ export { startApp } from "@arcote.tech/platform";
32226
+ `);
32227
+ for (const g of protectedGroups) {
32228
+ writeFileSync7(join9(tmpDir, `${g.name}.ts`), `${importLines(g.members)}
32229
+ `);
32230
+ }
32231
+ };
32045
32232
  const allMemberPkgs = new Map;
32046
32233
  for (const m of publicMembers)
32047
32234
  allMemberPkgs.set(m.pkg.name, m.pkg);
@@ -32062,11 +32249,13 @@ export { startApp } from "@arcote.tech/platform";
32062
32249
  `);
32063
32250
  patchedPkgJsons.push({ path: pkgJsonPath, original });
32064
32251
  }
32252
+ const statsMode = buildStatsEnabled();
32253
+ const mapsByJs = new Map;
32065
32254
  let result;
32066
- try {
32067
- result = await Bun.build({
32255
+ const runBuild = () => {
32256
+ writeEntryFiles();
32257
+ return Bun.build({
32068
32258
  entrypoints: entryPaths,
32069
- outdir: outDir,
32070
32259
  splitting: true,
32071
32260
  format: "esm",
32072
32261
  target: "browser",
@@ -32074,9 +32263,12 @@ export { startApp } from "@arcote.tech/platform";
32074
32263
  plugins: [
32075
32264
  ...federated ? [] : [singleReactPlugin(rootDir)],
32076
32265
  jsxDevShimPlugin(),
32266
+ nodeServerBuiltinStubPlugin(),
32077
32267
  i18nExtractPlugin(i18nCollector, rootDir)
32078
32268
  ],
32079
32269
  naming: "[name].[ext]",
32270
+ minify,
32271
+ sourcemap: statsMode ? "external" : "none",
32080
32272
  define: {
32081
32273
  ONLY_SERVER: "false",
32082
32274
  ONLY_BROWSER: "true",
@@ -32084,12 +32276,46 @@ export { startApp } from "@arcote.tech/platform";
32084
32276
  "process.env.NODE_ENV": '"production"'
32085
32277
  }
32086
32278
  });
32279
+ };
32280
+ let outFiles = null;
32281
+ try {
32282
+ for (let attempt = 1;; attempt++) {
32283
+ result = await runBuild();
32284
+ if (result.success) {
32285
+ const files = [];
32286
+ mapsByJs.clear();
32287
+ for (const out of result.outputs) {
32288
+ const name = basename3(out.path);
32289
+ if (name.endsWith(".map")) {
32290
+ try {
32291
+ mapsByJs.set(name.replace(/\.map$/, ""), JSON.parse(await out.text()));
32292
+ } catch {}
32293
+ continue;
32294
+ }
32295
+ let bytes = new Uint8Array(await out.arrayBuffer());
32296
+ if (statsMode && name.endsWith(".js")) {
32297
+ bytes = stripTrailingSourcemapComment(bytes);
32298
+ }
32299
+ files.push({ kind: out.kind, name, bytes });
32300
+ }
32301
+ if (!minify || looksMinified(files)) {
32302
+ outFiles = files;
32303
+ break;
32304
+ }
32305
+ }
32306
+ if (attempt >= 3) {
32307
+ if (!result.success)
32308
+ break;
32309
+ throw new Error("Browser app build: output NIEZMINIFIKOWANY mimo minify:true (3 pr\xF3by). Przerwane, \u017Ceby nie wypu\u015Bci\u0107 ci\u0119\u017Ckich bundli.");
32310
+ }
32311
+ console.warn(` ! browser build pass ${attempt} ${result.success ? "niezminifikowany mimo minify:true (wy\u015Bcig minify Bun.build)" : "nieudany (wy\u015Bcig Bun.build \u2014 np. znikni\u0119te pliki entry)"} \u2014 powtarzam`);
32312
+ }
32087
32313
  } finally {
32088
32314
  for (const p of patchedPkgJsons)
32089
32315
  writeFileSync7(p.path, p.original);
32090
32316
  }
32091
32317
  rmSync3(tmpDir, { recursive: true, force: true });
32092
- if (!result.success) {
32318
+ if (!result.success || !outFiles) {
32093
32319
  for (const log2 of result.logs)
32094
32320
  console.error(log2);
32095
32321
  throw new Error("Browser app build failed");
@@ -32098,17 +32324,12 @@ export { startApp } from "@arcote.tech/platform";
32098
32324
  let initialHash = "";
32099
32325
  const groups = {};
32100
32326
  const sharedChunks = [];
32101
- for (const out of result.outputs) {
32102
- const name = basename3(out.path);
32327
+ for (const out of outFiles) {
32103
32328
  if (out.kind === "entry-point") {
32104
- const bytes = readFileSync8(out.path);
32105
- const hash = sha256Hex(bytes).slice(0, 16);
32106
- const stem = name.replace(/\.js$/, "");
32329
+ const hash = sha256Hex(out.bytes).slice(0, 16);
32330
+ const stem = out.name.replace(/\.js$/, "");
32107
32331
  const finalName = `${stem}.${hash}.js`;
32108
- const finalPath = join9(outDir, finalName);
32109
- rmSync3(finalPath, { force: true });
32110
- writeFileSync7(finalPath, bytes);
32111
- rmSync3(out.path, { force: true });
32332
+ writeFileSync7(join9(outDir, finalName), out.bytes);
32112
32333
  if (stem === "initial") {
32113
32334
  initialFile = finalName;
32114
32335
  initialHash = hash;
@@ -32120,18 +32341,56 @@ export { startApp } from "@arcote.tech/platform";
32120
32341
  };
32121
32342
  }
32122
32343
  } else if (out.kind === "chunk") {
32123
- sharedChunks.push(name);
32344
+ writeFileSync7(join9(outDir, out.name), out.bytes);
32345
+ sharedChunks.push(out.name);
32124
32346
  }
32125
32347
  }
32126
32348
  if (!initialFile) {
32127
32349
  throw new Error("Browser app build: initial entry not found in outputs");
32128
32350
  }
32351
+ const written = new Map;
32352
+ for (const out of outFiles) {
32353
+ const name = out.kind === "entry-point" ? `${out.name.replace(/\.js$/, "")}.${sha256Hex(out.bytes).slice(0, 16)}.js` : out.name;
32354
+ if (out.kind === "entry-point" || out.kind === "chunk") {
32355
+ written.set(name, sha256Hex(out.bytes));
32356
+ }
32357
+ }
32358
+ for (const f of readdirSync4(outDir)) {
32359
+ if (!f.endsWith(".js"))
32360
+ continue;
32361
+ const expected = written.get(f);
32362
+ const actual = sha256Hex(readFileSync8(join9(outDir, f)));
32363
+ if (!expected || expected !== actual) {
32364
+ throw new Error(`Browser app build: plik "${f}" w ${outDir} nie zgadza si\u0119 z artefaktami builda ` + `(${expected ? "tre\u015B\u0107 rozjechana z zapisem" : "obcy plik \u2014 co\u015B innego pisze do outDir"}). ` + `Przerwane, \u017Ceby nie wypu\u015Bci\u0107 niesp\xF3jnego builda.`);
32365
+ }
32366
+ }
32129
32367
  const manifest = {
32130
32368
  initial: { file: initialFile, hash: initialHash },
32131
32369
  groups,
32132
32370
  sharedChunks,
32133
32371
  cached: false
32134
32372
  };
32373
+ if (statsMode) {
32374
+ const jsByName = new Map;
32375
+ const mapsByFinal = new Map;
32376
+ const finalNameOf = (out) => out.kind === "entry-point" ? `${out.name.replace(/\.js$/, "")}.${sha256Hex(out.bytes).slice(0, 16)}.js` : out.name;
32377
+ for (const out of outFiles) {
32378
+ const fn = finalNameOf(out);
32379
+ jsByName.set(fn, out.bytes);
32380
+ const map = mapsByJs.get(out.name);
32381
+ if (map)
32382
+ mapsByFinal.set(fn, map);
32383
+ }
32384
+ reportBrowserBuildStats({
32385
+ label: federated ? "browser-fed" : "browser",
32386
+ entries: [
32387
+ { name: "initial", file: initialFile },
32388
+ ...Object.entries(groups).map(([stem, g]) => ({ name: stem, file: g.file }))
32389
+ ],
32390
+ jsByName,
32391
+ mapsByJs: mapsByFinal
32392
+ });
32393
+ }
32135
32394
  updateCache(cache, unitId, inputHash, {
32136
32395
  outputHashes: { _manifest: JSON.stringify(manifest) }
32137
32396
  });
@@ -32433,6 +32692,11 @@ function collectModuleNames(dir, acc) {
32433
32692
  }
32434
32693
  }
32435
32694
  }
32695
+ function packageDeclaresModule(pkg) {
32696
+ const names = new Set;
32697
+ collectModuleNames(join11(pkg.path, "src"), names);
32698
+ return names.size > 0;
32699
+ }
32436
32700
  function assertOneModulePerPackage(packages) {
32437
32701
  const offenders = [];
32438
32702
  for (const pkg of packages) {
@@ -32623,6 +32887,7 @@ function resolveWorkspace() {
32623
32887
  async function buildAll(ws, opts = {}) {
32624
32888
  const cache = loadBuildCache(ws.arcDir);
32625
32889
  const noCache = opts.noCache ?? false;
32890
+ const minify = opts.minify ?? true;
32626
32891
  const themePath = ws.rootPkg.arc?.theme;
32627
32892
  const federation = getFederationConfig(ws);
32628
32893
  log2(`Building (concurrency parallel${noCache ? ", no-cache" : ""})...`);
@@ -32637,8 +32902,9 @@ async function buildAll(ws, opts = {}) {
32637
32902
  const isHost = Boolean(federation?.host);
32638
32903
  const moduleNameOf2 = (name) => name.split("/").pop() ?? name;
32639
32904
  const exposureOf = (pkgName) => accessMap.exposure[moduleNameOf2(pkgName)]?.exposure ?? "local";
32640
- const localPackages = isHost ? ws.packages : ws.packages.filter((p) => exposureOf(p.name) !== "external");
32641
- const fedPackages = ws.packages.filter((p) => ["external", "both"].includes(exposureOf(p.name)));
32905
+ const modulePackages = ws.packages.filter(packageDeclaresModule);
32906
+ const localPackages = isHost ? modulePackages : modulePackages.filter((p) => exposureOf(p.name) !== "external");
32907
+ const fedPackages = modulePackages.filter((p) => ["external", "both"].includes(exposureOf(p.name)));
32642
32908
  const hasFederatedModules = fedPackages.length > 0;
32643
32909
  const needsPeerShell = isHost || hasFederatedModules;
32644
32910
  log2();
@@ -32649,16 +32915,17 @@ async function buildAll(ws, opts = {}) {
32649
32915
  ok(`Federated modules: ${fedPackages.map((p) => moduleNameOf2(p.name)).join(", ")}`);
32650
32916
  }
32651
32917
  const i18nCollector = new Map;
32652
- const [browserResult, , , , shellResult, fedResult] = await Promise.all([
32653
- buildBrowserApp(ws.rootDir, ws.browserDir, plan, cache, noCache, i18nCollector, {
32654
- federated: false,
32655
- exposeRuntime: isHost
32656
- }),
32918
+ const browserSequence = (async () => {
32919
+ const bundled = await buildBrowserApp(ws.rootDir, ws.browserDir, plan, cache, noCache, i18nCollector, { federated: false, exposeRuntime: isHost, minify });
32920
+ const fed = fedPlan ? await buildBrowserApp(ws.rootDir, join13(ws.arcDir, "browser-fed"), fedPlan, cache, noCache, i18nCollector, { federated: true, unitId: "browser-app-fed", minify }) : undefined;
32921
+ return { bundled, fed };
32922
+ })();
32923
+ const [{ bundled: browserResult, fed: fedResult }, , , , shellResult] = await Promise.all([
32924
+ browserSequence,
32657
32925
  buildStyles(ws.rootDir, ws.arcDir, ws.packages, themePath, cache, noCache),
32658
32926
  copyBrowserAssets(ws, cache, noCache),
32659
32927
  buildTranslations(ws.rootDir, ws.arcDir, cache, noCache),
32660
- needsPeerShell ? buildPeerShell(ws.rootDir, join13(ws.arcDir, "shell"), cache, noCache) : Promise.resolve(undefined),
32661
- fedPlan ? buildBrowserApp(ws.rootDir, join13(ws.arcDir, "browser-fed"), fedPlan, cache, noCache, i18nCollector, { federated: true, unitId: "browser-app-fed" }) : Promise.resolve(undefined)
32928
+ needsPeerShell ? buildPeerShell(ws.rootDir, join13(ws.arcDir, "shell"), cache, noCache) : Promise.resolve(undefined)
32662
32929
  ]);
32663
32930
  const { finalizeTranslations: finalizeTranslations3 } = await Promise.resolve().then(() => (init_i18n(), exports_i18n));
32664
32931
  await finalizeTranslations3(ws.rootDir, ws.arcDir, i18nCollector);
@@ -32806,20 +33073,25 @@ async function loadServerContext(ws) {
32806
33073
  if (!existsSync11(serverEntry)) {
32807
33074
  return { context: null, moduleAccess: new Map };
32808
33075
  }
33076
+ let serverError;
32809
33077
  try {
32810
33078
  await import(serverEntry);
32811
33079
  } catch (e) {
32812
33080
  err(`Failed to load server bundle ${SERVER_ENTRY_FILE}: ${e}`);
33081
+ serverError = e;
32813
33082
  }
32814
33083
  const { getContext, getAllModuleAccess } = await import(platformEntry);
32815
33084
  return {
32816
33085
  context: getContext() ?? null,
32817
- moduleAccess: getAllModuleAccess()
33086
+ moduleAccess: getAllModuleAccess(),
33087
+ serverError
32818
33088
  };
32819
33089
  }
32820
33090
 
32821
33091
  // src/commands/platform-build.ts
32822
33092
  async function platformBuild(opts = {}) {
33093
+ if (opts.stats)
33094
+ process.env.ARC_BUILD_STATS = "1";
32823
33095
  const ws = resolveWorkspace();
32824
33096
  const manifest = await buildAll(ws, { noCache: opts.noCache });
32825
33097
  const groupCount = Object.keys(manifest.groups).length;
@@ -33247,6 +33519,7 @@ function generateCaddyfile(cfg) {
33247
33519
  for (const [name, env2] of Object.entries(cfg.envs)) {
33248
33520
  lines.push(`${env2.domain} {${tlsDirective}`);
33249
33521
  lines.push(...logDirective);
33522
+ lines.push(" encode zstd gzip");
33250
33523
  if (observability) {
33251
33524
  lines.push(" handle_path /otel/* {");
33252
33525
  lines.push(" reverse_proxy otel-collector:4318");
@@ -35435,7 +35708,8 @@ async function bootstrap(inputs) {
35435
35708
  });
35436
35709
  ok("Host bootstrapped");
35437
35710
  }
35438
- const needUpStack = state.kind !== "ready" || state.marker === null || state.marker.configHash !== inputs.configHash || state.marker.cliVersion !== inputs.cliVersion || !await isRegistryRunning(cfg);
35711
+ const artifactsHash = new Bun.CryptoHasher("sha256").update(generateCaddyfile(cfg)).update(generateCompose({ cfg })).digest("hex");
35712
+ const needUpStack = state.kind !== "ready" || state.marker === null || state.marker.configHash !== inputs.configHash || state.marker.cliVersion !== inputs.cliVersion || state.marker.artifactsHash !== artifactsHash || inputs.forceAnsible === true || !await isRegistryRunning(cfg);
35439
35713
  if (needUpStack) {
35440
35714
  await upStack(inputs);
35441
35715
  ok("Docker stack up");
@@ -35449,6 +35723,7 @@ async function bootstrap(inputs) {
35449
35723
  await writeStateMarker(cfg.target, {
35450
35724
  cliVersion: inputs.cliVersion,
35451
35725
  configHash: inputs.configHash,
35726
+ artifactsHash,
35452
35727
  updatedAt: new Date().toISOString()
35453
35728
  });
35454
35729
  }
@@ -39025,7 +39300,7 @@ async function startPlatform(opts) {
39025
39300
  const dbPath = opts.dbPath ?? join23(ws.rootDir, ".arc", "data", devMode ? "dev.db" : "prod.db");
39026
39301
  let manifest;
39027
39302
  if (devMode) {
39028
- manifest = await buildAll(ws);
39303
+ manifest = await buildAll(ws, { minify: false });
39029
39304
  } else {
39030
39305
  const manifestPath = join23(ws.arcDir, "manifest.json");
39031
39306
  if (!existsSync20(manifestPath)) {
@@ -39041,7 +39316,11 @@ async function startPlatform(opts) {
39041
39316
  process.env.ARC_APP_SECRET = resolveAppSecret(ws.rootDir);
39042
39317
  }
39043
39318
  log2("Loading server context...");
39044
- const { context: context2, moduleAccess } = await loadServerContext(ws);
39319
+ const { context: context2, moduleAccess, serverError } = await loadServerContext(ws);
39320
+ if (serverError) {
39321
+ err("Serwer NIE wstanie: modu\u0142 serwerowy nie za\u0142adowa\u0142 si\u0119 (b\u0142\u0105d wy\u017Cej). " + "Cz\u0119\u015Bciowy kontekst = po\u0142owiczna aplikacja (query rzucaj\u0105 " + "'Element not found'). Napraw przyczyn\u0119 (np. brakuj\u0105ce zmienne " + "\u015Brodowiskowe) i wystartuj ponownie.");
39322
+ process.exit(1);
39323
+ }
39045
39324
  if (context2) {
39046
39325
  ok("Context loaded");
39047
39326
  } else {
@@ -39207,7 +39486,7 @@ function attachDevWatcher(ws, platform3, onReload) {
39207
39486
  isRebuilding = true;
39208
39487
  log2("Rebuilding...");
39209
39488
  try {
39210
- const next = await buildAll(ws);
39489
+ const next = await buildAll(ws, { minify: false });
39211
39490
  platform3.setManifest(next);
39212
39491
  platform3.notifyReload(next);
39213
39492
  onReload?.();
@@ -39299,7 +39578,7 @@ platform3.command("dev").description("Start platform in dev mode (Bun server + V
39299
39578
  map: opts.map,
39300
39579
  headless: opts.headless
39301
39580
  }));
39302
- platform3.command("build").description("Build platform for production").option("--no-cache", "Force full rebuild").action((opts) => platformBuild({ noCache: opts.cache === false }));
39581
+ platform3.command("build").description("Build platform for production").option("--no-cache", "Force full rebuild").option("--stats", "Wypisz rozbicie chunk\xF3w na biblioteki (per-wej\u015Bcie raw/gzip + najci\u0119\u017Csze liby, ext/wewn). Wymusza pe\u0142ny build.").action((opts) => platformBuild({ noCache: opts.cache === false, stats: opts.stats }));
39303
39582
  platform3.command("start").description("Start platform in production mode (requires prior build)").action(platformStart);
39304
39583
  platform3.command("pairing-token").description("Generate a one-time federation pairing code (writes to DB, prints it)").action(platformPairingToken);
39305
39584
  platform3.command("deploy [env]").description("Deploy platform to a remote server (reads deploy.arc.json, surveys if missing)").option("--skip-build", "Skip local build step").option("--rebuild", "Force rebuild before deploy").option("--build-only", "Build the Docker image locally, then exit (no remote push)").option("--image-tag <hash>", "Roll back / pin to an existing image tag instead of building a new one").option("--force-bootstrap", "Re-run Ansible host bootstrap even if the server is already configured").action((env2, opts) => platformDeploy(env2, opts));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc-cli",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "description": "CLI tool for Arc framework",
5
5
  "module": "index.ts",
6
6
  "main": "dist/index.js",
@@ -12,13 +12,13 @@
12
12
  "build": "bun build --target=bun ./src/index.ts --outdir=dist --external @arcote.tech/arc --external @arcote.tech/arc-ds --external @arcote.tech/arc-react --external @arcote.tech/platform --external @arcote.tech/arc-map --external '@opentelemetry/*' && chmod +x dist/index.js"
13
13
  },
14
14
  "dependencies": {
15
- "@arcote.tech/arc": "^0.8.2",
16
- "@arcote.tech/arc-ds": "^0.8.2",
17
- "@arcote.tech/arc-react": "^0.8.2",
18
- "@arcote.tech/arc-host": "^0.8.2",
19
- "@arcote.tech/arc-adapter-db-sqlite": "^0.8.2",
20
- "@arcote.tech/arc-adapter-db-postgres": "^0.8.2",
21
- "@arcote.tech/arc-otel": "^0.8.2",
15
+ "@arcote.tech/arc": "^0.8.4",
16
+ "@arcote.tech/arc-ds": "^0.8.4",
17
+ "@arcote.tech/arc-react": "^0.8.4",
18
+ "@arcote.tech/arc-host": "^0.8.4",
19
+ "@arcote.tech/arc-adapter-db-sqlite": "^0.8.4",
20
+ "@arcote.tech/arc-adapter-db-postgres": "^0.8.4",
21
+ "@arcote.tech/arc-otel": "^0.8.4",
22
22
  "@opentelemetry/api": "^1.9.0",
23
23
  "@opentelemetry/api-logs": "^0.57.0",
24
24
  "@opentelemetry/core": "^1.30.0",
@@ -31,8 +31,8 @@
31
31
  "@opentelemetry/sdk-trace-base": "^1.30.0",
32
32
  "@opentelemetry/sdk-trace-node": "^1.30.0",
33
33
  "@opentelemetry/semantic-conventions": "^1.27.0",
34
- "@arcote.tech/platform": "^0.8.2",
35
- "@arcote.tech/arc-map": "^0.8.2",
34
+ "@arcote.tech/platform": "^0.8.4",
35
+ "@arcote.tech/arc-map": "^0.8.4",
36
36
  "@clack/prompts": "^0.9.0",
37
37
  "commander": "^11.1.0",
38
38
  "chokidar": "^3.5.3",
@@ -120,6 +120,20 @@ function collectModuleNames(dir: string, acc: Set<string>): void {
120
120
  }
121
121
  }
122
122
 
123
+ /**
124
+ * Czy pakiet DEKLARUJE moduł Arc (`module("...")` w źródłach). Tylko takie
125
+ * pakiety są „członkami" wchodzącymi do initial/grup — czysta biblioteka bez
126
+ * modułu (np. pakiet z szablonami maili) NIE może być side-effect-importowana
127
+ * przez initial, bo z `sideEffects:true` wciągałaby całe swoje drzewo
128
+ * zależności do pierwszego wczytania (patrz: @react-email w NDT — 1,4 MB
129
+ * stosu maili w bundlu klienta przez samo `import "@ndt/email"`).
130
+ */
131
+ export function packageDeclaresModule(pkg: WorkspacePackage): boolean {
132
+ const names = new Set<string>();
133
+ collectModuleNames(join(pkg.path, "src"), names);
134
+ return names.size > 0;
135
+ }
136
+
123
137
  /**
124
138
  * Assert every workspace package declares at most one `module()`. Throws a
125
139
  * build error listing offenders, with the fix (split each extra module into