@orkastery/cli 0.4.3 → 0.5.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/adapters/README.md +11 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +3 -1
- package/adapters/claude-code/agents/ork-check.md +1 -1
- package/adapters/claude-code/agents/ork-go.md +2 -2
- package/adapters/claude-code/agents/ork-goal.md +1 -1
- package/adapters/claude-code/agents/ork-master.md +1 -1
- package/adapters/claude-code/agents/ork-plan.md +1 -1
- package/adapters/claude-code/agents/ork-ship.md +1 -1
- package/adapters/claude-code/commands/master.md +11 -8
- package/adapters/claude-code/commands/ork.md +8 -0
- package/adapters/claude-code/commands/ship.md +2 -2
- package/adapters/codex/skills/ork/SKILL.md +12 -1
- package/adapters/hermes/README.md +3 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +17 -2
- package/adapters/hermes/bin/ork-brain.sh +3 -0
- package/adapters/hermes/bin/ork-maestro.sh +4 -1
- package/adapters/hermes/bin/ork-network-roadmap.sh +11 -0
- package/adapters/hermes/bin/ork-roadmap-status.sh +9 -0
- package/adapters/hermes/hermes.plugin.json +2 -0
- package/adapters/hermes/hitl-ingress/__init__.py +36 -3
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +19 -20
- package/adapters/openclaw/README.md +34 -2
- package/adapters/openclaw/dist/hitl-ingress.js +37 -3
- package/adapters/openclaw/dist/index.js +92 -20
- package/adapters/openclaw/openclaw.plugin.json +10 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +32 -3
- package/adapters/openclaw/src/index.ts +108 -20
- package/assets/docs/padroes/documentacao-de-produto.md +2 -2
- package/assets/docs/padroes/roadmap-de-produto.md +1 -1
- package/assets/docs/produto/_modelo-feature.md +2 -0
- package/assets/orkmind_bridge.py +214 -1
- package/dist/adapters/claude-bg.js +6 -3
- package/dist/board.js +74 -0
- package/dist/branch-de-estado.js +5 -4
- package/dist/busca-semantica.js +161 -0
- package/dist/ci.js +141 -10
- package/dist/company-brain-cli.js +27 -2
- package/dist/company-brain-context.js +132 -0
- package/dist/company-brain-dossie.js +376 -0
- package/dist/company-brain-mcp.js +4 -1
- package/dist/conducao.js +71 -18
- package/dist/decisao-autonoma.js +42 -0
- package/dist/docs.js +64 -22
- package/dist/doctor.js +33 -0
- package/dist/entrega-pr.js +109 -5
- package/dist/experiencia-instalacao.js +256 -0
- package/dist/experiencia.js +72 -0
- package/dist/fabrica-estado.js +43 -10
- package/dist/fechamento.js +105 -0
- package/dist/forja.js +435 -0
- package/dist/gates.js +9 -1
- package/dist/hitl-classificacao.js +11 -1
- package/dist/hitl-contract.js +50 -7
- package/dist/hitl-curto.js +158 -0
- package/dist/hitl-gates.js +67 -1
- package/dist/hitl-lote.js +30 -15
- package/dist/hitl-presentation.js +10 -7
- package/dist/hitl-resumo.js +33 -12
- package/dist/hitl-texto-livre.js +150 -0
- package/dist/hosts.js +86 -1
- package/dist/index.js +602 -38
- package/dist/indice-vetorial.js +317 -0
- package/dist/init.js +13 -0
- package/dist/integracoes-locais.js +1 -1
- package/dist/intelligence-benchmark-contract.js +570 -0
- package/dist/intelligence-graph-cli.js +430 -0
- package/dist/intelligence-graph-contract.js +577 -0
- package/dist/intelligence-graph-extract-md.js +469 -0
- package/dist/intelligence-graph-extract-ts.js +810 -0
- package/dist/intelligence-graph-extract.js +369 -0
- package/dist/intelligence-graph-index.js +512 -0
- package/dist/intelligence-graph-parsers.js +327 -0
- package/dist/intelligence-graph-query.js +344 -0
- package/dist/intelligence-graph-repo.js +187 -0
- package/dist/leases.js +84 -0
- package/dist/ledger.js +14 -0
- package/dist/maestro-cli.js +10 -1
- package/dist/maestro-contract.js +5 -1
- package/dist/maestro-discovery.js +16 -2
- package/dist/maestro-snapshot.js +6 -2
- package/dist/manifest.js +142 -5
- package/dist/master-batch.js +3 -2
- package/dist/master-digest.js +2 -2
- package/dist/master-nota.js +203 -0
- package/dist/master.js +4 -0
- package/dist/mcp-experiencia.js +164 -0
- package/dist/mcp-git.js +4 -0
- package/dist/mcp-install.js +3 -1
- package/dist/mcp-maestro.js +3 -2
- package/dist/mcp-server.js +84 -5
- package/dist/memoria.js +147 -1
- package/dist/network-roadmap.js +907 -0
- package/dist/ocupacao.js +38 -2
- package/dist/onboarding.js +113 -7
- package/dist/orkmind.js +165 -9
- package/dist/phase.js +166 -13
- package/dist/policies.js +2 -0
- package/dist/projeto-alvo.js +551 -0
- package/dist/prompts.js +1 -1
- package/dist/pulse-consentimento.js +4 -2
- package/dist/pulse-delivery.js +5 -0
- package/dist/pulse-resposta.js +674 -49
- package/dist/pulse.js +21 -0
- package/dist/retry.js +51 -8
- package/dist/roadmap-reservas.js +262 -20
- package/dist/roadmap-status.js +213 -0
- package/dist/thread-close.js +3 -0
- package/dist/verify.js +4 -3
- package/eval/casos/orchestration-experience-pt-br.json +48 -0
- package/eval/casos/orchestration-experience.json +48 -0
- package/package.json +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +581 -0
- package/schemas/graph-benchmark.v1.schema.json +2240 -0
- package/schemas/maestro-snapshot.schema.json +23 -0
- package/skills/README.md +7 -5
- package/skills/core/onboarding/SKILL.md +7 -0
- package/skills/core/orchestration-experience/SKILL.md +113 -0
- package/skills/core/orchestration-experience-pt-br/SKILL.md +112 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/go-implementation/SKILL.md +2 -0
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OPCOES_TS_DESCRITAS = void 0;
|
|
4
|
+
exports.extrairTypeScript = extrairTypeScript;
|
|
5
|
+
/** D2: opcoes fixas do compilador, descritas por nome para entrar no `config_hash`. */
|
|
6
|
+
exports.OPCOES_TS_DESCRITAS = Object.freeze({
|
|
7
|
+
allowJs: true, checkJs: false, esModuleInterop: true, jsx: 'preserve', module: 'commonjs', moduleResolution: 'node10',
|
|
8
|
+
noLib: true, resolveJsonModule: true, target: 'es2022', types: [], node_modules: 'fora-da-resolucao',
|
|
9
|
+
});
|
|
10
|
+
const EXTENSOES_JS = ['.cjs', '.js', '.jsx', '.mjs'];
|
|
11
|
+
/**
|
|
12
|
+
* B3: o especificador ESM e URL. A leitura como URL so coincide com o caminho literal quando nao ha
|
|
13
|
+
* busca nem fragmento (`?`, `#`), escape (`%`), barra invertida (vira `/`), espaco nem controle (a URL
|
|
14
|
+
* apara as pontas e descarta tab e quebra de linha) e quando o ultimo segmento e um nome (barra final,
|
|
15
|
+
* `.` e `..` apontam para pasta, que o ESM nao importa).
|
|
16
|
+
*/
|
|
17
|
+
function urlComoCaminho(especificador) {
|
|
18
|
+
for (let i = 0; i < especificador.length; i++) {
|
|
19
|
+
const c = especificador.charCodeAt(i);
|
|
20
|
+
if (c <= 0x20 || c === 0x7f || c === 0x23 || c === 0x25 || c === 0x3f || c === 0x5c)
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const ultimo = especificador.slice(especificador.lastIndexOf('/') + 1);
|
|
24
|
+
return ultimo !== '' && ultimo !== '.' && ultimo !== '..';
|
|
25
|
+
}
|
|
26
|
+
/** Texto sem surrogate solto (o `JSON.parse` aceita o escape de D800 sem par; o leitor do Node, nao). */
|
|
27
|
+
function textoBemFormado(t) {
|
|
28
|
+
for (let i = 0; i < t.length; i++) {
|
|
29
|
+
const c = t.charCodeAt(i);
|
|
30
|
+
if (c >= 0xd800 && c <= 0xdbff) {
|
|
31
|
+
const d = t.charCodeAt(i + 1);
|
|
32
|
+
if (!(d >= 0xdc00 && d <= 0xdfff))
|
|
33
|
+
return false;
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
else if (c >= 0xdc00 && c <= 0xdfff)
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
function opcoes(ts) {
|
|
42
|
+
return {
|
|
43
|
+
allowJs: true, checkJs: false, esModuleInterop: true, jsx: ts.JsxEmit.Preserve, module: ts.ModuleKind.CommonJS,
|
|
44
|
+
moduleResolution: ts.ModuleResolutionKind.Node10, noLib: true, resolveJsonModule: true, target: ts.ScriptTarget.ES2022,
|
|
45
|
+
types: [], noEmit: true, skipLibCheck: true,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const extensao = (p) => {
|
|
49
|
+
const nome = p.slice(p.lastIndexOf('/') + 1), i = nome.lastIndexOf('.');
|
|
50
|
+
return i <= 0 ? '' : nome.slice(i).toLowerCase();
|
|
51
|
+
};
|
|
52
|
+
const emNodeModules = (p) => p.split('/').includes('node_modules');
|
|
53
|
+
/** Caminho relativo a raiz de um especificador relativo; `null` se sair do repositorio. */
|
|
54
|
+
function caminhoLiteral(de, especificador) {
|
|
55
|
+
if (!especificador.startsWith('./') && !especificador.startsWith('../'))
|
|
56
|
+
return null;
|
|
57
|
+
const partes = [...de.split('/').slice(0, -1), ...especificador.split('/')], r = [];
|
|
58
|
+
for (const p of partes) {
|
|
59
|
+
if (p === '' || p === '.')
|
|
60
|
+
continue;
|
|
61
|
+
if (p === '..') {
|
|
62
|
+
if (!r.length)
|
|
63
|
+
return null;
|
|
64
|
+
r.pop();
|
|
65
|
+
}
|
|
66
|
+
else
|
|
67
|
+
r.push(p);
|
|
68
|
+
}
|
|
69
|
+
return r.join('/');
|
|
70
|
+
}
|
|
71
|
+
function criarHost(ts, e) {
|
|
72
|
+
const arquivos = new Set(e.arquivos), dirs = new Set([e.raiz]);
|
|
73
|
+
for (const p of e.arquivos) {
|
|
74
|
+
const partes = p.split('/');
|
|
75
|
+
for (let i = 1; i < partes.length; i++)
|
|
76
|
+
dirs.add(`${e.raiz}/${partes.slice(0, i).join('/')}`);
|
|
77
|
+
}
|
|
78
|
+
const dentro = (nome) => (nome.startsWith(`${e.raiz}/`) ? nome.slice(e.raiz.length + 1) : null);
|
|
79
|
+
// A resolucao de modulo nunca entra em node_modules, mesmo versionado: pacote de terceiro e externo.
|
|
80
|
+
const resolvivel = (nome) => {
|
|
81
|
+
const p = dentro(nome);
|
|
82
|
+
return p !== null && !emNodeModules(p) ? p : null;
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
getSourceFile: (nome, alvo) => {
|
|
86
|
+
const p = dentro(nome), t = p === null ? undefined : e.texto(p);
|
|
87
|
+
return t === undefined ? undefined : ts.createSourceFile(nome, t, alvo, true);
|
|
88
|
+
},
|
|
89
|
+
getDefaultLibFileName: () => `${e.raiz}/__sem-biblioteca__.d.ts`,
|
|
90
|
+
writeFile: () => undefined,
|
|
91
|
+
getCurrentDirectory: () => e.raiz,
|
|
92
|
+
getDirectories: () => [],
|
|
93
|
+
fileExists: (nome) => {
|
|
94
|
+
const p = resolvivel(nome);
|
|
95
|
+
return p !== null && arquivos.has(p);
|
|
96
|
+
},
|
|
97
|
+
readFile: (nome) => {
|
|
98
|
+
const p = resolvivel(nome);
|
|
99
|
+
return p === null || !arquivos.has(p) ? undefined : e.texto(p);
|
|
100
|
+
},
|
|
101
|
+
directoryExists: (nome) => {
|
|
102
|
+
const n = nome.length > 1 && nome.endsWith('/') ? nome.slice(0, -1) : nome, p = n === e.raiz ? '' : resolvivel(n);
|
|
103
|
+
return p !== null && dirs.has(n);
|
|
104
|
+
},
|
|
105
|
+
realpath: (p) => p,
|
|
106
|
+
getCanonicalFileName: (p) => p,
|
|
107
|
+
useCaseSensitiveFileNames: () => true,
|
|
108
|
+
getNewLine: () => '\n',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function extrairTypeScript(e, ts) {
|
|
112
|
+
const saida = { nos: [], arestas: [], diagnosticos: [], lacunas: [] };
|
|
113
|
+
if (e.fontes.length === 0)
|
|
114
|
+
return saida;
|
|
115
|
+
const absoluto = (p) => `${e.raiz}/${p}`;
|
|
116
|
+
const relativo = (p) => (p.startsWith(`${e.raiz}/`) ? p.slice(e.raiz.length + 1) : null);
|
|
117
|
+
const host = criarHost(ts, e), opts = opcoes(ts);
|
|
118
|
+
const programa = ts.createProgram(e.fontes.map((f) => absoluto(f.path)), opts, host);
|
|
119
|
+
const checker = programa.getTypeChecker();
|
|
120
|
+
const cache = ts.createModuleResolutionCache(e.raiz, (p) => p, opts);
|
|
121
|
+
const arquivos = new Set(e.arquivos);
|
|
122
|
+
const S = ts.SymbolFlags, K = ts.SyntaxKind;
|
|
123
|
+
const ehTopo = (n) => !!n.parent && ts.isSourceFile(n.parent);
|
|
124
|
+
const exportaDefault = (n) => (ts.getCombinedModifierFlags(n) & ts.ModifierFlags.Default) !== 0;
|
|
125
|
+
const variavelDoTopo = (d) => ts.isVariableDeclarationList(d.parent) && ts.isVariableStatement(d.parent.parent) && ehTopo(d.parent.parent);
|
|
126
|
+
const membroDeClasse = (n) => ts.isMethodDeclaration(n) || ts.isPropertyDeclaration(n) || ts.isGetAccessorDeclaration(n) || ts.isSetAccessorDeclaration(n)
|
|
127
|
+
|| ts.isConstructorDeclaration(n);
|
|
128
|
+
const nomeDe = (n) => n && (ts.isIdentifier(n) || ts.isPrivateIdentifier(n) || ts.isStringLiteral(n) || ts.isNumericLiteral(n)) ? n.text : null;
|
|
129
|
+
const nomeDaClasse = (c) => (c.name ? c.name.text : exportaDefault(c) ? 'default' : null);
|
|
130
|
+
/** D4: nome do simbolo de uma declaracao, antes da regra de texto do contrato. */
|
|
131
|
+
function nomeDoSimbolo(d) {
|
|
132
|
+
if (ts.isBindingElement(d)) {
|
|
133
|
+
let p = d;
|
|
134
|
+
while (ts.isBindingElement(p) || ts.isObjectBindingPattern(p) || ts.isArrayBindingPattern(p))
|
|
135
|
+
p = p.parent;
|
|
136
|
+
return ts.isVariableDeclaration(p) && variavelDoTopo(p) && ts.isIdentifier(d.name) ? d.name.text : null;
|
|
137
|
+
}
|
|
138
|
+
if (ts.isVariableDeclaration(d))
|
|
139
|
+
return variavelDoTopo(d) && ts.isIdentifier(d.name) ? d.name.text : null;
|
|
140
|
+
if (ts.isFunctionDeclaration(d))
|
|
141
|
+
return ehTopo(d) ? (d.name ? d.name.text : exportaDefault(d) ? 'default' : null) : null;
|
|
142
|
+
if (ts.isClassDeclaration(d))
|
|
143
|
+
return ehTopo(d) ? nomeDaClasse(d) : null;
|
|
144
|
+
if (ts.isInterfaceDeclaration(d) || ts.isTypeAliasDeclaration(d) || ts.isEnumDeclaration(d))
|
|
145
|
+
return ehTopo(d) ? d.name.text : null;
|
|
146
|
+
if (ts.isModuleDeclaration(d)) {
|
|
147
|
+
return ehTopo(d) && ts.isIdentifier(d.name) && (d.flags & ts.NodeFlags.GlobalAugmentation) === 0 ? d.name.text : null;
|
|
148
|
+
}
|
|
149
|
+
// `export default nome` so reexporta: o import resolve ate a declaracao de `nome`.
|
|
150
|
+
if (ts.isExportAssignment(d))
|
|
151
|
+
return ehTopo(d) && !d.isExportEquals && !ts.isIdentifier(d.expression) ? 'default' : null;
|
|
152
|
+
if (membroDeClasse(d) && ts.isClassDeclaration(d.parent) && ehTopo(d.parent)) {
|
|
153
|
+
const classe = nomeDaClasse(d.parent), membro = ts.isConstructorDeclaration(d) ? 'constructor' : nomeDe(d.name);
|
|
154
|
+
return classe !== null && membro !== null ? `${classe}.${membro}` : null;
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
/** Ref do simbolo; nome que o contrato recusaria (vazio, longo, com controle) nao vira no. */
|
|
159
|
+
const refDe = (d) => {
|
|
160
|
+
const fragment = nomeDoSimbolo(d), path = relativo(d.getSourceFile().fileName);
|
|
161
|
+
return fragment === null || path === null || !e.aceitaFragmento(fragment) ? null : { kind: 'symbol', path, fragment };
|
|
162
|
+
};
|
|
163
|
+
const semAlias = (s) => (s && s.flags & S.Alias ? checker.getAliasedSymbol(s) : s);
|
|
164
|
+
/** O alvo precisa cair num no so: declaracao fora do grafo ou em mais de um no nao liga. */
|
|
165
|
+
function alvoDo(s) {
|
|
166
|
+
const decls = s?.declarations ?? [];
|
|
167
|
+
if (decls.length === 0)
|
|
168
|
+
return { ref: null, categoria: 'chamada-nao-resolvida' };
|
|
169
|
+
const refs = new Map();
|
|
170
|
+
for (const d of decls) {
|
|
171
|
+
const r = refDe(d);
|
|
172
|
+
if (!r)
|
|
173
|
+
return { ref: null, categoria: 'chamada-alvo-fora-do-grafo' };
|
|
174
|
+
refs.set(`${r.path}\u0000${r.fragment}`, r);
|
|
175
|
+
}
|
|
176
|
+
return refs.size === 1 ? { ref: [...refs.values()][0], categoria: '' } : { ref: null, categoria: 'chamada-alvo-ambiguo' };
|
|
177
|
+
}
|
|
178
|
+
/** Resolucao do compilador, restrita ao manifesto e fora de node_modules. */
|
|
179
|
+
function resolver(especificador, de) {
|
|
180
|
+
const r = ts.resolveModuleName(especificador, absoluto(de), opts, host, cache).resolvedModule;
|
|
181
|
+
const p = r && !r.isExternalLibraryImport ? relativo(r.resolvedFileName) : null;
|
|
182
|
+
return p !== null && arquivos.has(p) && !emNodeModules(p) ? p : null;
|
|
183
|
+
}
|
|
184
|
+
const literal = (n) => n && (ts.isStringLiteral(n) || ts.isNoSubstitutionTemplateLiteral(n)) ? n.text : null;
|
|
185
|
+
const ehRequire = (n) => ts.isCallExpression(n) && ts.isIdentifier(n.expression)
|
|
186
|
+
&& n.expression.text === 'require' && n.arguments.length === 1 && !(checker.getSymbolAtLocation(n.expression)?.declarations ?? []).length;
|
|
187
|
+
const ehImportDinamico = (n) => ts.isCallExpression(n) && n.expression.kind === K.ImportKeyword;
|
|
188
|
+
/** Declaracao local que liga um nome a um modulo: import, `import = require` ou `require` atribuido. */
|
|
189
|
+
function ehImportLocal(d, sf) {
|
|
190
|
+
if (d.getSourceFile() !== sf)
|
|
191
|
+
return false;
|
|
192
|
+
if (ts.isImportSpecifier(d) || ts.isImportClause(d) || ts.isNamespaceImport(d))
|
|
193
|
+
return true;
|
|
194
|
+
if (ts.isImportEqualsDeclaration(d))
|
|
195
|
+
return ts.isExternalModuleReference(d.moduleReference);
|
|
196
|
+
let v = d;
|
|
197
|
+
while (ts.isBindingElement(v) || ts.isObjectBindingPattern(v) || ts.isArrayBindingPattern(v))
|
|
198
|
+
v = v.parent;
|
|
199
|
+
return ts.isVariableDeclaration(v) && !!v.initializer && ehRequire(v.initializer);
|
|
200
|
+
}
|
|
201
|
+
const viaImportLocal = (s, sf) => !!s && (s.flags & S.Alias) !== 0 && (s.declarations ?? []).some((d) => ehImportLocal(d, sf));
|
|
202
|
+
/** O nome importado so traz tipo: o alvo existe e nao tem valor (interface, tipo). */
|
|
203
|
+
const semValor = (nome) => {
|
|
204
|
+
const s0 = checker.getSymbolAtLocation(nome), alvo = s0 && s0.flags & S.Alias ? checker.getAliasedSymbol(s0) : s0;
|
|
205
|
+
return !!alvo && (alvo.declarations?.length ?? 0) > 0 && (alvo.flags & S.Value) === 0;
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Chave do especificador de um no de import. `t`: so traz tipo (`import type`, nomes que so sao tipo,
|
|
209
|
+
* `import('x').T`) e some na compilacao, entao segue o compilador; so vale em fonte TypeScript, porque
|
|
210
|
+
* JavaScript nao e compilado e o import roda (B-R1). `d`: `import()`, que o Node resolve como ESM.
|
|
211
|
+
* `r`: `require` literal. `v`: o resto.
|
|
212
|
+
*/
|
|
213
|
+
function chaveDoImport(n, compilado) {
|
|
214
|
+
let esp = null, tipo = 'v';
|
|
215
|
+
if (ts.isImportDeclaration(n)) {
|
|
216
|
+
esp = literal(n.moduleSpecifier);
|
|
217
|
+
const ic = n.importClause, nomes = ic?.namedBindings && ts.isNamedImports(ic.namedBindings) ? ic.namedBindings.elements : [];
|
|
218
|
+
const soTipo = compilado && !!ic && (ic.isTypeOnly || (!(ic.namedBindings && ts.isNamespaceImport(ic.namedBindings))
|
|
219
|
+
&& (!ic.name || semValor(ic.name)) && (!!ic.name || nomes.length > 0) && nomes.every((el) => el.isTypeOnly || semValor(el.name))));
|
|
220
|
+
if (soTipo)
|
|
221
|
+
tipo = 't';
|
|
222
|
+
}
|
|
223
|
+
else if (ts.isExportDeclaration(n) && n.moduleSpecifier) {
|
|
224
|
+
esp = literal(n.moduleSpecifier);
|
|
225
|
+
const nomes = n.exportClause && ts.isNamedExports(n.exportClause) ? n.exportClause.elements : null;
|
|
226
|
+
if (compilado && (n.isTypeOnly || (nomes && nomes.length > 0 && nomes.every((el) => el.isTypeOnly || semValor(el.name)))))
|
|
227
|
+
tipo = 't';
|
|
228
|
+
}
|
|
229
|
+
else if (ts.isImportEqualsDeclaration(n) && ts.isExternalModuleReference(n.moduleReference)) {
|
|
230
|
+
esp = literal(n.moduleReference.expression);
|
|
231
|
+
if (compilado && n.isTypeOnly)
|
|
232
|
+
tipo = 't';
|
|
233
|
+
}
|
|
234
|
+
else if (ts.isImportTypeNode(n) && ts.isLiteralTypeNode(n.argument)) {
|
|
235
|
+
esp = literal(n.argument.literal);
|
|
236
|
+
tipo = 't';
|
|
237
|
+
}
|
|
238
|
+
else if (ehImportDinamico(n)) {
|
|
239
|
+
esp = literal(n.arguments[0]);
|
|
240
|
+
tipo = 'd';
|
|
241
|
+
}
|
|
242
|
+
else if (ehRequire(n)) {
|
|
243
|
+
esp = literal(n.arguments[0]);
|
|
244
|
+
tipo = 'r';
|
|
245
|
+
}
|
|
246
|
+
return esp === null ? null : `${tipo}\u0000${esp}`;
|
|
247
|
+
}
|
|
248
|
+
const especificadorDaChave = (chave) => chave.slice(2);
|
|
249
|
+
/** Fonte TypeScript, que o compilador transforma; JavaScript roda como esta. */
|
|
250
|
+
const compiladoEm = (arquivo) => !EXTENSOES_JS.includes(extensao(arquivo));
|
|
251
|
+
/** Chaves de todos os imports dentro de um no. */
|
|
252
|
+
function especificadoresEm(raiz) {
|
|
253
|
+
const r = [], compilado = compiladoEm(raiz.getSourceFile().fileName);
|
|
254
|
+
const coletar = (n) => {
|
|
255
|
+
const chave = chaveDoImport(n, compilado);
|
|
256
|
+
if (chave !== null)
|
|
257
|
+
r.push(chave);
|
|
258
|
+
ts.forEachChild(n, coletar);
|
|
259
|
+
};
|
|
260
|
+
coletar(raiz);
|
|
261
|
+
return r;
|
|
262
|
+
}
|
|
263
|
+
const DECLARACOES = [['.d.ts', '.js'], ['.d.cts', '.cjs'], ['.d.mts', '.mjs']];
|
|
264
|
+
const existe = (p) => (p !== '' && arquivos.has(p) && !emNodeModules(p) ? p : null);
|
|
265
|
+
/** Implementacao ao lado de um arquivo de declaracao: e ela que roda. */
|
|
266
|
+
function implementacaoDe(declaracao) {
|
|
267
|
+
for (const [ext, impl] of DECLARACOES)
|
|
268
|
+
if (declaracao.endsWith(ext))
|
|
269
|
+
return existe(declaracao.slice(0, -ext.length) + impl);
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
const junta = (pasta, nome) => (pasta ? `${pasta}/${nome}` : nome);
|
|
273
|
+
/** JSON como o `JSON.parse` do Node o le, sem o BOM; `undefined` se nao for JSON. */
|
|
274
|
+
function lerJson(caminho) {
|
|
275
|
+
const bruto = e.texto(caminho);
|
|
276
|
+
if (bruto === undefined)
|
|
277
|
+
return undefined;
|
|
278
|
+
try {
|
|
279
|
+
return JSON.parse(bruto.charCodeAt(0) === 0xfeff ? bruto.slice(1) : bruto);
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* package.json como o Node 22 o le: BOM ignorado, raiz objeto, `name` e `type` texto sem surrogate
|
|
287
|
+
* solto quando presentes. Fora disso o Node lanca `ERR_INVALID_PACKAGE_CONFIG` e o resultado e `null`.
|
|
288
|
+
*/
|
|
289
|
+
const pacotes = new Map();
|
|
290
|
+
function lerPacote(caminho) {
|
|
291
|
+
const lido = pacotes.get(caminho);
|
|
292
|
+
if (lido !== undefined)
|
|
293
|
+
return lido;
|
|
294
|
+
let r = null;
|
|
295
|
+
const v = lerJson(caminho);
|
|
296
|
+
if (v !== null && typeof v === 'object' && !Array.isArray(v)) {
|
|
297
|
+
const o = v;
|
|
298
|
+
const texto = (k) => !(k in o) || (typeof o[k] === 'string' && textoBemFormado(o[k]));
|
|
299
|
+
if (texto('name') && texto('type'))
|
|
300
|
+
r = o;
|
|
301
|
+
}
|
|
302
|
+
pacotes.set(caminho, r);
|
|
303
|
+
return r;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Resolucao do Node para especificador relativo: exato em ESM; em CommonJS, primeiro como arquivo
|
|
307
|
+
* (exato, `.js`, `.json`, `.node`) e depois como pasta (`main` do package.json, `index`). Barra
|
|
308
|
+
* final, `.` e `..` so valem como pasta. package.json invalido faz o Node falhar; `main` absoluto ou
|
|
309
|
+
* que sai do repositorio aponta para fora do que o manifesto ve, e nao liga.
|
|
310
|
+
*/
|
|
311
|
+
function resolverNode(de, especificador, esm) {
|
|
312
|
+
const base = caminhoLiteral(de, especificador === '.' || especificador === '..' ? `./${especificador}` : especificador);
|
|
313
|
+
if (base === null)
|
|
314
|
+
return null;
|
|
315
|
+
if (esm)
|
|
316
|
+
return existe(base);
|
|
317
|
+
const comoArquivo = (p) => (p ? existe(p) ?? existe(`${p}.js`) ?? existe(`${p}.json`) ?? existe(`${p}.node`) : null);
|
|
318
|
+
const comoIndice = (p) => existe(junta(p, 'index.js')) ?? existe(junta(p, 'index.json')) ?? existe(junta(p, 'index.node'));
|
|
319
|
+
const comoPasta = (p) => {
|
|
320
|
+
const arquivoDoPacote = junta(p, 'package.json');
|
|
321
|
+
if (existe(arquivoDoPacote)) {
|
|
322
|
+
const pacote = lerPacote(arquivoDoPacote);
|
|
323
|
+
if (pacote === null)
|
|
324
|
+
return null;
|
|
325
|
+
const main = pacote.main;
|
|
326
|
+
if (typeof main === 'string' && main) {
|
|
327
|
+
// `main` e relativo a pasta, mesmo quando comeca com ponto (`.`, `..`, `.oculto.js`).
|
|
328
|
+
const m = main.startsWith('/') ? null : caminhoLiteral(arquivoDoPacote, `./${main}`);
|
|
329
|
+
if (m === null)
|
|
330
|
+
return null;
|
|
331
|
+
const r = comoArquivo(m) ?? comoIndice(m);
|
|
332
|
+
if (r !== null)
|
|
333
|
+
return r;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return comoIndice(p);
|
|
337
|
+
};
|
|
338
|
+
const soPasta = especificador.endsWith('/') || /(^|\/)\.\.?$/.test(especificador);
|
|
339
|
+
return soPasta ? comoPasta(base) : comoArquivo(base) ?? comoPasta(base);
|
|
340
|
+
}
|
|
341
|
+
/** package.json mais proximo acima do arquivo: `type` e se o Node o consegue ler. */
|
|
342
|
+
const escopos = new Map();
|
|
343
|
+
function escopoDe(arquivo) {
|
|
344
|
+
const caminho = [];
|
|
345
|
+
let dir = arquivo.includes('/') ? arquivo.slice(0, arquivo.lastIndexOf('/')) : '';
|
|
346
|
+
let r;
|
|
347
|
+
for (;;) {
|
|
348
|
+
r = escopos.get(dir);
|
|
349
|
+
if (r)
|
|
350
|
+
break;
|
|
351
|
+
caminho.push(dir);
|
|
352
|
+
const pacote = junta(dir, 'package.json');
|
|
353
|
+
if (arquivos.has(pacote)) {
|
|
354
|
+
const lido = lerPacote(pacote);
|
|
355
|
+
r = lido === null ? { invalido: true, tipo: undefined } : { invalido: false, tipo: lido.type };
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
if (dir === '') {
|
|
359
|
+
r = { invalido: false, tipo: undefined };
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
dir = dir.includes('/') ? dir.slice(0, dir.lastIndexOf('/')) : '';
|
|
363
|
+
}
|
|
364
|
+
for (const d of caminho)
|
|
365
|
+
escopos.set(d, r);
|
|
366
|
+
return r;
|
|
367
|
+
}
|
|
368
|
+
const VARIAVEIS_DO_CJS = new Set(['require', 'module', 'exports', '__filename', '__dirname']);
|
|
369
|
+
/**
|
|
370
|
+
* Sintaxe que o Node 22 so aceita em ESM: import e export estaticos, `import.meta`, `await` no topo e
|
|
371
|
+
* `let`/`const`/`class` no topo que declara variavel do CommonJS, tambem por desestruturacao. Em `.js`
|
|
372
|
+
* sem `type`, e ela que leva o Node a tentar ESM quando o CommonJS nao compila.
|
|
373
|
+
*/
|
|
374
|
+
const nomesDaLigacao = (b) => (ts.isIdentifier(b) ? [b.text]
|
|
375
|
+
: b.elements.flatMap((el) => (ts.isBindingElement(el) ? nomesDaLigacao(el.name) : [])));
|
|
376
|
+
function sintaxeEsm(sf) {
|
|
377
|
+
for (const st of sf.statements) {
|
|
378
|
+
if (ts.isImportDeclaration(st) || ts.isExportDeclaration(st) || ts.isExportAssignment(st))
|
|
379
|
+
return true;
|
|
380
|
+
if (ts.canHaveModifiers(st) && ts.getModifiers(st)?.some((m) => m.kind === K.ExportKeyword))
|
|
381
|
+
return true;
|
|
382
|
+
if (ts.isVariableStatement(st) && (st.declarationList.flags & ts.NodeFlags.BlockScoped) !== 0
|
|
383
|
+
&& st.declarationList.declarations.some((d) => nomesDaLigacao(d.name).some((x) => VARIAVEIS_DO_CJS.has(x))))
|
|
384
|
+
return true;
|
|
385
|
+
if (ts.isClassDeclaration(st) && st.name && VARIAVEIS_DO_CJS.has(st.name.text))
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
let achou = false;
|
|
389
|
+
const visitar = (n, topo) => {
|
|
390
|
+
if (achou)
|
|
391
|
+
return;
|
|
392
|
+
if ((ts.isMetaProperty(n) && n.keywordToken === K.ImportKeyword)
|
|
393
|
+
|| (topo && (ts.isAwaitExpression(n) || (ts.isForOfStatement(n) && !!n.awaitModifier)))) {
|
|
394
|
+
achou = true;
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const dentro = topo && !ts.isFunctionLike(n) && !ts.isClassStaticBlockDeclaration(n);
|
|
398
|
+
ts.forEachChild(n, (f) => visitar(f, dentro));
|
|
399
|
+
};
|
|
400
|
+
visitar(sf, true);
|
|
401
|
+
return achou;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Como o Node 22 carrega a fonte JavaScript: `esm`, `cjs` ou `falha`. O juiz de sintaxe e o V8 do Node
|
|
405
|
+
* (D16), alem do parser do TypeScript: `.mjs` so como ESM, `.cjs` so como CommonJS, `.js` pelo `type`
|
|
406
|
+
* do escopo e, sem ele, como o Node 22 detecta: CommonJS se compila, senao ESM se tem sintaxe de ESM e
|
|
407
|
+
* compila como tal. `.jsx` nao tem formato ESM no Node e so carrega por `require`, como CommonJS.
|
|
408
|
+
* Escopo invalido tambem e `falha`.
|
|
409
|
+
*/
|
|
410
|
+
const formatos = new Map();
|
|
411
|
+
{
|
|
412
|
+
// Decidido de uma vez para toda fonte JavaScript: uma chamada ao juiz, que compila o ESM em lote.
|
|
413
|
+
const regras = [];
|
|
414
|
+
for (const f of e.fontes) {
|
|
415
|
+
const ext = extensao(f.path), sf = programa.getSourceFile(absoluto(f.path));
|
|
416
|
+
if (!EXTENSOES_JS.includes(ext))
|
|
417
|
+
continue;
|
|
418
|
+
if (!sf || sf.fileName !== absoluto(f.path) || programa.getSyntacticDiagnostics(sf).length > 0) {
|
|
419
|
+
formatos.set(f.path, 'falha');
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
const escopo = ext === '.mjs' || ext === '.cjs' ? null : escopoDe(f.path);
|
|
423
|
+
const regra = (r, esmPossivel) => {
|
|
424
|
+
regras.push({ caminho: f.path, regra: r, esmPossivel });
|
|
425
|
+
};
|
|
426
|
+
if (escopo?.invalido)
|
|
427
|
+
formatos.set(f.path, 'falha');
|
|
428
|
+
else if (ext === '.mjs' || (ext === '.js' && escopo?.tipo === 'module'))
|
|
429
|
+
regra('esm', true);
|
|
430
|
+
else if (ext === '.cjs' || ext === '.jsx' || escopo?.tipo === 'commonjs')
|
|
431
|
+
regra('cjs', false);
|
|
432
|
+
else
|
|
433
|
+
regra('ambiguo', sintaxeEsm(sf));
|
|
434
|
+
}
|
|
435
|
+
const pedidos = [];
|
|
436
|
+
const indices = regras.map((r) => {
|
|
437
|
+
const texto = e.texto(r.caminho) ?? '', i = { cjs: -1, esm: -1 };
|
|
438
|
+
if (r.regra !== 'esm')
|
|
439
|
+
i.cjs = pedidos.push({ texto, formato: 'cjs' }) - 1;
|
|
440
|
+
if (r.esmPossivel)
|
|
441
|
+
i.esm = pedidos.push({ texto, formato: 'esm' }) - 1;
|
|
442
|
+
return i;
|
|
443
|
+
});
|
|
444
|
+
const aceitos = pedidos.length ? e.sintaxe(pedidos) : [];
|
|
445
|
+
const aceito = (i) => i >= 0 && aceitos[i] === true;
|
|
446
|
+
regras.forEach((r, k) => {
|
|
447
|
+
const cjs = aceito(indices[k].cjs), esm = aceito(indices[k].esm);
|
|
448
|
+
if (r.regra === 'esm')
|
|
449
|
+
formatos.set(r.caminho, esm ? 'esm' : 'falha');
|
|
450
|
+
else
|
|
451
|
+
formatos.set(r.caminho, cjs ? 'cjs' : r.regra === 'ambiguo' && esm ? 'esm' : 'falha');
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
const formatoDe = (caminho) => formatos.get(caminho) ?? 'falha';
|
|
455
|
+
/**
|
|
456
|
+
* Alvo que o Node carrega de verdade. Por `import`: `.js`, `.mjs` e `.cjs` (JSON pede atributo e
|
|
457
|
+
* extensao desconhecida falha). Por `require`: `.js` e `.cjs` em CommonJS, e `.json` que e JSON. O
|
|
458
|
+
* `.js` so carrega com o proprio escopo valido; o resto (TypeScript por remocao de tipos, `.node`, sem
|
|
459
|
+
* extensao, ESM por `require`) fica fora por nao ser provado.
|
|
460
|
+
*/
|
|
461
|
+
function carregavel(alvo, porImport) {
|
|
462
|
+
const ext = extensao(alvo);
|
|
463
|
+
if (ext === '.json')
|
|
464
|
+
return !porImport && lerJson(alvo) !== undefined;
|
|
465
|
+
if (ext !== '.js' && ext !== '.cjs' && ext !== '.mjs')
|
|
466
|
+
return false;
|
|
467
|
+
const formato = formatoDe(alvo);
|
|
468
|
+
return porImport ? formato !== 'falha' : formato === 'cjs';
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Alvo do especificador para o Node: `import()` resolve como ESM em qualquer formato; import estatico,
|
|
472
|
+
* so em ESM; `require`, so em CommonJS e com o escopo de quem importa valido (o Node le esse escopo e
|
|
473
|
+
* lanca erro). Especificador nao relativo vai a node_modules e fica sem alvo. Em ESM o especificador e
|
|
474
|
+
* URL: so liga o que a URL le como o caminho literal (`urlComoCaminho`).
|
|
475
|
+
*/
|
|
476
|
+
function alvoNoNode(de, chave) {
|
|
477
|
+
const esp = especificadorDaChave(chave), formato = formatoDe(de);
|
|
478
|
+
const relativoAoArquivo = esp === '.' || esp === '..' || esp.startsWith('./') || esp.startsWith('../');
|
|
479
|
+
if (!relativoAoArquivo || formato === 'falha')
|
|
480
|
+
return null;
|
|
481
|
+
let alvo;
|
|
482
|
+
if (chave[0] === 'd' || (chave[0] === 'v' && formato === 'esm'))
|
|
483
|
+
alvo = urlComoCaminho(esp) ? resolverNode(de, esp, true) : null;
|
|
484
|
+
else if (chave[0] === 'r' && formato === 'cjs' && !escopoDe(de).invalido)
|
|
485
|
+
alvo = resolverNode(de, esp, false);
|
|
486
|
+
else
|
|
487
|
+
return null;
|
|
488
|
+
return alvo !== null && carregavel(alvo, chave[0] !== 'r') ? alvo : null;
|
|
489
|
+
}
|
|
490
|
+
// Primeira passada, em todos os arquivos: o alvo de cada especificador para o compilador e para o
|
|
491
|
+
// runtime. Onde divergem, a aresta de import vai ao que roda e nenhum simbolo passa por ali (D3).
|
|
492
|
+
const resolucoes = new Map();
|
|
493
|
+
const divergentesGlobais = new Set();
|
|
494
|
+
for (const fonte of e.fontes) {
|
|
495
|
+
const sf = programa.getSourceFile(absoluto(fonte.path));
|
|
496
|
+
if (!sf || sf.fileName !== absoluto(fonte.path))
|
|
497
|
+
continue;
|
|
498
|
+
const ext = extensao(fonte.path), mapa = new Map();
|
|
499
|
+
for (const chave of especificadoresEm(sf)) {
|
|
500
|
+
if (mapa.has(chave))
|
|
501
|
+
continue;
|
|
502
|
+
const esp = especificadorDaChave(chave), doCompilador = resolver(esp, fonte.path);
|
|
503
|
+
let alvo = doCompilador, divergente = false;
|
|
504
|
+
// Import so de tipo nao roda: vale o que o compilador liga. Fonte JavaScript roda no Node, e onde
|
|
505
|
+
// o Node falha (formato, escopo, alvo que nao carrega) nao ha aresta.
|
|
506
|
+
if (chave[0] !== 't' && EXTENSOES_JS.includes(ext)) {
|
|
507
|
+
const runtime = alvoNoNode(fonte.path, chave);
|
|
508
|
+
if (runtime !== doCompilador) {
|
|
509
|
+
divergente = true;
|
|
510
|
+
alvo = runtime;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
const impl = chave[0] !== 't' && !divergente && doCompilador !== null ? implementacaoDe(doCompilador) : null;
|
|
514
|
+
if (impl !== null) {
|
|
515
|
+
divergente = true;
|
|
516
|
+
alvo = impl;
|
|
517
|
+
}
|
|
518
|
+
mapa.set(chave, { alvo, divergente, doCompilador });
|
|
519
|
+
if (divergente)
|
|
520
|
+
divergentesGlobais.add(`${fonte.path}\u0000${chave}`);
|
|
521
|
+
}
|
|
522
|
+
resolucoes.set(fonte.path, mapa);
|
|
523
|
+
}
|
|
524
|
+
// B-N2: modulo que importa, direto ou por outro modulo, um arquivo com import divergente pode reexportar
|
|
525
|
+
// o que o compilador liga e o runtime nao (`export *`, `module.exports = require(...)`). O fecho segue
|
|
526
|
+
// o grafo de import inteiro, e so vale para simbolo que nao e declarado no proprio salto.
|
|
527
|
+
const dependentes = new Map();
|
|
528
|
+
for (const [p, mapa] of resolucoes) {
|
|
529
|
+
for (const r of mapa.values()) {
|
|
530
|
+
for (const alvo of [r.doCompilador, r.alvo]) {
|
|
531
|
+
if (alvo === null)
|
|
532
|
+
continue;
|
|
533
|
+
let d = dependentes.get(alvo);
|
|
534
|
+
if (!d)
|
|
535
|
+
dependentes.set(alvo, (d = new Set()));
|
|
536
|
+
d.add(p);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const contaminados = new Set([...divergentesGlobais].map((k) => k.slice(0, k.indexOf('\u0000'))));
|
|
541
|
+
for (const fila = [...contaminados]; fila.length;) {
|
|
542
|
+
for (const d of dependentes.get(fila.pop()) ?? []) {
|
|
543
|
+
if (!contaminados.has(d)) {
|
|
544
|
+
contaminados.add(d);
|
|
545
|
+
fila.push(d);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* A cadeia de alias passa por um import divergente: num salto dela, ou num modulo intermediario que
|
|
551
|
+
* reexporta o que o compilador liga e o runtime nao (`module.exports = require('./a.js')`).
|
|
552
|
+
*/
|
|
553
|
+
const especificadoresDaDeclaracao = new Map();
|
|
554
|
+
function cadeiaDivergente(s, destino) {
|
|
555
|
+
const vistos = new Set();
|
|
556
|
+
for (let atual = s; atual && (atual.flags & S.Alias) !== 0 && !vistos.has(atual); atual = checker.getImmediateAliasedSymbol(atual)) {
|
|
557
|
+
vistos.add(atual);
|
|
558
|
+
for (const d of atual.declarations ?? []) {
|
|
559
|
+
const p = relativo(d.getSourceFile().fileName);
|
|
560
|
+
if (p === null)
|
|
561
|
+
continue;
|
|
562
|
+
let topo = d;
|
|
563
|
+
while (topo.parent && !ts.isSourceFile(topo.parent))
|
|
564
|
+
topo = topo.parent;
|
|
565
|
+
let esps = especificadoresDaDeclaracao.get(topo);
|
|
566
|
+
if (!esps)
|
|
567
|
+
especificadoresDaDeclaracao.set(topo, (esps = especificadoresEm(topo)));
|
|
568
|
+
for (const esp of esps) {
|
|
569
|
+
if (divergentesGlobais.has(`${p}\u0000${esp}`))
|
|
570
|
+
return true;
|
|
571
|
+
const salto = resolucoes.get(p)?.get(esp)?.doCompilador ?? null;
|
|
572
|
+
if (salto !== null && salto !== destino && contaminados.has(salto))
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
for (const fonte of e.fontes) {
|
|
580
|
+
const sf = programa.getSourceFile(absoluto(fonte.path));
|
|
581
|
+
// Fonte que o compilador trocou por outra (pacote duplicado) nao e lida: o no seria do outro arquivo.
|
|
582
|
+
if (!sf || sf.fileName !== absoluto(fonte.path)) {
|
|
583
|
+
saida.lacunas.push({ categoria: sf ? 'fonte-redirecionada' : 'fonte-nao-lida', path: fonte.path, inicio: null, detalhe: null });
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
const arquivo = { kind: 'file', path: fonte.path, fragment: null };
|
|
587
|
+
const trecho = (n, fim = n.getEnd()) => ({ path: fonte.path, inicio: n.getStart(sf), fim });
|
|
588
|
+
const aresta = (kind, from, to, t) => {
|
|
589
|
+
saida.arestas.push({ kind, from, to, extrator: e.extrator, metodo: 'ast', trecho: t });
|
|
590
|
+
};
|
|
591
|
+
const lacuna = (categoria, n, detalhe = null) => {
|
|
592
|
+
saida.lacunas.push({ categoria, path: fonte.path, inicio: n ? n.getStart(sf) : null, detalhe });
|
|
593
|
+
};
|
|
594
|
+
const diagnostico = (kind, reference) => {
|
|
595
|
+
saida.diagnosticos.push({ kind, path: fonte.path, reference: reference.replace(/\s+/g, ' '), extrator: e.extrator });
|
|
596
|
+
};
|
|
597
|
+
const alvos = resolucoes.get(fonte.path) ?? new Map();
|
|
598
|
+
// Arquivo que o compilador ligou no lugar do que roda: nenhuma chamada deste arquivo vai a ele.
|
|
599
|
+
const divergentes = new Set([...alvos.values()].filter((r) => r.divergente && r.doCompilador !== null).map((r) => r.doCompilador));
|
|
600
|
+
// AN3: classes importadas por nome aqui; `this` e `super` so ligam a membro delas em outro arquivo.
|
|
601
|
+
const classesImportadas = new Set();
|
|
602
|
+
const importada = (nome) => {
|
|
603
|
+
const s0 = nome ? checker.getSymbolAtLocation(nome) : undefined;
|
|
604
|
+
if (!s0 || (s0.flags & S.Alias) === 0)
|
|
605
|
+
return;
|
|
606
|
+
const alvo = checker.getAliasedSymbol(s0), arquivoDoAlvo = alvo.declarations?.[0] ? relativo(alvo.declarations[0].getSourceFile().fileName) : null;
|
|
607
|
+
if (cadeiaDivergente(s0, arquivoDoAlvo))
|
|
608
|
+
return;
|
|
609
|
+
if (alvo.flags & S.Class)
|
|
610
|
+
classesImportadas.add(alvo);
|
|
611
|
+
// Modulo importado inteiro (`* as ns`, `import = require`, `require` atribuido): as classes que ele exporta.
|
|
612
|
+
else if (alvo.flags & S.ValueModule) {
|
|
613
|
+
for (const x of checker.getExportsOfModule(alvo)) {
|
|
614
|
+
const c = semAlias(x), arquivoDaClasse = c?.declarations?.[0] ? relativo(c.declarations[0].getSourceFile().fileName) : null;
|
|
615
|
+
// B-N1: a classe pode chegar por reexport divergente; confere a cadeia ate o arquivo dela.
|
|
616
|
+
if (c && c.flags & S.Class && !cadeiaDivergente(s0, arquivoDaClasse))
|
|
617
|
+
classesImportadas.add(c);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
const nomesDoPadrao = (n) => {
|
|
622
|
+
if (ts.isIdentifier(n))
|
|
623
|
+
importada(n);
|
|
624
|
+
else
|
|
625
|
+
for (const el of n.elements)
|
|
626
|
+
if (ts.isBindingElement(el))
|
|
627
|
+
nomesDoPadrao(el.name);
|
|
628
|
+
};
|
|
629
|
+
const juntarImportadas = (n) => {
|
|
630
|
+
if (ts.isImportClause(n))
|
|
631
|
+
importada(n.name);
|
|
632
|
+
else if (ts.isImportSpecifier(n) || ts.isNamespaceImport(n))
|
|
633
|
+
importada(n.name);
|
|
634
|
+
else if (ts.isImportEqualsDeclaration(n) && ts.isExternalModuleReference(n.moduleReference))
|
|
635
|
+
importada(n.name);
|
|
636
|
+
else if (ts.isVariableDeclaration(n) && n.initializer && ehRequire(n.initializer))
|
|
637
|
+
nomesDoPadrao(n.name);
|
|
638
|
+
ts.forEachChild(n, juntarImportadas);
|
|
639
|
+
};
|
|
640
|
+
juntarImportadas(sf);
|
|
641
|
+
// Declaracoes de topo e membros de classe de topo (D4).
|
|
642
|
+
const declarar = (d) => {
|
|
643
|
+
const r = refDe(d);
|
|
644
|
+
if (r && r.path === fonte.path) {
|
|
645
|
+
saida.nos.push(r);
|
|
646
|
+
aresta('declares', arquivo, r, trecho(d));
|
|
647
|
+
return r;
|
|
648
|
+
}
|
|
649
|
+
if (nomeDoSimbolo(d) !== null)
|
|
650
|
+
lacuna('simbolo-recusado', d);
|
|
651
|
+
return null;
|
|
652
|
+
};
|
|
653
|
+
const elementos = (padrao, visitar) => {
|
|
654
|
+
for (const el of padrao.elements) {
|
|
655
|
+
if (!ts.isBindingElement(el))
|
|
656
|
+
continue;
|
|
657
|
+
if (ts.isIdentifier(el.name))
|
|
658
|
+
visitar(el);
|
|
659
|
+
else
|
|
660
|
+
elementos(el.name, visitar);
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
const simbolosDoTopo = new Map();
|
|
664
|
+
for (const st of sf.statements) {
|
|
665
|
+
if (ts.isVariableStatement(st)) {
|
|
666
|
+
for (const d of st.declarationList.declarations) {
|
|
667
|
+
if (ts.isIdentifier(d.name))
|
|
668
|
+
simbolosDoTopo.set(d, declarar(d));
|
|
669
|
+
else
|
|
670
|
+
elementos(d.name, (el) => { declarar(el); });
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
else if (ts.isFunctionDeclaration(st) || ts.isClassDeclaration(st) || ts.isInterfaceDeclaration(st) || ts.isTypeAliasDeclaration(st)
|
|
674
|
+
|| ts.isEnumDeclaration(st) || ts.isModuleDeclaration(st) || ts.isExportAssignment(st)) {
|
|
675
|
+
const r = declarar(st);
|
|
676
|
+
simbolosDoTopo.set(st, r);
|
|
677
|
+
if (r && ts.isClassDeclaration(st)) {
|
|
678
|
+
for (const m of st.members) {
|
|
679
|
+
const rm = refDe(m);
|
|
680
|
+
if (!rm) {
|
|
681
|
+
if (nomeDoSimbolo(m) !== null)
|
|
682
|
+
lacuna('simbolo-recusado', m);
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
saida.nos.push(rm);
|
|
686
|
+
aresta('contains', r, rm, trecho(m));
|
|
687
|
+
simbolosDoTopo.set(m, rm);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const importar = (chave, t) => {
|
|
693
|
+
const r = alvos.get(chave) ?? { alvo: null, divergente: false }, especificador = especificadorDaChave(chave);
|
|
694
|
+
if (r.alvo === null)
|
|
695
|
+
diagnostico('unresolved-import', especificador);
|
|
696
|
+
else
|
|
697
|
+
aresta('imports', arquivo, { kind: 'file', path: r.alvo, fragment: null }, t);
|
|
698
|
+
if (r.divergente)
|
|
699
|
+
saida.lacunas.push({ categoria: 'import-divergente', path: fonte.path, inicio: t.inicio, detalhe: especificador });
|
|
700
|
+
return r;
|
|
701
|
+
};
|
|
702
|
+
const simboloImportado = (nome, t) => {
|
|
703
|
+
const s0 = checker.getSymbolAtLocation(nome), { ref } = alvoDo(semAlias(s0));
|
|
704
|
+
if (ref && !divergentes.has(ref.path) && !cadeiaDivergente(s0, ref.path))
|
|
705
|
+
aresta('imports', arquivo, ref, t);
|
|
706
|
+
else
|
|
707
|
+
lacuna('import-sem-simbolo', nome);
|
|
708
|
+
};
|
|
709
|
+
/** D3: alvo de chamada pela ligacao lexica do callee, nunca pelo tipo inferido do objeto. */
|
|
710
|
+
function alvoDaChamada(callee) {
|
|
711
|
+
let r, ligado, alias;
|
|
712
|
+
if (ts.isIdentifier(callee)) {
|
|
713
|
+
alias = checker.getSymbolAtLocation(callee);
|
|
714
|
+
r = alvoDo(semAlias(alias));
|
|
715
|
+
ligado = viaImportLocal(alias, sf);
|
|
716
|
+
}
|
|
717
|
+
else if (ts.isPropertyAccessExpression(callee)) {
|
|
718
|
+
const objeto = callee.expression;
|
|
719
|
+
if (objeto.kind === K.ThisKeyword || objeto.kind === K.SuperKeyword) {
|
|
720
|
+
// Membro de outro arquivo so liga se a classe que o declara foi importada por nome aqui.
|
|
721
|
+
const membro = semAlias(checker.getSymbolAtLocation(callee.name)), classe = membro?.declarations?.[0]?.parent;
|
|
722
|
+
const simboloDaClasse = classe && ts.isClassDeclaration(classe) && classe.name ? checker.getSymbolAtLocation(classe.name) : undefined;
|
|
723
|
+
r = alvoDo(membro);
|
|
724
|
+
ligado = !!simboloDaClasse && classesImportadas.has(simboloDaClasse);
|
|
725
|
+
}
|
|
726
|
+
else if (ts.isIdentifier(objeto)) {
|
|
727
|
+
alias = checker.getSymbolAtLocation(objeto);
|
|
728
|
+
const dono = semAlias(alias);
|
|
729
|
+
if (!dono || (dono.flags & (S.Class | S.Enum | S.ValueModule | S.NamespaceModule)) === 0)
|
|
730
|
+
return { ref: null, categoria: 'chamada-por-tipo' };
|
|
731
|
+
r = alvoDo(semAlias(checker.getSymbolAtLocation(callee.name)));
|
|
732
|
+
ligado = viaImportLocal(alias, sf);
|
|
733
|
+
}
|
|
734
|
+
else
|
|
735
|
+
return { ref: null, categoria: 'chamada-por-tipo' };
|
|
736
|
+
}
|
|
737
|
+
else
|
|
738
|
+
return { ref: null, categoria: 'chamada-nao-resolvida' };
|
|
739
|
+
if (!r.ref)
|
|
740
|
+
return r;
|
|
741
|
+
if (cadeiaDivergente(alias, r.ref.path))
|
|
742
|
+
return { ref: null, categoria: 'chamada-por-import-divergente' };
|
|
743
|
+
// Alvo em outro arquivo so com ligacao deste arquivo: import local, nunca global de script.
|
|
744
|
+
if (r.ref.path !== fonte.path && !ligado)
|
|
745
|
+
return { ref: null, categoria: 'chamada-global-entre-arquivos' };
|
|
746
|
+
if (divergentes.has(r.ref.path))
|
|
747
|
+
return { ref: null, categoria: 'chamada-por-import-divergente' };
|
|
748
|
+
return r;
|
|
749
|
+
}
|
|
750
|
+
const chamada = (n, atual) => {
|
|
751
|
+
if (!atual) {
|
|
752
|
+
lacuna('chamada-fora-de-simbolo', n);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const { ref, categoria } = alvoDaChamada(n.expression);
|
|
756
|
+
if (!ref) {
|
|
757
|
+
const listada = categoria === 'chamada-alvo-ambiguo' || categoria === 'chamada-global-entre-arquivos' || categoria === 'chamada-por-import-divergente';
|
|
758
|
+
lacuna(categoria, n, listada ? n.expression.getText(sf) : null);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
// O trecho vai do inicio da chamada ao parentese de abertura: `soma(`, `new Classe(`.
|
|
762
|
+
const fim = n.arguments ? n.arguments.pos : n.expression.getEnd();
|
|
763
|
+
aresta('calls', atual, ref, trecho(n, fim));
|
|
764
|
+
};
|
|
765
|
+
const compilado = compiladoEm(fonte.path);
|
|
766
|
+
const visitar = (n, atual) => {
|
|
767
|
+
const proximo = simbolosDoTopo.has(n) ? (simbolosDoTopo.get(n) ?? null) : atual;
|
|
768
|
+
const chave = chaveDoImport(n, compilado);
|
|
769
|
+
if (ts.isImportDeclaration(n)) {
|
|
770
|
+
const r = chave === null ? null : importar(chave, trecho(n));
|
|
771
|
+
if (r && r.alvo !== null && !r.divergente && n.importClause) {
|
|
772
|
+
const ic = n.importClause;
|
|
773
|
+
if (ic.name)
|
|
774
|
+
simboloImportado(ic.name, trecho(ic.name));
|
|
775
|
+
if (ic.namedBindings && ts.isNamedImports(ic.namedBindings))
|
|
776
|
+
for (const el of ic.namedBindings.elements)
|
|
777
|
+
simboloImportado(el.name, trecho(el));
|
|
778
|
+
}
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (ts.isExportDeclaration(n) && n.moduleSpecifier) {
|
|
782
|
+
const r = chave === null ? null : importar(chave, trecho(n));
|
|
783
|
+
if (r && r.alvo !== null && !r.divergente && n.exportClause && ts.isNamedExports(n.exportClause)) {
|
|
784
|
+
for (const el of n.exportClause.elements)
|
|
785
|
+
simboloImportado(el.name, trecho(el));
|
|
786
|
+
}
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (ts.isImportEqualsDeclaration(n) && ts.isExternalModuleReference(n.moduleReference)) {
|
|
790
|
+
if (chave !== null)
|
|
791
|
+
importar(chave, trecho(n));
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
if (ts.isImportTypeNode(n) && chave !== null)
|
|
795
|
+
importar(chave, trecho(n));
|
|
796
|
+
if (ehRequire(n) || ehImportDinamico(n)) {
|
|
797
|
+
if (chave !== null)
|
|
798
|
+
importar(chave, trecho(n));
|
|
799
|
+
else
|
|
800
|
+
diagnostico('dynamic-resolution', n.arguments[0] ? n.arguments[0].getText(sf) : '');
|
|
801
|
+
}
|
|
802
|
+
else if (ts.isCallExpression(n) || ts.isNewExpression(n))
|
|
803
|
+
chamada(n, proximo);
|
|
804
|
+
ts.forEachChild(n, (filho) => visitar(filho, proximo));
|
|
805
|
+
};
|
|
806
|
+
for (const st of sf.statements)
|
|
807
|
+
visitar(st, null);
|
|
808
|
+
}
|
|
809
|
+
return saida;
|
|
810
|
+
}
|