vitral 0.4.0 → 0.6.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/dist/angular.d.ts +2 -2
- package/dist/angular.js +5 -5
- package/dist/auto.js +5 -5
- package/dist/chunk-B2MPYF4M.js +27 -0
- package/dist/chunk-DAESKNQ6.js +207 -0
- package/dist/{chunk-WD4CDGDZ.js → chunk-HIVMSPGM.js} +12 -11
- package/dist/{chunk-IH3ZT3QU.js → chunk-JBNITHPI.js} +1 -1
- package/dist/{chunk-6EX5KYT5.js → chunk-KU6HF2EW.js} +2 -2
- package/dist/chunk-NZEQGRBL.js +93 -0
- package/dist/chunk-OVVGVPIV.js +21 -0
- package/dist/{chunk-BCC25ANK.js → chunk-PLM7U7LR.js} +10 -6
- package/dist/chunk-PY7P5BF4.js +404 -0
- package/dist/{chunk-EMSB2NVX.js → chunk-S7PIVPBX.js} +10 -4
- package/dist/{chunk-W6322KGP.js → chunk-UXLKRSOM.js} +2 -2
- package/dist/cli.js +414 -44
- package/dist/dict.d.ts +69 -0
- package/dist/dict.js +8 -0
- package/dist/edge.d.ts +124 -0
- package/dist/edge.js +413 -0
- package/dist/element.d.ts +2 -2
- package/dist/element.js +5 -5
- package/dist/embed.js +2 -2
- package/dist/{events-Dkpkw_Sv.d.ts → events-0rxFlWJJ.d.ts} +1 -1
- package/dist/{expose-OVWTPHH5.js → expose-EQOWTJNC.js} +3 -3
- package/dist/federation.d.ts +1 -1
- package/dist/hydrate-R76V2I36.js +182 -0
- package/dist/iframe.js +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/match-CMBM_jLW.d.ts +16 -0
- package/dist/otel.d.ts +29 -1
- package/dist/otel.js +13 -1
- package/dist/preact.d.ts +4 -4
- package/dist/preact.js +4 -4
- package/dist/prefetch.d.ts +25 -0
- package/dist/prefetch.js +178 -0
- package/dist/react-server.d.ts +20 -0
- package/dist/react-server.js +13 -0
- package/dist/react.d.ts +7 -4
- package/dist/react.js +19 -31
- package/dist/reframe-MUV472OU.js +251 -0
- package/dist/routes.d.ts +4 -13
- package/dist/routes.js +27 -23
- package/dist/{shadow-PVSETNMZ.js → shadow-S4SKHTA7.js} +2 -2
- package/dist/single-spa.d.ts +1 -1
- package/dist/{slot-UPg6ob9v.d.ts → slot-D49L1rdU.d.ts} +1 -1
- package/dist/solid.d.ts +3 -3
- package/dist/solid.js +4 -4
- package/dist/{sri-M7ZRU57G.js → sri-576GTDIA.js} +2 -1
- package/dist/svelte.d.ts +4 -4
- package/dist/svelte.js +4 -4
- package/dist/testing.d.ts +171 -3
- package/dist/testing.js +191 -4
- package/dist/{types-CA2p9KHC.d.ts → types-DUtcF3HO.d.ts} +12 -1
- package/dist/vitals.d.ts +29 -0
- package/dist/vitals.js +152 -0
- package/dist/vite.js +28 -422
- package/dist/vue.d.ts +2 -2
- package/dist/vue.js +4 -4
- package/package.json +111 -16
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
verify
|
|
4
|
+
} from "./chunk-DAESKNQ6.js";
|
|
5
|
+
import {
|
|
6
|
+
loadTs
|
|
7
|
+
} from "./chunk-PY7P5BF4.js";
|
|
8
|
+
import {
|
|
9
|
+
dictMatch
|
|
10
|
+
} from "./chunk-NZEQGRBL.js";
|
|
2
11
|
|
|
3
12
|
// src/cli.ts
|
|
4
|
-
import { createRequire } from "module";
|
|
13
|
+
import { createRequire as createRequire2 } from "module";
|
|
5
14
|
|
|
6
15
|
// src/cli/canal.ts
|
|
7
16
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -420,10 +429,100 @@ function serveMcp(input = process.stdin, output = process.stdout, options = {})
|
|
|
420
429
|
return new Promise((done) => lines.on("close", () => Promise.all(pending).then(() => done())));
|
|
421
430
|
}
|
|
422
431
|
|
|
432
|
+
// src/cli/dict.ts
|
|
433
|
+
import { execFileSync } from "child_process";
|
|
434
|
+
import { createHash, randomBytes } from "crypto";
|
|
435
|
+
import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync4, statSync, writeFileSync as writeFileSync4 } from "fs";
|
|
436
|
+
import { join, relative, sep } from "path";
|
|
437
|
+
import * as zlib from "zlib";
|
|
438
|
+
var INDEX = "vitral-dict.json";
|
|
439
|
+
var TYPES = { ".js": "text/javascript", ".mjs": "text/javascript", ".css": "text/css" };
|
|
440
|
+
var DCZ_MAGIC = Buffer.from([94, 42, 77, 24, 32, 0, 0, 0]);
|
|
441
|
+
var DCB_MAGIC = Buffer.from([255, 68, 67, 66]);
|
|
442
|
+
function files(dist) {
|
|
443
|
+
const out = /* @__PURE__ */ new Map();
|
|
444
|
+
const walk = (dir) => {
|
|
445
|
+
for (const name of readdirSync(dir).sort()) {
|
|
446
|
+
const full = join(dir, name);
|
|
447
|
+
if (statSync(full).isDirectory()) walk(full);
|
|
448
|
+
else if (TYPES[ext(name)]) out.set(relative(dist, full).split(sep).join("/"), full);
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
walk(dist);
|
|
452
|
+
return out;
|
|
453
|
+
}
|
|
454
|
+
var ext = (name) => name.slice(name.lastIndexOf("."));
|
|
455
|
+
function nodeZstd() {
|
|
456
|
+
const z = zlib.zstdCompressSync;
|
|
457
|
+
if (!z) return void 0;
|
|
458
|
+
const sample = randomBytes(256);
|
|
459
|
+
return z(sample, { dictionary: sample }).length < 64 ? z : void 0;
|
|
460
|
+
}
|
|
461
|
+
function brotliBin() {
|
|
462
|
+
try {
|
|
463
|
+
execFileSync("brotli", ["--version"], { stdio: "ignore" });
|
|
464
|
+
} catch {
|
|
465
|
+
return void 0;
|
|
466
|
+
}
|
|
467
|
+
return (input, dictionary) => execFileSync("brotli", ["-q", "11", "-c", "-D", dictionary, input], { maxBuffer: 1 << 30 });
|
|
468
|
+
}
|
|
469
|
+
var kb = (n) => (n / 1024).toFixed(1).padStart(7) + " kB";
|
|
470
|
+
function dict(o) {
|
|
471
|
+
const log = o.log ?? console.log;
|
|
472
|
+
const zstd = nodeZstd();
|
|
473
|
+
if (!zstd) throw new Error(`[vitral] dict precisa do zstd com dicion\xE1rio do Node (22.19+ ou 24.6+). Este \xE9 ${process.version}.`);
|
|
474
|
+
const brotli = o.brotli === false ? void 0 : o.brotli ?? brotliBin();
|
|
475
|
+
const level = o.level ?? 19;
|
|
476
|
+
if (!existsSync4(o.dist)) throw new Error(`[vitral] ${o.dist} n\xE3o existe`);
|
|
477
|
+
const novos = files(o.dist);
|
|
478
|
+
const antigos = /* @__PURE__ */ new Map();
|
|
479
|
+
for (const from of o.from) {
|
|
480
|
+
if (!existsSync4(from)) throw new Error(`[vitral] ${from} n\xE3o existe`);
|
|
481
|
+
for (const [rel2, full] of files(from)) {
|
|
482
|
+
const key = dictMatch("/" + rel2);
|
|
483
|
+
antigos.set(key, [...antigos.get(key) ?? [], full]);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const index = { version: 1, files: {} };
|
|
487
|
+
const linhas = [];
|
|
488
|
+
for (const [rel2, full] of novos) {
|
|
489
|
+
const deltas = {};
|
|
490
|
+
index.files[rel2] = { type: TYPES[ext(rel2)], deltas };
|
|
491
|
+
const input = readFileSync4(full);
|
|
492
|
+
const vistos = /* @__PURE__ */ new Set();
|
|
493
|
+
for (const old of antigos.get(dictMatch("/" + rel2)) ?? []) {
|
|
494
|
+
const dictionary = readFileSync4(old);
|
|
495
|
+
const sha = createHash("sha256").update(dictionary).digest();
|
|
496
|
+
const hash = `:${sha.toString("base64")}:`;
|
|
497
|
+
if (vistos.has(hash) || dictionary.equals(input)) continue;
|
|
498
|
+
vistos.add(hash);
|
|
499
|
+
const id = sha.toString("hex").slice(0, 12);
|
|
500
|
+
const variant = {};
|
|
501
|
+
const dcz = Buffer.concat([DCZ_MAGIC, sha, zstd(input, { dictionary, params: { [zlib.constants.ZSTD_c_compressionLevel]: level } })]);
|
|
502
|
+
writeFileSync4(join(o.dist, `${rel2}.${id}.dcz`), dcz);
|
|
503
|
+
variant.dcz = `${rel2}.${id}.dcz`;
|
|
504
|
+
let dcb;
|
|
505
|
+
if (brotli) {
|
|
506
|
+
dcb = Buffer.concat([DCB_MAGIC, sha, brotli(full, old)]);
|
|
507
|
+
writeFileSync4(join(o.dist, `${rel2}.${id}.dcb`), dcb);
|
|
508
|
+
variant.dcb = `${rel2}.${id}.dcb`;
|
|
509
|
+
}
|
|
510
|
+
deltas[hash] = variant;
|
|
511
|
+
const br = zlib.brotliCompressSync(input, { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 } }).length;
|
|
512
|
+
linhas.push(`${rel2} br ${kb(br)} dcz ${kb(dcz.length)}${dcb ? ` dcb ${kb(dcb.length)}` : ""} (de ${relative(process.cwd(), old) || old})`);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
writeFileSync4(join(o.dist, INDEX), JSON.stringify(index, null, 2) + "\n");
|
|
516
|
+
const n = Object.values(index.files).reduce((s, f) => s + Object.keys(f.deltas).length, 0);
|
|
517
|
+
for (const l of linhas) log(l);
|
|
518
|
+
log(`[vitral] ${Object.keys(index.files).length} arquivos, ${n} deltas${brotli ? "" : " (s\xF3 .dcz: falta o bin\xE1rio brotli para .dcb)"}. \xCDndice: ${join(o.dist, INDEX)}`);
|
|
519
|
+
return index;
|
|
520
|
+
}
|
|
521
|
+
|
|
423
522
|
// src/cli/doctor.ts
|
|
424
|
-
import { createHash } from "crypto";
|
|
425
|
-
import { existsSync as
|
|
426
|
-
import { dirname as dirname2, join, resolve as resolve3 } from "path";
|
|
523
|
+
import { createHash as createHash2 } from "crypto";
|
|
524
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
525
|
+
import { dirname as dirname2, join as join2, resolve as resolve3 } from "path";
|
|
427
526
|
import { gzipSync } from "zlib";
|
|
428
527
|
var WARN_KB = 250;
|
|
429
528
|
var ANY_ORIGIN = "https://host.invalid";
|
|
@@ -431,7 +530,7 @@ var satisfies;
|
|
|
431
530
|
var ok = (title) => ({ status: "ok", title });
|
|
432
531
|
var warn = (title, cause, fix) => ({ status: "warn", title, cause, fix });
|
|
433
532
|
var error = (title, cause, fix) => ({ status: "error", title, cause, fix });
|
|
434
|
-
var
|
|
533
|
+
var kb2 = (bytes) => `${(bytes / 1024).toFixed(1)} kB`;
|
|
435
534
|
async function get(f, url, origin, timeout) {
|
|
436
535
|
try {
|
|
437
536
|
const res = await f(url, { headers: { Origin: origin }, signal: AbortSignal.timeout(timeout) });
|
|
@@ -503,8 +602,8 @@ async function hostPolicies(f, cwd, host, timeout, out) {
|
|
|
503
602
|
for (const h of (res.headers.get("content-security-policy") ?? "").split(",")) if (h.trim()) policies.push(parseCsp(h, `header de ${host}`));
|
|
504
603
|
for (const c of meta(new TextDecoder().decode(bytes))) policies.push(parseCsp(c, `<meta> de ${host}`));
|
|
505
604
|
} else {
|
|
506
|
-
const file = ["index.html", "public/index.html", "src/index.html"].map((p) => resolve3(cwd, p)).find((p) =>
|
|
507
|
-
if (file) for (const c of meta(
|
|
605
|
+
const file = ["index.html", "public/index.html", "src/index.html"].map((p) => resolve3(cwd, p)).find((p) => existsSync5(p));
|
|
606
|
+
if (file) for (const c of meta(readFileSync5(file, "utf8"))) policies.push(parseCsp(c, `<meta> de ${file}`));
|
|
508
607
|
}
|
|
509
608
|
out.push(
|
|
510
609
|
policies.length ? ok(`CSP encontrada (${policies.map((p) => p.source).join(", ")})`) : ok(`CSP: nenhuma encontrada ${host ? `em ${host}` : "no index.html"}${host ? "" : " (passe --host para ler o header da p\xE1gina)"}`)
|
|
@@ -552,7 +651,7 @@ function matchesIntegrity(bytes, integrity) {
|
|
|
552
651
|
if (!hashes.length) return void 0;
|
|
553
652
|
const bits = Math.max(...hashes.map((m) => +m[1]));
|
|
554
653
|
const clean2 = (b) => b.replace(/-/g, "+").replace(/_/g, "/").replace(/=/g, "");
|
|
555
|
-
const actual = clean2(
|
|
654
|
+
const actual = clean2(createHash2(`sha${bits}`).update(bytes).digest("base64"));
|
|
556
655
|
return hashes.some((m) => +m[1] === bits && clean2(m[2]) === actual);
|
|
557
656
|
}
|
|
558
657
|
var FRAMEWORKS = {
|
|
@@ -561,8 +660,8 @@ var FRAMEWORKS = {
|
|
|
561
660
|
};
|
|
562
661
|
function installed(cwd, pkg) {
|
|
563
662
|
for (let dir = resolve3(cwd); ; dir = dirname2(dir)) {
|
|
564
|
-
const file =
|
|
565
|
-
if (
|
|
663
|
+
const file = join2(dir, "node_modules", pkg, "package.json");
|
|
664
|
+
if (existsSync5(file)) return JSON.parse(readFileSync5(file, "utf8")).version;
|
|
566
665
|
if (dir === dirname2(dir)) return;
|
|
567
666
|
}
|
|
568
667
|
}
|
|
@@ -655,7 +754,7 @@ async function checkRemote(name, e, ctx, seen) {
|
|
|
655
754
|
}
|
|
656
755
|
if (!e.integrity && !m.integrity) out.push(warn("sem integrity: o JS roda sem confer\xEAncia", void 0, "gere o manifesto com o plugin vitral/vite ou fixe integrity no host."));
|
|
657
756
|
const gz = gzipSync(bytes).length;
|
|
658
|
-
const size = `tamanho do JS: ${
|
|
757
|
+
const size = `tamanho do JS: ${kb2(bytes.length)} (${kb2(gz)} com gzip)`;
|
|
659
758
|
if (ctx.maxKb !== void 0 && gz > ctx.maxKb * 1024)
|
|
660
759
|
out.push(error(size, `passa do limite de ${ctx.maxKb} kB.`, "veja o que entrou no bundle (vite build --mode production com rollup-plugin-visualizer) e use dedupe para o framework."));
|
|
661
760
|
else if (ctx.maxKb === void 0 && gz > WARN_KB * 1024) out.push(warn(size, `acima de ${WARN_KB} kB com gzip, o remote atrasa a p\xE1gina.`, "confira o que entrou no bundle; use dedupe para o framework; passe --max-kb para tornar isto um erro."));
|
|
@@ -672,9 +771,9 @@ async function checkRemote(name, e, ctx, seen) {
|
|
|
672
771
|
);
|
|
673
772
|
}
|
|
674
773
|
}
|
|
675
|
-
const
|
|
774
|
+
const files2 = Object.entries(m.files ?? {}).filter(([f]) => abs(f) !== js);
|
|
676
775
|
const bad = [];
|
|
677
|
-
for (const [file, integrity] of
|
|
776
|
+
for (const [file, integrity] of files2) {
|
|
678
777
|
const got = await get(ctx.f, abs(file), ctx.origin, ctx.timeout);
|
|
679
778
|
if (!got.res?.ok) bad.push(`${file} (${got.failure ?? `HTTP ${got.res.status}`})`);
|
|
680
779
|
else if (!matchesIntegrity(got.bytes, integrity)) bad.push(`${file} (hash n\xE3o confere)`);
|
|
@@ -685,7 +784,7 @@ async function checkRemote(name, e, ctx, seen) {
|
|
|
685
784
|
}
|
|
686
785
|
if (bad.length)
|
|
687
786
|
out.push(error(`arquivos do manifesto com problema: ${bad.join(", ")}`, "o host n\xE3o carrega o CSS ou o build dividido (INTEGRITY ou LOAD_FAILED).", "publique todos os arquivos do mesmo build junto com o vitral.json."));
|
|
688
|
-
else if (
|
|
787
|
+
else if (files2.length) out.push(ok(`${files2.length} arquivo(s) do manifesto conferem com o hash`));
|
|
689
788
|
const range = e.contract ?? ctx.contract;
|
|
690
789
|
if (m.contract?.version && range) {
|
|
691
790
|
if (satisfies(m.contract.version, range)) out.push(ok(`contrato ${m.contract.version} na faixa ${range} do host`));
|
|
@@ -804,8 +903,8 @@ async function doctorCli(argv, options = {}, log = console.log) {
|
|
|
804
903
|
}
|
|
805
904
|
|
|
806
905
|
// src/cli/migrate.ts
|
|
807
|
-
import { existsSync as
|
|
808
|
-
import { basename, dirname as dirname3, extname, join as
|
|
906
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
907
|
+
import { basename, dirname as dirname3, extname, join as join3, relative as relative2, resolve as resolve4 } from "path";
|
|
809
908
|
var FRAMEWORKS2 = {
|
|
810
909
|
preact: { wrap: ["vitral/preact", "preactRemote"], plugin: ["@preact/preset-vite", "preact", "preact()"] },
|
|
811
910
|
react: { wrap: ["vitral/react", "reactRemote"], plugin: ["@vitejs/plugin-react", "react", "react()"] },
|
|
@@ -831,13 +930,13 @@ var VITE_CONFIG = (ts) => `vite.vitral.config.${ts ? "mts" : "mjs"}`;
|
|
|
831
930
|
var OUT_DIR = "dist-vitral";
|
|
832
931
|
function read(file) {
|
|
833
932
|
try {
|
|
834
|
-
return
|
|
933
|
+
return readFileSync6(file, "utf8");
|
|
835
934
|
} catch {
|
|
836
935
|
return null;
|
|
837
936
|
}
|
|
838
937
|
}
|
|
839
938
|
function rel(from, to) {
|
|
840
|
-
const r =
|
|
939
|
+
const r = relative2(from, to).replace(/\\/g, "/");
|
|
841
940
|
return r.startsWith(".") ? r : "./" + r;
|
|
842
941
|
}
|
|
843
942
|
function spec(from, to) {
|
|
@@ -868,28 +967,28 @@ function pairs(obj) {
|
|
|
868
967
|
return [...obj.matchAll(re)].map((m) => [m[2], m[4] ?? m[6]]);
|
|
869
968
|
}
|
|
870
969
|
function project(cwd) {
|
|
871
|
-
const raw = read(
|
|
970
|
+
const raw = read(join3(cwd, "package.json"));
|
|
872
971
|
const pkg = raw ? JSON.parse(raw) : null;
|
|
873
972
|
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies, ...pkg?.peerDependencies };
|
|
874
|
-
return { cwd, pkg, deps, ts:
|
|
973
|
+
return { cwd, pkg, deps, ts: existsSync6(join3(cwd, "tsconfig.json")), framework: DETECT.find(([d]) => d in deps)?.[1] ?? null };
|
|
875
974
|
}
|
|
876
975
|
function sources2(dir, out = []) {
|
|
877
976
|
let list;
|
|
878
977
|
try {
|
|
879
|
-
list =
|
|
978
|
+
list = readdirSync2(dir, { withFileTypes: true });
|
|
880
979
|
} catch {
|
|
881
980
|
return out;
|
|
882
981
|
}
|
|
883
982
|
for (const e of list) {
|
|
884
983
|
if (e.isDirectory()) {
|
|
885
|
-
if (!/^(node_modules|dist|build|\..*)$/.test(e.name)) sources2(
|
|
886
|
-
} else if (/\.(tsx?|jsx?|mjs)$/.test(e.name) && !/\.(test|spec|d)\./.test(e.name)) out.push(
|
|
984
|
+
if (!/^(node_modules|dist|build|\..*)$/.test(e.name)) sources2(join3(dir, e.name), out);
|
|
985
|
+
} else if (/\.(tsx?|jsx?|mjs)$/.test(e.name) && !/\.(test|spec|d)\./.test(e.name)) out.push(join3(dir, e.name));
|
|
887
986
|
}
|
|
888
987
|
return out;
|
|
889
988
|
}
|
|
890
989
|
var LIFECYCLE_EXPORT = /export\s+(?:(?:async\s+)?function\s*\*?\s*mount\b|(?:const|let|var)\s+mount\b|(?:const|let|var)\s*\{[^}]*\bmount\b[^}]*\}|\{[^}]*\bmount\b[^}]*\})/;
|
|
891
990
|
function configs(cwd) {
|
|
892
|
-
return CONFIGS.map((f) => ({ file: f, text: read(
|
|
991
|
+
return CONFIGS.map((f) => ({ file: f, text: read(join3(cwd, f)) })).filter((c) => c.text !== null);
|
|
893
992
|
}
|
|
894
993
|
function detect(p) {
|
|
895
994
|
if (configs(p.cwd).some((c) => MF_CALL.test(c.text) && /\bexposes\s*:/.test(c.text))) return "module-federation";
|
|
@@ -899,7 +998,7 @@ function detect(p) {
|
|
|
899
998
|
}
|
|
900
999
|
function viteConfig(p, vitral, notes) {
|
|
901
1000
|
const path = VITE_CONFIG(p.ts);
|
|
902
|
-
if (
|
|
1001
|
+
if (existsSync6(join3(p.cwd, path))) {
|
|
903
1002
|
notes.push(`${path} j\xE1 existe e n\xE3o foi alterado. Confira se ele usa: ${vitral}`);
|
|
904
1003
|
return null;
|
|
905
1004
|
}
|
|
@@ -922,16 +1021,16 @@ function viteConfig(p, vitral, notes) {
|
|
|
922
1021
|
}
|
|
923
1022
|
function buildScript(p) {
|
|
924
1023
|
if (!p.pkg || p.pkg.scripts?.["build:vitral"]) return null;
|
|
925
|
-
const before = read(
|
|
1024
|
+
const before = read(join3(p.cwd, "package.json"));
|
|
926
1025
|
const indent = /^(\s+)"/m.exec(before)?.[1] ?? " ";
|
|
927
1026
|
const pkg = { ...p.pkg, scripts: { ...p.pkg.scripts, "build:vitral": `vite build -c ${VITE_CONFIG(p.ts)}` } };
|
|
928
1027
|
return { path: "package.json", before, after: JSON.stringify(pkg, null, indent) + (before.endsWith("\n") ? "\n" : "") };
|
|
929
1028
|
}
|
|
930
1029
|
function resolveSource(base, file) {
|
|
931
1030
|
const abs = resolve4(base, file);
|
|
932
|
-
if (extname(abs) &&
|
|
933
|
-
for (const
|
|
934
|
-
for (const
|
|
1031
|
+
if (extname(abs) && existsSync6(abs)) return abs;
|
|
1032
|
+
for (const ext2 of SOURCE_EXT) if (existsSync6(abs + ext2)) return abs + ext2;
|
|
1033
|
+
for (const ext2 of SOURCE_EXT) if (existsSync6(join3(abs, "index" + ext2))) return join3(abs, "index" + ext2);
|
|
935
1034
|
return abs;
|
|
936
1035
|
}
|
|
937
1036
|
function reactRoot(text2, file) {
|
|
@@ -950,13 +1049,13 @@ function reactRoot(text2, file) {
|
|
|
950
1049
|
function singleSpa(p) {
|
|
951
1050
|
const changes = [];
|
|
952
1051
|
const notes = [];
|
|
953
|
-
const src =
|
|
954
|
-
const
|
|
955
|
-
const found =
|
|
1052
|
+
const src = existsSync6(join3(p.cwd, "src")) ? join3(p.cwd, "src") : p.cwd;
|
|
1053
|
+
const files2 = sources2(src);
|
|
1054
|
+
const found = files2.filter((f) => {
|
|
956
1055
|
const t = read(f);
|
|
957
1056
|
return LIFECYCLE_EXPORT.test(t) && /\bunmount\b/.test(t);
|
|
958
1057
|
});
|
|
959
|
-
const roots =
|
|
1058
|
+
const roots = files2.filter((f) => /\bregisterApplication\s*\(/.test(read(f)));
|
|
960
1059
|
if (roots.length) {
|
|
961
1060
|
notes.push(
|
|
962
1061
|
`Root config em ${roots.map((f) => rel(p.cwd, f)).join(", ")}: troque cada registerApplication por configure({ remotes }) e o activeWhen por uma rota do host ou routes() de vitral/routes. Veja https://joaogabriel15.github.io/vitral/migracao/single-spa`
|
|
@@ -968,7 +1067,7 @@ function singleSpa(p) {
|
|
|
968
1067
|
}
|
|
969
1068
|
const [file, ...others] = found;
|
|
970
1069
|
if (others.length) notes.push(`Outros arquivos tamb\xE9m exportam lifecycles e n\xE3o foram migrados: ${others.map((f) => rel(p.cwd, f)).join(", ")}`);
|
|
971
|
-
const out =
|
|
1070
|
+
const out = join3(src, `vitral-remote.${p.ts ? "ts" : "js"}`);
|
|
972
1071
|
const outRel = rel(p.cwd, out).replace(/^\.\//, "");
|
|
973
1072
|
const text2 = read(file);
|
|
974
1073
|
const root = /single-spa-react/.test(text2) ? reactRoot(text2, file) : null;
|
|
@@ -991,13 +1090,13 @@ export default singleSpaRemote(lifecycles)
|
|
|
991
1090
|
`${outRel} embrulha os lifecycles de ${rel(p.cwd, file)} sem mudar o app. Para tirar o single-spa do bundle, troque por reactRemote, vueRemote etc. Veja https://joaogabriel15.github.io/vitral/migracao/single-spa`
|
|
992
1091
|
);
|
|
993
1092
|
}
|
|
994
|
-
if (
|
|
1093
|
+
if (existsSync6(out)) notes.push(`${outRel} j\xE1 existe e n\xE3o foi alterado.`);
|
|
995
1094
|
else changes.push({ path: outRel, before: null, after: code });
|
|
996
1095
|
const vite = viteConfig(p, `vitral({ entry: '${outRel}' })`, notes);
|
|
997
1096
|
if (vite) changes.push(vite);
|
|
998
1097
|
const script = buildScript(p);
|
|
999
1098
|
if (script) changes.push(script);
|
|
1000
|
-
if (/\b(navigateToUrl|mountParcel|mountRootParcel)\b/.test(
|
|
1099
|
+
if (/\b(navigateToUrl|mountParcel|mountRootParcel)\b/.test(files2.map((f) => read(f)).join("\n"))) {
|
|
1001
1100
|
notes.push("O c\xF3digo usa navigateToUrl ou parcels do single-spa: fora de um host single-spa, troque navigateToUrl por ctx.navigate e parcels por <Remote> ou createSlot.");
|
|
1002
1101
|
}
|
|
1003
1102
|
if (/vite-plugin-single-spa|webpack-config-single-spa|systemjs/.test(Object.keys(p.deps).join(" "))) {
|
|
@@ -1041,8 +1140,8 @@ ${sugestoes.join("\n")}
|
|
|
1041
1140
|
return { from: "module-federation", changes, notes };
|
|
1042
1141
|
}
|
|
1043
1142
|
const name = /\bname\s*:\s*(['"`])([^'"`]+)\1/.exec(remote.call)?.[2];
|
|
1044
|
-
const base =
|
|
1045
|
-
const wrapDir =
|
|
1143
|
+
const base = join3(p.cwd, dirname3(remote.file));
|
|
1144
|
+
const wrapDir = join3(p.cwd, "src", "vitral");
|
|
1046
1145
|
const fw = p.framework && p.framework !== "angular" ? FRAMEWORKS2[p.framework] : null;
|
|
1047
1146
|
const entries = [];
|
|
1048
1147
|
let entry;
|
|
@@ -1057,7 +1156,7 @@ ${sugestoes.join("\n")}
|
|
|
1057
1156
|
modules.push(key);
|
|
1058
1157
|
continue;
|
|
1059
1158
|
}
|
|
1060
|
-
const wrapper =
|
|
1159
|
+
const wrapper = join3(wrapDir, `${key.slice(2).replace(/[^\w-]/g, "-")}.${p.ts ? "ts" : "js"}`);
|
|
1061
1160
|
const wrapperRel = rel(p.cwd, wrapper).replace(/^\.\//, "");
|
|
1062
1161
|
const imp = spec(wrapDir, file);
|
|
1063
1162
|
const [from, fn] = fw.wrap;
|
|
@@ -1067,7 +1166,7 @@ import Componente from '${imp}'
|
|
|
1067
1166
|
export * from '${imp}'
|
|
1068
1167
|
export default ${fn}(Componente)
|
|
1069
1168
|
`;
|
|
1070
|
-
if (
|
|
1169
|
+
if (existsSync6(wrapper)) notes.push(`${wrapperRel} j\xE1 existe e n\xE3o foi alterado.`);
|
|
1071
1170
|
else changes.push({ path: wrapperRel, before: null, after: code });
|
|
1072
1171
|
entries.push([key, wrapperRel]);
|
|
1073
1172
|
entry ??= wrapperRel;
|
|
@@ -1141,15 +1240,259 @@ ${result.notes.map((n) => ` - ${n}`).join("\n")}`);
|
|
|
1141
1240
|
return result;
|
|
1142
1241
|
}
|
|
1143
1242
|
for (const c of result.changes) {
|
|
1144
|
-
const file =
|
|
1243
|
+
const file = join3(cwd, c.path);
|
|
1145
1244
|
mkdirSync2(dirname3(file), { recursive: true });
|
|
1146
|
-
|
|
1245
|
+
writeFileSync5(file, c.after);
|
|
1147
1246
|
}
|
|
1148
1247
|
if (result.changes.length) log(`
|
|
1149
1248
|
[vitral] ${result.changes.length} arquivo(s) escrito(s). Rode \`npm run build:vitral\`.`);
|
|
1150
1249
|
return result;
|
|
1151
1250
|
}
|
|
1152
1251
|
|
|
1252
|
+
// src/cli/test.ts
|
|
1253
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
|
|
1254
|
+
import { createRequire } from "module";
|
|
1255
|
+
import { dirname as dirname4, join as join4, resolve as resolve5 } from "path";
|
|
1256
|
+
import { pathToFileURL } from "url";
|
|
1257
|
+
var FILE = "/__vitral_check__.ts";
|
|
1258
|
+
function typeErrors(ts, snippets) {
|
|
1259
|
+
let code = "";
|
|
1260
|
+
const starts = [];
|
|
1261
|
+
snippets.forEach((s, i) => {
|
|
1262
|
+
starts.push(code.length);
|
|
1263
|
+
code += `namespace __c${i} {
|
|
1264
|
+
${s}
|
|
1265
|
+
}
|
|
1266
|
+
`;
|
|
1267
|
+
});
|
|
1268
|
+
const options = { strict: true, noEmit: true, target: ts.ScriptTarget.ES2022, skipLibCheck: true, types: [] };
|
|
1269
|
+
const host = ts.createCompilerHost(options);
|
|
1270
|
+
const getSourceFile = host.getSourceFile;
|
|
1271
|
+
host.getSourceFile = (name, lang, ...rest) => name === FILE ? ts.createSourceFile(name, code, lang) : getSourceFile.call(host, name, lang, ...rest);
|
|
1272
|
+
const fileExists = host.fileExists;
|
|
1273
|
+
host.fileExists = (name) => name === FILE || fileExists.call(host, name);
|
|
1274
|
+
const program = ts.createProgram([FILE], options, host);
|
|
1275
|
+
const out = snippets.map(() => []);
|
|
1276
|
+
for (const d of [...program.getSyntacticDiagnostics(), ...program.getSemanticDiagnostics()]) {
|
|
1277
|
+
if (d.file?.fileName !== FILE || d.start === void 0) continue;
|
|
1278
|
+
let i = starts.length - 1;
|
|
1279
|
+
while (i > 0 && starts[i] > d.start) i--;
|
|
1280
|
+
out[i].push({ code: d.code, text: ts.flattenDiagnosticMessageText(d.messageText, " ") });
|
|
1281
|
+
}
|
|
1282
|
+
return out;
|
|
1283
|
+
}
|
|
1284
|
+
function propFields(ts, type) {
|
|
1285
|
+
const src = ts.createSourceFile("p.ts", `type P = ${type}`, ts.ScriptTarget.ES2022, true);
|
|
1286
|
+
const alias = src.statements[0];
|
|
1287
|
+
if (!alias || !ts.isTypeAliasDeclaration(alias) || !ts.isTypeLiteralNode(alias.type)) return null;
|
|
1288
|
+
const fields = /* @__PURE__ */ new Map();
|
|
1289
|
+
for (const m of alias.type.members) {
|
|
1290
|
+
if (!ts.isPropertySignature(m) || !m.type) return null;
|
|
1291
|
+
const name = ts.isIdentifier(m.name) || ts.isStringLiteral(m.name) ? m.name.text : m.name.getText(src);
|
|
1292
|
+
fields.set(name, { optional: !!m.questionToken, type: m.type.getText(src) });
|
|
1293
|
+
}
|
|
1294
|
+
return fields;
|
|
1295
|
+
}
|
|
1296
|
+
function diffProps(before, after, ts) {
|
|
1297
|
+
const breaking = [];
|
|
1298
|
+
const compatible = [];
|
|
1299
|
+
const norm = (s) => s.replace(/\s+/g, " ").replace(/;\s*}/g, " }").trim();
|
|
1300
|
+
if (norm(before) === norm(after)) return { breaking, compatible };
|
|
1301
|
+
if (!ts) return { breaking: [`Props mudou (sem TypeScript para detalhar): ${norm(before)} \u2192 ${norm(after)}`], compatible };
|
|
1302
|
+
const old = propFields(ts, before);
|
|
1303
|
+
const now = propFields(ts, after);
|
|
1304
|
+
if (!old || !now) {
|
|
1305
|
+
const [errors] = typeErrors(ts, [`type O = ${before}
|
|
1306
|
+
type N = ${after}
|
|
1307
|
+
declare const o: O
|
|
1308
|
+
export const n: N = o`]);
|
|
1309
|
+
(errors.length ? breaking : compatible).push(`Props mudou: ${norm(before)} \u2192 ${norm(after)}`);
|
|
1310
|
+
return { breaking, compatible };
|
|
1311
|
+
}
|
|
1312
|
+
const changed = [];
|
|
1313
|
+
for (const [name, f] of now) {
|
|
1314
|
+
const was = old.get(name);
|
|
1315
|
+
if (!was) (f.optional ? compatible : breaking).push(`${f.optional ? "prop opcional nova" : "prop obrigat\xF3ria nova"}: ${name}: ${f.type}`);
|
|
1316
|
+
else {
|
|
1317
|
+
if (was.optional && !f.optional) breaking.push(`${name} virou obrigat\xF3ria`);
|
|
1318
|
+
if (!was.optional && f.optional) compatible.push(`${name} virou opcional`);
|
|
1319
|
+
if (norm(was.type) !== norm(f.type)) changed.push([name, was, f]);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
for (const [name, f] of old) if (!now.has(name)) breaking.push(`prop removida: ${name}: ${f.type} (o host que ainda passa ${name} n\xE3o compila)`);
|
|
1323
|
+
const results = typeErrors(
|
|
1324
|
+
ts,
|
|
1325
|
+
changed.map(([, was, f]) => `type O = ${was.type}
|
|
1326
|
+
type N = ${f.type}
|
|
1327
|
+
declare const o: O
|
|
1328
|
+
export const n: N = o`)
|
|
1329
|
+
);
|
|
1330
|
+
changed.forEach(([name, was, f], i) => {
|
|
1331
|
+
if (results[i].length) breaking.push(`tipo de ${name} mudou: ${norm(was.type)} \u2192 ${norm(f.type)} (${results[i][0].text})`);
|
|
1332
|
+
else compatible.push(`tipo de ${name} aceita mais valores: ${norm(was.type)} \u2192 ${norm(f.type)}`);
|
|
1333
|
+
});
|
|
1334
|
+
return { breaking, compatible };
|
|
1335
|
+
}
|
|
1336
|
+
function tsLiteral(value) {
|
|
1337
|
+
if (value === null || typeof value !== "object") return value === void 0 ? "undefined" : JSON.stringify(value);
|
|
1338
|
+
if (Array.isArray(value)) return `[${value.map(tsLiteral).join(", ")}]`;
|
|
1339
|
+
const v = value;
|
|
1340
|
+
if (v.$fn === true) return "((..._a: any[]): any => undefined)";
|
|
1341
|
+
if (typeof v.$date === "string") return `new Date(${JSON.stringify(v.$date)})`;
|
|
1342
|
+
if (typeof v.$unknown === "string") return "(undefined as any)";
|
|
1343
|
+
return `{ ${Object.entries(v).map(([k, x]) => `${JSON.stringify(k)}: ${tsLiteral(x)}`).join(", ")} }`;
|
|
1344
|
+
}
|
|
1345
|
+
var EXCESS = /* @__PURE__ */ new Set([2353, 2561]);
|
|
1346
|
+
function checkCaseProps(contract, props, ts) {
|
|
1347
|
+
const failures = [];
|
|
1348
|
+
const warnings = [];
|
|
1349
|
+
const fields = propFields(ts, props);
|
|
1350
|
+
const all = [];
|
|
1351
|
+
contract.cases.forEach((c, i) => {
|
|
1352
|
+
all.push({ at: `caso ${i + 1}`, value: c.props });
|
|
1353
|
+
c.updates?.forEach((u, j) => all.push({ at: `caso ${i + 1}, update ${j + 1}`, value: u }));
|
|
1354
|
+
});
|
|
1355
|
+
const extra = (at, what) => warnings.push(`${at}: o host passa uma prop que o remote n\xE3o declara: ${what}`);
|
|
1356
|
+
const values = all.map(({ at, value }) => {
|
|
1357
|
+
if (!fields || !value || typeof value !== "object") return value;
|
|
1358
|
+
const known = Object.entries(value).filter(([k]) => fields.has(k) || (extra(at, k), false));
|
|
1359
|
+
return Object.fromEntries(known);
|
|
1360
|
+
});
|
|
1361
|
+
const results = typeErrors(
|
|
1362
|
+
ts,
|
|
1363
|
+
values.map((value) => `type Props = ${props}
|
|
1364
|
+
export const p: Props = ${tsLiteral(value)}`)
|
|
1365
|
+
);
|
|
1366
|
+
results.forEach((errors, i) => {
|
|
1367
|
+
for (const e of errors) {
|
|
1368
|
+
if (EXCESS.has(e.code)) extra(all[i].at, e.text);
|
|
1369
|
+
else failures.push(`${all[i].at}: props n\xE3o batem com o Props local: ${e.text}`);
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
return { failures, warnings };
|
|
1373
|
+
}
|
|
1374
|
+
async function installDom(cwd) {
|
|
1375
|
+
let mod;
|
|
1376
|
+
try {
|
|
1377
|
+
mod = await import(pathToFileURL(createRequire(join4(cwd, "package.json")).resolve("happy-dom")).href);
|
|
1378
|
+
} catch {
|
|
1379
|
+
try {
|
|
1380
|
+
mod = await import("happy-dom");
|
|
1381
|
+
} catch {
|
|
1382
|
+
throw new Error("[vitral] vitral test precisa do happy-dom: npm i -D happy-dom");
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
const win = new mod.Window({ url: "http://localhost/" });
|
|
1386
|
+
const g = globalThis;
|
|
1387
|
+
const dom = /* @__PURE__ */ new Set(["Event", "EventTarget", "CustomEvent", "MessageEvent", "DOMException", "navigator", "window", "self"]);
|
|
1388
|
+
const before = /* @__PURE__ */ new Map();
|
|
1389
|
+
for (const key of Object.getOwnPropertyNames(win)) {
|
|
1390
|
+
if (key in g && !dom.has(key)) continue;
|
|
1391
|
+
const desc = Object.getOwnPropertyDescriptor(g, key);
|
|
1392
|
+
if (desc && !desc.configurable) continue;
|
|
1393
|
+
before.set(key, desc);
|
|
1394
|
+
Object.defineProperty(g, key, { value: win[key], configurable: true, writable: true });
|
|
1395
|
+
}
|
|
1396
|
+
return async () => {
|
|
1397
|
+
await win.happyDOM?.close?.();
|
|
1398
|
+
for (const [key, desc] of before) {
|
|
1399
|
+
if (desc) Object.defineProperty(g, key, desc);
|
|
1400
|
+
else delete g[key];
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
function contractFiles(cwd, paths) {
|
|
1405
|
+
return paths.flatMap((p) => {
|
|
1406
|
+
const abs = resolve5(cwd, p);
|
|
1407
|
+
if (!existsSync7(abs)) throw new Error(`[vitral] contrato n\xE3o encontrado: ${p}`);
|
|
1408
|
+
return statSync2(abs).isDirectory() ? readdirSync3(abs).filter((f) => f.endsWith(".contract.json")).sort().map((f) => join4(abs, f)) : [abs];
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
function sourcesIn(dir) {
|
|
1412
|
+
const out = [];
|
|
1413
|
+
const walk = (d) => {
|
|
1414
|
+
for (const f of readdirSync3(d, { withFileTypes: true })) {
|
|
1415
|
+
if (f.name === "node_modules") continue;
|
|
1416
|
+
const p = join4(d, f.name);
|
|
1417
|
+
if (f.isDirectory()) walk(p);
|
|
1418
|
+
else if (/\.[cm]?js$/.test(f.name)) out.push(readFileSync7(p, "utf8"));
|
|
1419
|
+
}
|
|
1420
|
+
};
|
|
1421
|
+
walk(dir);
|
|
1422
|
+
return out;
|
|
1423
|
+
}
|
|
1424
|
+
function publishedTypes(since, cwd) {
|
|
1425
|
+
if (/^https?:\/\//.test(since)) {
|
|
1426
|
+
if (since.endsWith(".d.ts")) return since;
|
|
1427
|
+
return new URL("vitral.d.ts", since.endsWith(".json") || since.endsWith("/") ? since : since + "/").href;
|
|
1428
|
+
}
|
|
1429
|
+
const abs = resolve5(cwd, since);
|
|
1430
|
+
return existsSync7(abs) && statSync2(abs).isDirectory() ? join4(abs, "vitral.d.ts") : abs;
|
|
1431
|
+
}
|
|
1432
|
+
async function readText(where, get2) {
|
|
1433
|
+
if (!/^https?:\/\//.test(where)) return readFileSync7(where, "utf8");
|
|
1434
|
+
const res = await get2(where);
|
|
1435
|
+
if (!res.ok) throw new Error(`HTTP ${res.status} em ${where}`);
|
|
1436
|
+
return res.text();
|
|
1437
|
+
}
|
|
1438
|
+
async function test(options = {}) {
|
|
1439
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1440
|
+
const log = options.log ?? console.log;
|
|
1441
|
+
const get2 = options.fetch ?? fetch;
|
|
1442
|
+
const dir = resolve5(cwd, options.dir ?? "dist");
|
|
1443
|
+
if (!options.contracts?.length && !options.since) throw new Error("[vitral] passe --contract arquivo e/ou --since url. Veja `vitral --help`.");
|
|
1444
|
+
const localFile = resolve5(cwd, options.types ?? join4(dir, "vitral.d.ts"));
|
|
1445
|
+
const local = existsSync7(localFile) ? parseProps(readFileSync7(localFile, "utf8")) : null;
|
|
1446
|
+
let ts;
|
|
1447
|
+
const typescript = async () => ts === void 0 ? ts = await loadTs(cwd) : ts;
|
|
1448
|
+
let failures = 0;
|
|
1449
|
+
if (options.since) {
|
|
1450
|
+
const where = publishedTypes(options.since, cwd);
|
|
1451
|
+
if (!local) throw new Error(`[vitral] sem Props local em ${localFile}. Rode o build com o plugin, ou passe --types.`);
|
|
1452
|
+
const before = parseProps(await readText(where, get2));
|
|
1453
|
+
if (!before) throw new Error(`[vitral] ${where} sem "export type Props".`);
|
|
1454
|
+
const diff2 = diffProps(before, local, await typescript());
|
|
1455
|
+
for (const c of diff2.breaking) log(`\u2717 quebra: ${c}`);
|
|
1456
|
+
for (const c of diff2.compatible) log(`\u2713 compat\xEDvel: ${c}`);
|
|
1457
|
+
log(diff2.breaking.length ? `[vitral] ${diff2.breaking.length} mudan\xE7a(s) nas props que quebram o host (desde ${where}).` : `[vitral] props compat\xEDveis com ${where}.`);
|
|
1458
|
+
failures += diff2.breaking.length;
|
|
1459
|
+
}
|
|
1460
|
+
if (options.contracts?.length) {
|
|
1461
|
+
const files2 = contractFiles(cwd, options.contracts);
|
|
1462
|
+
let entry = options.entry && resolve5(cwd, options.entry);
|
|
1463
|
+
if (!entry) {
|
|
1464
|
+
const manifest = join4(dir, "vitral.json");
|
|
1465
|
+
if (!existsSync7(manifest)) throw new Error(`[vitral] ${manifest} n\xE3o existe. Rode o build ou passe --entry.`);
|
|
1466
|
+
entry = resolve5(dir, JSON.parse(readFileSync7(manifest, "utf8")).entry);
|
|
1467
|
+
}
|
|
1468
|
+
const close = typeof document === "undefined" ? await installDom(cwd) : void 0;
|
|
1469
|
+
try {
|
|
1470
|
+
const mod = await import(pathToFileURL(entry).href);
|
|
1471
|
+
const sources3 = sourcesIn(dirname4(entry));
|
|
1472
|
+
const tsApi = local ? await typescript() : null;
|
|
1473
|
+
if (!local) log(`[vitral] sem Props local em ${localFile}: as props gravadas n\xE3o foram conferidas pelo tipo.`);
|
|
1474
|
+
else if (!tsApi) log("[vitral] sem TypeScript no projeto: as props gravadas n\xE3o foram conferidas pelo tipo.");
|
|
1475
|
+
for (const file of files2) {
|
|
1476
|
+
const contract = JSON.parse(readFileSync7(file, "utf8"));
|
|
1477
|
+
const result = await verify(contract, mod, { sources: sources3 });
|
|
1478
|
+
if (local && tsApi) {
|
|
1479
|
+
const typed = checkCaseProps(contract, local, tsApi);
|
|
1480
|
+
result.failures.push(...typed.failures);
|
|
1481
|
+
result.warnings.push(...typed.warnings);
|
|
1482
|
+
}
|
|
1483
|
+
const who = `${contract.remote}${contract.host ? ` (host ${contract.host})` : ""}`;
|
|
1484
|
+
for (const w of result.warnings) log(`! ${who}: ${w}`);
|
|
1485
|
+
for (const f of result.failures) log(`\u2717 ${who}: ${f}`);
|
|
1486
|
+
log(result.failures.length ? `[vitral] ${file}: ${result.failures.length} falha(s) em ${result.cases} caso(s).` : `[vitral] ${file}: ${result.cases} caso(s) passaram.`);
|
|
1487
|
+
failures += result.failures.length;
|
|
1488
|
+
}
|
|
1489
|
+
} finally {
|
|
1490
|
+
await close?.();
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
return failures;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1153
1496
|
// src/cli.ts
|
|
1154
1497
|
var AJUDA = `Uso:
|
|
1155
1498
|
vitral types [remotes.json | nome=url ...] [--base url] [--out arquivo] [--check]
|
|
@@ -1160,6 +1503,9 @@ var AJUDA = `Uso:
|
|
|
1160
1503
|
[--contract faixa] [--max-kb n] [--json]
|
|
1161
1504
|
vitral migrate [pasta] [--from single-spa|module-federation] [--dry-run]
|
|
1162
1505
|
vitral canal <canal.json> [--stable x.y.z] [--canary x.y.z | off] [--percent n]
|
|
1506
|
+
vitral test [--contract arquivo|pasta ...] [--since url|arquivo] [--dir dist] [--entry arquivo]
|
|
1507
|
+
[--types vitral.d.ts]
|
|
1508
|
+
vitral dict <dist> --from <dist-anterior> [--from ...] [--level n] [--no-dcb]
|
|
1163
1509
|
|
|
1164
1510
|
types Baixa o vitral.d.ts de cada remote e gera vitral-env.d.ts.
|
|
1165
1511
|
--check s\xF3 confere se o hash de cada manifesto ainda \xE9 o
|
|
@@ -1177,7 +1523,16 @@ var AJUDA = `Uso:
|
|
|
1177
1523
|
--dry-run s\xF3 mostra o que mudaria, sem escrever.
|
|
1178
1524
|
canal Cria ou edita o canal de um remote (canary e rollback) e mostra
|
|
1179
1525
|
como ficou. --stable \xE9 obrigat\xF3ria ao criar; --canary off tira a
|
|
1180
|
-
canary; --percent vai de 0 a 100
|
|
1526
|
+
canary; --percent vai de 0 a 100.
|
|
1527
|
+
test No CI do remote. --contract monta o build em happy-dom com cada
|
|
1528
|
+
caso gravado pelo host (record() de vitral/testing) e falha se o
|
|
1529
|
+
mount quebrar, se um evento que o host escuta sumir ou se as props
|
|
1530
|
+
gravadas n\xE3o baterem com o Props local. --since compara o Props
|
|
1531
|
+
do vitral.d.ts publicado com o local e falha nas mudan\xE7as que
|
|
1532
|
+
quebram o host.
|
|
1533
|
+
dict Pr\xE9-comprime cada arquivo do dist com a vers\xE3o anterior dele
|
|
1534
|
+
como dicion\xE1rio (.dcz e, com o bin\xE1rio brotli, .dcb) e grava
|
|
1535
|
+
vitral-dict.json para o middleware vitral/dict.`;
|
|
1181
1536
|
var falhou = (err) => {
|
|
1182
1537
|
console.error(err.message);
|
|
1183
1538
|
process.exitCode = 1;
|
|
@@ -1189,7 +1544,7 @@ function bool(args2, name) {
|
|
|
1189
1544
|
}
|
|
1190
1545
|
function version() {
|
|
1191
1546
|
try {
|
|
1192
|
-
return
|
|
1547
|
+
return createRequire2(import.meta.url)("../package.json").version;
|
|
1193
1548
|
} catch {
|
|
1194
1549
|
return "0.0.0";
|
|
1195
1550
|
}
|
|
@@ -1247,6 +1602,21 @@ if (command === "types") {
|
|
|
1247
1602
|
} catch (err) {
|
|
1248
1603
|
falhou(err);
|
|
1249
1604
|
}
|
|
1605
|
+
} else if (command === "test") {
|
|
1606
|
+
const contracts = [];
|
|
1607
|
+
for (let c = flag(args, "--contract"); c !== void 0; c = flag(args, "--contract")) contracts.push(c);
|
|
1608
|
+
finish(test({ contracts, since: flag(args, "--since"), dir: flag(args, "--dir"), entry: flag(args, "--entry"), types: flag(args, "--types") }));
|
|
1609
|
+
} else if (command === "dict" && args.length) {
|
|
1610
|
+
const from = [];
|
|
1611
|
+
for (let f = flag(args, "--from"); f !== void 0; f = flag(args, "--from")) from.push(f);
|
|
1612
|
+
const level = flag(args, "--level");
|
|
1613
|
+
const noDcb = bool(args, "--no-dcb");
|
|
1614
|
+
try {
|
|
1615
|
+
if (!from.length) throw new Error("[vitral] dict precisa de --from <dist-anterior>");
|
|
1616
|
+
dict({ dist: args[0], from, level: level === void 0 ? void 0 : Number(level), brotli: noDcb ? false : void 0 });
|
|
1617
|
+
} catch (err) {
|
|
1618
|
+
falhou(err);
|
|
1619
|
+
}
|
|
1250
1620
|
} else {
|
|
1251
1621
|
console.log(AJUDA);
|
|
1252
1622
|
process.exitCode = command && command !== "--help" ? 1 : 0;
|