@writedocs/generator 0.3.0 → 0.4.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/package.json +3 -2
- package/src/lib/config-schema.js +23 -5
- package/src/lib/config-schema.ts +76 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@writedocs/generator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Static site generator for docs — a writedocs.json + MDX folder in, a static site out.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"build": "node bin/writedocs.js build",
|
|
21
21
|
"init": "node bin/writedocs.js init",
|
|
22
22
|
"build:schema": "esbuild src/lib/config-schema.ts --format=esm --target=node22 --outfile=src/lib/config-schema.js",
|
|
23
|
-
"prepare": "npm run build:schema"
|
|
23
|
+
"prepare": "npm run build:schema",
|
|
24
|
+
"test": "node --test \"scripts/*.test.js\""
|
|
24
25
|
},
|
|
25
26
|
"keywords": [
|
|
26
27
|
"docs",
|
package/src/lib/config-schema.js
CHANGED
|
@@ -464,15 +464,33 @@ function issuesDoRamo(ramo) {
|
|
|
464
464
|
const comIssues = ramo;
|
|
465
465
|
return comIssues?.issues ?? [];
|
|
466
466
|
}
|
|
467
|
+
function maiorProfundidade(lista) {
|
|
468
|
+
let maior = 0;
|
|
469
|
+
for (const issue of lista) {
|
|
470
|
+
const p = issue.caminho.length + (issue.code === "unrecognized_keys" ? 1 : 0);
|
|
471
|
+
if (p > maior) maior = p;
|
|
472
|
+
}
|
|
473
|
+
return maior;
|
|
474
|
+
}
|
|
475
|
+
const planoPorRamo = /* @__PURE__ */ new WeakMap();
|
|
476
|
+
function planoDoRamo(ramo) {
|
|
477
|
+
const lista = issuesDoRamo(ramo);
|
|
478
|
+
if (typeof ramo !== "object" || ramo === null) return desembrulharUnioes(lista);
|
|
479
|
+
const memoizado = planoPorRamo.get(ramo);
|
|
480
|
+
if (memoizado) return memoizado;
|
|
481
|
+
const plano = desembrulharUnioes(lista);
|
|
482
|
+
planoPorRamo.set(ramo, plano);
|
|
483
|
+
return plano;
|
|
484
|
+
}
|
|
467
485
|
function melhorRamo(ramos) {
|
|
468
486
|
return ramos.map((ramo) => {
|
|
469
|
-
const
|
|
487
|
+
const plano = planoDoRamo(ramo);
|
|
470
488
|
return {
|
|
471
|
-
lista,
|
|
472
|
-
n:
|
|
473
|
-
profundidade:
|
|
489
|
+
lista: issuesDoRamo(ramo),
|
|
490
|
+
n: plano.length,
|
|
491
|
+
profundidade: maiorProfundidade(plano)
|
|
474
492
|
};
|
|
475
|
-
}).sort((a, b) =>
|
|
493
|
+
}).sort((a, b) => b.profundidade - a.profundidade || a.n - b.n)[0].lista;
|
|
476
494
|
}
|
|
477
495
|
function desembrulharUnioes(issues, prefixo = []) {
|
|
478
496
|
const saida = [];
|
package/src/lib/config-schema.ts
CHANGED
|
@@ -1062,15 +1062,84 @@ function issuesDoRamo(ramo: unknown): IssueCru[] {
|
|
|
1062
1062
|
return comIssues?.issues ?? [];
|
|
1063
1063
|
}
|
|
1064
1064
|
|
|
1065
|
+
/** O maior valor de `f` sobre a lista, ou 0 se ela for vazia. Um `for` e nao
|
|
1066
|
+
* `Math.max(0, ...lista.map(f))`: o spread vira argumentos de chamada, e uma
|
|
1067
|
+
* lista grande o bastante (config com dezenas de milhares de erros) estoura o
|
|
1068
|
+
* limite de argumentos com RangeError em vez de devolver um numero. */
|
|
1069
|
+
function maiorProfundidade(lista: IssuePlano[]): number {
|
|
1070
|
+
let maior = 0;
|
|
1071
|
+
for (const issue of lista) {
|
|
1072
|
+
const p = issue.caminho.length + (issue.code === 'unrecognized_keys' ? 1 : 0);
|
|
1073
|
+
if (p > maior) maior = p;
|
|
1074
|
+
}
|
|
1075
|
+
return maior;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/** Plano ACHATADO de um ramo, memoizado por identidade do ramo.
|
|
1079
|
+
*
|
|
1080
|
+
* A memoizacao nao e otimizacao, e o que torna o achatamento viavel. Pontuar um
|
|
1081
|
+
* ramo exige desembrulhar as unioes DENTRO dele, e cada uma dessas tem os seus
|
|
1082
|
+
* proprios ramos - sem cache, a pontuacao visita o mesmo sub-ramo uma vez por
|
|
1083
|
+
* caminho que leva ate ele, o que e exponencial na profundidade do aninhamento.
|
|
1084
|
+
* Medido com grupos aninhados e um erro no fundo: 3,7 ms com 4 niveis, 281 ms
|
|
1085
|
+
* com 8, 69 SEGUNDOS com 12. Com o cache, os mesmos casos ficam em 0,2 ms,
|
|
1086
|
+
* porque cada no da arvore de erro do Zod e achatado uma vez so.
|
|
1087
|
+
*
|
|
1088
|
+
* Se alguem remover este WeakMap por parecer supérfluo, o `writedocs validate`
|
|
1089
|
+
* passa a travar em configs profundas. Nao remova. */
|
|
1090
|
+
const planoPorRamo = new WeakMap<object, IssuePlano[]>();
|
|
1091
|
+
|
|
1092
|
+
function planoDoRamo(ramo: unknown): IssuePlano[] {
|
|
1093
|
+
const lista = issuesDoRamo(ramo);
|
|
1094
|
+
if (typeof ramo !== 'object' || ramo === null) return desembrulharUnioes(lista);
|
|
1095
|
+
const memoizado = planoPorRamo.get(ramo);
|
|
1096
|
+
if (memoizado) return memoizado;
|
|
1097
|
+
// Prefixo vazio de proposito: a pontuacao compara ramos entre si, entao a
|
|
1098
|
+
// profundidade tem que ser relativa ao proprio ramo, nao ao documento.
|
|
1099
|
+
const plano = desembrulharUnioes(lista);
|
|
1100
|
+
planoPorRamo.set(ramo, plano);
|
|
1101
|
+
return plano;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1065
1104
|
/** Escolhe qual ramo de uma uniao o cliente PROVAVELMENTE quis.
|
|
1066
1105
|
*
|
|
1067
|
-
* Criterio:
|
|
1106
|
+
* Criterio: caminho mais fundo primeiro; empate resolvido por menos issues. A
|
|
1068
1107
|
* intuicao e a do Gabriel na spec - o ramo certo e o que nao reclama da chave
|
|
1069
1108
|
* que o cliente usou -, e a profundidade importa porque um ramo que chegou
|
|
1070
1109
|
* fundo antes de falhar (`products.0.product`) reconheceu a forma, enquanto um
|
|
1071
1110
|
* que falha na raiz (`expected array, received object`) so recusou o shape.
|
|
1072
1111
|
*
|
|
1073
|
-
*
|
|
1112
|
+
* DUAS CORRECOES sobre a primeira versao (WD-063), as duas medidas contra o
|
|
1113
|
+
* `00-kitchen-sink` real - ver o relato de revisao 43:
|
|
1114
|
+
*
|
|
1115
|
+
* 1. PONTUA O RAMO ACHATADO, nao a lista crua. Um ramo que e ele proprio uma
|
|
1116
|
+
* uniao aparece como UM issue (`invalid_union`) no caminho vazio - ou seja,
|
|
1117
|
+
* com a MENOR contagem e a MENOR profundidade possiveis, sem que isso diga
|
|
1118
|
+
* nada sobre o quanto ele reconheceu. Era o caso mais comum que existe: um
|
|
1119
|
+
* item de `pages` e `string | grupo | link`, e o grupo e outra uniao, entao
|
|
1120
|
+
* todo erro estrutural dentro de um grupo empatava com o ramo `string` e
|
|
1121
|
+
* perdia pela ordem de declaracao. O cliente que escrevia
|
|
1122
|
+
* `{ "group": "G", "pages": 42 }` recebia "expected string, received
|
|
1123
|
+
* object" - conselho errado, nao so inutil. Achatando primeiro, o mesmo caso
|
|
1124
|
+
* vira `pages.0.pages: expected array, received number`.
|
|
1125
|
+
*
|
|
1126
|
+
* 2. PROFUNDIDADE EFETIVA: `unrecognized_keys` reporta no caminho do PAI, com a
|
|
1127
|
+
* chave ofensora em `keys`, entao a profundidade dele sai subestimada em 1.
|
|
1128
|
+
* Sem a correcao, um ramo que identificou a chave errada perde para um que
|
|
1129
|
+
* so exigiu um campo ausente. (`validateDocsConfig` ja fazia a mesma conta
|
|
1130
|
+
* para achar a LINHA; aqui ela entra tambem na pontuacao.)
|
|
1131
|
+
*
|
|
1132
|
+
* Medido sobre as 60 folhas da `navigation` do `00-kitchen-sink`, trocando o
|
|
1133
|
+
* tipo de cada uma: a versao anterior apontava o campo exato em 42/60 e PARAVA
|
|
1134
|
+
* RASO em 18/60 (sempre os mesmos 18: os que ficam sob um `pages`); esta aponta
|
|
1135
|
+
* 60/60 para valor escalar errado e nunca para raso. Para um objeto vazio ou
|
|
1136
|
+
* parcial no lugar de uma pagina, as duas ficam tecnicamente empatadas (41
|
|
1137
|
+
* contra 42): a anterior diz "expected string", esta lista os campos que
|
|
1138
|
+
* faltam para ser um grupo. E o custo aceito desta versao, e substitui o antigo
|
|
1139
|
+
* (`tabs` e `products` escritos juntos, que agora sai como
|
|
1140
|
+
* `Unrecognized key: "products"`).
|
|
1141
|
+
*
|
|
1142
|
+
* EMPATE TOTAL (mesma profundidade E mesmo numero de issues): vence o ramo
|
|
1074
1143
|
* declarado primeiro no schema. `Array.prototype.sort` e estavel desde a
|
|
1075
1144
|
* ES2019, entao ordenar e pegar o [0] ja entrega isso - se alguem trocar por
|
|
1076
1145
|
* uma ordenacao instavel, o desempate vira sorteio. Duas razoes para "o
|
|
@@ -1084,14 +1153,14 @@ function issuesDoRamo(ramo: unknown): IssueCru[] {
|
|
|
1084
1153
|
function melhorRamo(ramos: unknown[]): IssueCru[] {
|
|
1085
1154
|
return ramos
|
|
1086
1155
|
.map((ramo) => {
|
|
1087
|
-
const
|
|
1156
|
+
const plano = planoDoRamo(ramo);
|
|
1088
1157
|
return {
|
|
1089
|
-
lista,
|
|
1090
|
-
n:
|
|
1091
|
-
profundidade:
|
|
1158
|
+
lista: issuesDoRamo(ramo),
|
|
1159
|
+
n: plano.length,
|
|
1160
|
+
profundidade: maiorProfundidade(plano),
|
|
1092
1161
|
};
|
|
1093
1162
|
})
|
|
1094
|
-
.sort((a, b) =>
|
|
1163
|
+
.sort((a, b) => b.profundidade - a.profundidade || a.n - b.n)[0].lista;
|
|
1095
1164
|
}
|
|
1096
1165
|
|
|
1097
1166
|
/** Desembrulha `invalid_union` recursivamente - o ramo escolhido normalmente e
|