/** * detectFrontendMode — source monorepo vs client package detection. * * The /documentation scaffold-doc CLI generates DIFFERENT artifacts per mode * (in-place TSX wiring vs PageRegistry docs.* registry) — a wrong verdict * ships an orphan doc page, so the detection contract is pinned here. */ import { describe, it, expect, beforeEach, afterEach } from 'vitest' import fs from 'node:fs' import os from 'node:os' import path from 'node:path' import { detectFrontendMode, SMARTSTACK_WEB_PACKAGE } from '../detector.js' let root: string beforeEach(() => { root = fs.mkdtempSync(path.join(os.tmpdir(), 'fmode-')) }) afterEach(() => { fs.rmSync(root, { recursive: true, force: true }) }) function write(rel: string, content: string): void { const abs = path.join(root, rel) fs.mkdirSync(path.dirname(abs), { recursive: true }) fs.writeFileSync(abs, content) } describe('detectFrontendMode', () => { it('detects a client project from the @atlashub/smartstack dependency (with version)', async () => { write( 'package.json', JSON.stringify({ name: 'gesa-web', dependencies: { [SMARTSTACK_WEB_PACKAGE]: '3.62.0', react: '^19.0.0' } }), ) const info = await detectFrontendMode(root) expect(info.mode).toBe('client') expect(info.packageVersion).toBe('3.62.0') }) it('detects the source monorepo from the package name', async () => { write('package.json', JSON.stringify({ name: SMARTSTACK_WEB_PACKAGE, version: '3.62.0' })) const info = await detectFrontendMode(root) expect(info.mode).toBe('source') expect(info.packageVersion).toBeNull() }) it('the package.json dependency wins even without client file markers', async () => { // No src/extensions/componentRegistry.generated.ts — the dependency alone decides. write('package.json', JSON.stringify({ name: 'x-web', devDependencies: { [SMARTSTACK_WEB_PACKAGE]: '3.60.0' } })) const info = await detectFrontendMode(root) expect(info.mode).toBe('client') expect(info.packageVersion).toBe('3.60.0') }) it('falls back to the source file markers when package.json is inconclusive', async () => { write('package.json', JSON.stringify({ name: 'weird-fork' })) write('src/i18n/config.ts', 'export {}') write('src/components/routing/DocRoutes.tsx', 'export {}') const info = await detectFrontendMode(root) expect(info.mode).toBe('source') }) it('falls back to the client marker when package.json is inconclusive', async () => { write('package.json', JSON.stringify({ name: 'weird-fork' })) write('src/extensions/componentRegistry.generated.ts', '// stub') const info = await detectFrontendMode(root) expect(info.mode).toBe('client') expect(info.packageVersion).toBeNull() }) it('a single source marker is NOT enough (both are required)', async () => { write('package.json', JSON.stringify({ name: 'weird-fork' })) write('src/i18n/config.ts', 'export {}') const info = await detectFrontendMode(root) expect(info.mode).toBe('unknown') }) it('returns unknown with evidence when nothing is conclusive', async () => { const info = await detectFrontendMode(root) expect(info.mode).toBe('unknown') expect(info.packageVersion).toBeNull() expect(info.evidence.length).toBeGreaterThan(0) }) it('survives an unreadable package.json (falls through to markers)', async () => { write('package.json', '{ not json') write('src/extensions/componentRegistry.generated.ts', '// stub') const info = await detectFrontendMode(root) expect(info.mode).toBe('client') }) })