Skip to content

Commit c1ccffe

Browse files
committed
test(compat-eslint): cover --debug-estree counter behaviour
The counter shipped in 1a9c5a3 had no direct tests β€” the visible output was only inspected by hand against tsslint dogfood + Dify. Pin the four invariants via spawned child processes (env var is read at module load, so toggling in-process isn't workable): - off-path: TSSLINT_DEBUG_ESTREE unset β†’ counter stays empty (boolean-check-only fast path; no allocations) - on-path: counter populates with the expected node types (Program / JSXOpeningElement / JSXAttribute) and notably does NOT contain 'TSJsxAttributes' β€” pins the parent-skip invariant from 5a9e582 from the counter side too - globalThis sharing: a second require() (separate module instance, mimicking project vs CLI module-resolution split) sees the same populated map via Symbol.for(...) global key - resetNodeTypeCounts(): clears the map between sessions Each child driver triggers a visitor-keys walk over the produced ESTree so getters fire (otherwise convertLazy alone only counts the eager Program node). Counts are read on the next microtask so deferred queueMicrotask bumps in LazyNode's ctor have landed.
1 parent 5a9e582 commit c1ccffe

1 file changed

Lines changed: 123 additions & 0 deletions

File tree

β€Žpackages/compat-eslint/test/lazy-estree.test.tsβ€Ž

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,129 @@ function findTsJsxExpr(sf: ts.SourceFile, attrName?: string): ts.JsxExpression |
13801380
check('saw JSXSpreadAttribute (sanity)', seenTypes.has('JSXSpreadAttribute'));
13811381
}
13821382

