Files
Investidor-V2/tools/portal-audit-consumers.cjs
nala 6ddafc95ba
tests / ci (push) Canceled after 0s
oportunidades 4
2026-10-10 07:03:59 +02:00

146 lines
6.0 KiB
JavaScript

// Evidence inventory, not a second requirements matrix. Static bindings are not acceptance.
const fs = require('node:fs');
const path = require('node:path');
const ts = require('typescript');
const root = path.resolve(__dirname, '..');
const rows = [];
const controls = [];
function walk(directory) {
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
const file = path.join(directory, entry.name);
if (entry.isDirectory()) walk(file);
else if (entry.name.endsWith('.tsx')) {
const source = fs.readFileSync(file, 'utf8');
const tree = ts.createSourceFile(
file,
source,
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TSX,
);
function visit(node) {
const opening = ts.isJsxElement(node)
? node.openingElement
: ts.isJsxSelfClosingElement(node)
? node
: null;
if (opening) {
const tag = opening.tagName.getText(tree);
const attributes = opening.attributes.properties;
const names = attributes.map((a) => a.name?.getText(tree));
const base = {
file: path.relative(root, file).replace(/\\/g, '/'),
line:
tree.getLineAndCharacterOfPosition(
node.getStart(tree),
).line + 1,
component: tag,
};
if (tag === 'button' || tag === 'Button') {
let form = null;
for (
let parent = node.parent;
parent;
parent = parent.parent
) {
if (
ts.isJsxElement(parent) &&
['form', 'Form'].includes(
parent.openingElement.tagName.getText(tree),
)
) {
form = parent.openingElement;
break;
}
}
const formNames =
form?.attributes.properties.map((a) =>
a.name?.getText(tree),
) ?? [];
rows.push({
...base,
label: ts.isJsxElement(node)
? node.children
.map((c) => c.getText(tree))
.join(' ')
.replace(/\s+/g, ' ')
.slice(0, 180)
: '',
binding: names.some((n) =>
['onClick', 'onSelect'].includes(n),
)
? 'direct_handler'
: form && form.tagName.getText(tree) === 'Form'
? 'inertia_form'
: formNames.includes('onSubmit')
? 'form_handler'
: form &&
(formNames.includes('action') ||
formNames.includes('method'))
? 'native_form_action'
: names.includes('asChild')
? 'delegated_child'
: 'context_or_handler_review',
functional_demonstration:
'Not inferred from static binding; use per-journey QA evidence and visual walkthrough',
});
}
if (
tag === 'select' ||
/CountrySelect|ProvinceSelect|TerritoryFields|TerritoryLevel|TerritoryField|MapPanel|InvestmentMap|Geography/.test(
tag,
)
) {
controls.push({
...base,
attributes: attributes
.map((a) => a.getText(tree))
.join(' ')
.slice(0, 700),
visual_verified: false,
});
}
}
ts.forEachChild(node, visit);
}
visit(tree);
}
}
}
walk(path.join(root, 'resources/js/pages'));
walk(path.join(root, 'resources/js/components'));
const file = path.join(
root,
'storage/app/private/investmoz-v2/portal-audit/consumer-actions-lote2.json',
);
fs.writeFileSync(
file,
JSON.stringify(
{
captured_at: new Date().toISOString(),
scope: 'All TSX pages/components; inventory of source controls and action bindings, not rendered DOM or functional completion',
buttons: rows,
selectors_and_maps: controls,
counts: {
buttons: rows.length,
selectors_and_maps: controls.length,
binding_review: rows.filter(
(r) => r.binding === 'context_or_handler_review',
).length,
},
visual_verified: false,
},
null,
2,
),
);
console.log(
JSON.stringify({
buttons: rows.length,
selectors_and_maps: controls.length,
review: rows
.filter((r) => r.binding === 'context_or_handler_review')
.map(({ file, line, label }) => ({ file, line, label })),
}),
);