import { test } from 'brittle'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'
import { walkGraph } from '../lib/traverse.js'
const HERE = dirname(fileURLToPath(import.meta.url))
// Was `resolve(HERE, '../../')` — the live hyper-fleeky tree. A sibling repo's
// version bump turned two assertions here red (invariant I1). fixtures/workspace
// is ours and cannot drift; see its README for what stands in for what.
const FIXTURE = resolve(HERE, 'fixtures/workspace')
test('traverse: a single module', async (t) => {
const graph = await walkGraph(resolve(FIXTURE, 'widget-store'))
t.is(graph.modules.size, 1, '1 module found')
t.ok(graph.modules.has('widget-store'), 'found widget-store')
const mod = graph.modules.get('widget-store')
t.is(mod.version, '0.2.0', 'correct version')
t.ok(mod.entry.endsWith('index.js'), 'entry is index.js')
t.ok(mod.dependencies['widget-rooms'], 'depends on widget-rooms')
t.is(mod.testCommand, 'brittle test/*.test.js', 'test command')
t.is(mod.type, 'module', 'ESM module')
// Files
t.ok(mod.files.has('index.js'), 'has index.js')
t.ok(mod.files.has('lib/widget-store.js'), 'has lib/widget-store.js')
t.ok(mod.files.has('lib/schema.js'), 'has lib/schema.js')
t.ok(mod.files.has('lib/handlers/widgets.js'), 'walks nested directories')
t.ok(mod.files.has('test/store.test.js'), 'includes the test file')
// File is parsed (source no longer retained — see TreeReducer.onFileDiscovered)
const indexFile = mod.files.get('index.js')
t.ok(indexFile.parsed, 'index.js has parsed result')
t.is(indexFile.parsed.exports.length, 4, 'index.js has its 4 exports')
})
test('traverse: file relationships', async (t) => {
const graph = await walkGraph(resolve(FIXTURE, 'widget-store'))
const mod = graph.modules.get('widget-store')
// The barrel imports from lib/
const indexFile = mod.files.get('index.js')
t.alike(indexFile.localImports.map(i => i.specifier).sort(),
['./lib/schema.js', './lib/widget-store.js'], 'barrel resolves both lib imports')
// lib/widget-store.js imports schema.js — a relative edge one level down
const mainFile = mod.files.get('lib/widget-store.js')
t.alike(mainFile.localImports.map(i => i.specifier), ['./schema.js'],
'widget-store.js imports schema.js')
// 'widget-rooms' is a package, not a local edge, so it is NOT a localImport
t.absent(mainFile.localImports.find(i => i.specifier === 'widget-rooms'),
'a bare specifier is not a local import')
})
test('traverse: import maps', async (t) => {
const graph = await walkGraph(resolve(FIXTURE, 'mapped-io'))
const mod = graph.modules.get('mapped-io')
t.ok(mod, 'found mapped-io')
t.ok(mod.importMaps, 'has import maps')
t.alike(Object.keys(mod.importMaps).sort(), ['fs', 'os', 'path'], 'all three maps')
t.is(mod.importMaps.fs.bare, 'bare-fs', 'fs maps to bare-fs under Bare')
t.is(mod.importMaps.fs.default, 'fs', 'and to fs by default')
})
test('traverse: multi-module scan', async (t) => {
const graph = await walkGraph(FIXTURE, { maxDepth: 1 })
// Deterministic now: the old version could only assert "more than five",
// because it was counting whatever happened to be checked out.
t.is(graph.modules.size, 4, '4 modules')
t.alike(Array.from(graph.modules.keys()).sort(),
['legacy-sync', 'mapped-io', 'widget-rooms', 'widget-store'], 'the expected set')
for (const [name, mod] of graph.modules) {
t.ok(mod.files.size > 0, name + ' has ' + mod.files.size + ' files')
}
})
test('traverse: a CJS module', async (t) => {
const graph = await walkGraph(resolve(FIXTURE, 'legacy-sync'))
const mod = graph.modules.get('legacy-sync')
t.ok(mod, 'found legacy-sync')
t.is(mod.type, 'commonjs', 'detected as commonjs (no type field)')
t.ok(mod.files.has('index.js'), 'has index.js')
})