1383+
// --- Debug-estree counter (env-gated globalThis instrumentation) -------
1384+
//
1385+
// Counter is gated by env TSSLINT_DEBUG_ESTREE=1 (read at module load).
1386+
// To exercise both the off and on paths cleanly, spawn a child Node
1387+
// process with the desired env, run a small inline script that builds a
1388+
// LazyNode, and print the resulting counter via getNodeTypeCounts().
1389+
1390+
const { spawnSync } = require('child_process') as typeof import('child_process');
1391+
const repoCompatEslint = require.resolve('../index.js');
1392+
1393+
function runChild(env: NodeJS.ProcessEnv, code: string): { stdout: string; stderr: string; status: number } {
1394+
const r = spawnSync(process.execPath, ['-e', code], {
1395+
env: { ...process.env, ...env },
1396+
encoding: 'utf8',
1397+
});
1398+
return { stdout: r.stdout || '', stderr: r.stderr || '', status: r.status ?? -1 };
1399+
}
1400+
1401+
// Inline driver: load lazy, build the source file, force materialisation
1402+
// of the JSX subtree by reading getters (otherwise the counter only sees
1403+
// the eager Program node), then read getNodeTypeCounts on the next
1404+
// microtask so the deferred queueMicrotask bumps have landed.
1405+
const childCommon = `
1406+
const ts = require('typescript');
1407+
const lazy = require(${JSON.stringify(repoCompatEslint.replace(/index\.js$/, 'lib/lazy-estree.js'))});
1408+
const visitorKeys = require(${JSON.stringify(repoCompatEslint.replace(/index\.js$/, 'lib/visitor-keys.js'))}).visitorKeys;
1409+
const sf = ts.createSourceFile('/t.tsx', 'let _ = <Foo a={x} b={y} />;', ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
1410+
const { estree } = lazy.convertLazy(sf);
1411+
const seen = new WeakSet();
1412+
function walk(n) {
1413+
if (n == null || typeof n !== 'object' || seen.has(n)) return;
1414+
seen.add(n);
1415+
const keys = visitorKeys[n.type];
1416+
if (!keys) return;
1417+
for (const k of keys) {
1418+
const v = n[k];
1419+
if (Array.isArray(v)) for (const x of v) walk(x);
1420+
else walk(v);
1421+
}
1422+
}
1423+
walk(estree);
1424+
Promise.resolve().then(() => {
1425+
const counts = lazy.getNodeTypeCounts();
1426+
process.stdout.write(JSON.stringify([...counts.entries()].sort()) + '\\n');
1427+
});
1428+
`;
1429+
1430+
// ── 8: env unset β†’ counter empty ───────────────────────────────────────
1431+
{
1432+
const r = runChild({ TSSLINT_DEBUG_ESTREE: '' }, childCommon);
1433+
check('off-path: child exited 0', r.status === 0, r.stderr);
1434+
check('off-path: counter empty', r.stdout.trim() === '[]', `got: ${r.stdout.trim()}`);
1435+
}
1436+
1437+
// ── 9: env set β†’ counter populated ─────────────────────────────────────
1438+
{
1439+
const r = runChild({ TSSLINT_DEBUG_ESTREE: '1' }, childCommon);
1440+
check('on-path: child exited 0', r.status === 0, r.stderr);
1441+
const parsed = JSON.parse(r.stdout.trim()) as Array<[string, number]>;
1442+
const map = new Map(parsed);
1443+
check('on-path: counter has Program', (map.get('Program') ?? 0) >= 1);
1444+
check('on-path: counter has JSXOpeningElement', (map.get('JSXOpeningElement') ?? 0) >= 1);
1445+
check('on-path: counter has JSXAttribute', (map.get('JSXAttribute') ?? 0) >= 1);
1446+
check(
1447+
'on-path: counter has NO TSJsxAttributes (skip works)',
1448+
!map.has('TSJsxAttributes'),
1449+
`got TS-prefixed: ${parsed.filter(([t]) => t.startsWith('TS')).map(([t, n]) => `${t}=${n}`).join(', ')}`,
1450+
);
1451+
}
1452+
1453+
// ── 10: globalThis-shared counter β€” second require() instance sees it ──
1454+
{
1455+
const childTwoInstances = `
1456+
const ts = require('typescript');
1457+
// Load lazy-estree TWICE via different require paths to mimic the
1458+
// CLI-vs-project module-resolution split. delete from cache between
1459+
// to force a fresh module instance, then check that counts populated by
1460+
// the first instance are visible from the second's getNodeTypeCounts().
1461+
const path1 = ${JSON.stringify(repoCompatEslint.replace(/index\.js$/, 'lib/lazy-estree.js'))};
1462+
const lazy1 = require(path1);
1463+
const sf = ts.createSourceFile('/t.tsx', 'let _ = <Foo a={x} />;', ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
1464+
lazy1.convertLazy(sf);
1465+
delete require.cache[require.resolve(path1)];
1466+
const lazy2 = require(path1);
1467+
Promise.resolve().then(() => {
1468+
const c1 = lazy1.getNodeTypeCounts();
1469+
const c2 = lazy2.getNodeTypeCounts();
1470+
// Same Map instance backing both β€” both should report identical entries.
1471+
process.stdout.write(JSON.stringify({
1472+
sameSize: c1.size === c2.size,
1473+
sameProgramCount: c1.get('Program') === c2.get('Program'),
1474+
nonEmpty: c2.size > 0,
1475+
}) + '\\n');
1476+
});
1477+
`;
1478+
const r = runChild({ TSSLINT_DEBUG_ESTREE: '1' }, childTwoInstances);
1479+
check('shared-counter: child exited 0', r.status === 0, r.stderr);
1480+
const parsed = JSON.parse(r.stdout.trim()) as { sameSize: boolean; sameProgramCount: boolean; nonEmpty: boolean };
1481+
check('shared-counter: second instance sees populated counter', parsed.nonEmpty);
1482+
check('shared-counter: both instances see same map size', parsed.sameSize);
1483+
check('shared-counter: both instances see same Program count', parsed.sameProgramCount);
1484+
}
1485+
1486+
// ── 11: resetNodeTypeCounts() clears the counter ───────────────────────
1487+
{
1488+
const code = `
1489+
const ts = require('typescript');
1490+
const lazy = require(${JSON.stringify(repoCompatEslint.replace(/index\.js$/, 'lib/lazy-estree.js'))});
1491+
lazy.convertLazy(ts.createSourceFile('/t.tsx', 'let _ = <Foo a={x} />;', ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX));
1492+
Promise.resolve().then(() => {
1493+
const before = lazy.getNodeTypeCounts().size;
1494+
lazy.resetNodeTypeCounts();
1495+
const after = lazy.getNodeTypeCounts().size;
1496+
process.stdout.write(JSON.stringify({ before, after }) + '\\n');
1497+
});
1498+
`;
1499+
const r = runChild({ TSSLINT_DEBUG_ESTREE: '1' }, code);
1500+
check('reset: child exited 0', r.status === 0, r.stderr);
1501+
const { before, after } = JSON.parse(r.stdout.trim());
1502+
check('reset: before non-empty', before > 0);
1503+
check('reset: after empty', after === 0);
1504+
}
1505+
13831506
// --- Done ---------------------------------------------------------------
13841507

13851508
console.log(`\n${failures.length === 0 ? 'all pass' : `${failures.length} FAILED`}`);

0 commit comments

Comments
Β (0)