/** * scaffold-doc test fixtures — minimal on-disk projects for BOTH frontend * deployment modes. * * The historical test blind spot: the suite only ever ran against the source * monorepo shape, so the client-project degradation (wiring targets absent → * silent `file: null` → orphan doc pages) was invisible. These builders mirror * what `ss init` actually emits (client) and what the SmartStack.app monorepo * carries (source), so both code paths are exercised at their REAL shape. */ import fs from 'node:fs' import path from 'node:path' import type { ScaffoldDocInput } from '../types.js' export const CLIENT_WEB = ['web', 'testapp-web'] export const SOURCE_WEB = ['web', 'smartstack-web'] export const I18N: NonNullable = { fr: { title: 'Affaires', summary: 'Suivi des affaires', objective: 'Gérer les affaires.', sections: { objective: '1. Objectif' } }, en: { title: 'Cases' }, de: { title: 'Fälle' }, it: { title: 'Pratiche' }, } export function write(root: string, rel: string, content: string): void { const abs = path.join(root, rel) fs.mkdirSync(path.dirname(abs), { recursive: true }) fs.writeFileSync(abs, content) } export function read(root: string, rel: string): string { return fs.readFileSync(path.join(root, rel), 'utf-8') } export function exists(root: string, rel: string): boolean { return fs.existsSync(path.join(root, rel)) } /** * A generated CLIENT project (what `ss init` emits): the web package depends * on @atlashub/smartstack, routing goes through src/extensions registries. */ export function makeClientProject( root: string, opts: { withPage?: boolean; withExtensions?: boolean; withDependency?: boolean } = {}, ): string { const { withPage = true, withExtensions = true, withDependency = true } = opts const web = path.join(root, ...CLIENT_WEB) write( root, path.join(...CLIENT_WEB, 'package.json'), JSON.stringify({ name: 'testapp-web', version: '0.1.0', dependencies: withDependency ? { '@atlashub/smartstack': '3.62.0', react: '^19.0.0' } : { react: '^19.0.0' }, }), ) if (withExtensions) { write(root, path.join(...CLIENT_WEB, 'src', 'extensions', 'componentRegistry.generated.ts'), '// stub\n') } if (withPage) { write( root, path.join(...CLIENT_WEB, 'src', 'pages', 'docs', 'business', 'gaf', 'affaires', 'index.tsx'), `import { useTranslation } from 'react-i18next';\nexport default function GafAffairesDocPage() { const { t } = useTranslation('docs-gaf-affaires'); return

{t('title')}

; }\n`, ) } return web } /** * The SmartStack.app SOURCE monorepo shape: docs subsystem files in-tree. * `without` removes individual wiring targets to exercise the loud failures. */ export function makeSourceProject(root: string, opts: { without?: string[] } = {}): string { const skip = new Set(opts.without ?? []) const web = path.join(root, ...SOURCE_WEB) write(root, path.join(...SOURCE_WEB, 'package.json'), JSON.stringify({ name: '@atlashub/smartstack', version: '3.62.0' })) const targets: Record = { config: [path.join('src', 'i18n', 'config.ts'), `export const ns: string[] = [\n 'common',\n];\n`], docRoutes: [ path.join('src', 'components', 'routing', 'DocRoutes.tsx'), `// }>\nexport function DocRoutes() { return null; }\n`, ], docPanel: [ path.join('src', 'contexts', 'DocPanelContext.tsx'), `const docMapping: Record = {\n};\nexport { docMapping };\n`, ], userIndex: [ path.join('src', 'pages', 'system', 'docs', 'user', 'UserIndexPage.tsx'), `const apps = [{ modules: [\n] }];\nexport default function UserIndexPage() { return null; }\n`, ], manifest: [path.join('src', 'pages', 'docs', 'docs-manifest.json'), JSON.stringify({ modules: [] })], } for (const [key, [rel, content]] of Object.entries(targets)) { if (!skip.has(key)) write(root, path.join(...SOURCE_WEB, rel), content) } return web } /** A fully-defaulted client spec (as validate() would emit). */ export function clientSpec(root: string, overrides: Partial = {}): ScaffoldDocInput { return { type: 'user', target: 'affaires', application: 'gaf', namespace: 'docs-gaf-affaires', routePath: 'gaf/affaires', componentName: 'GafAffairesDocPage', pageImportPath: '@/pages/docs/business/gaf/affaires', i18n: I18N, manifest: { id: 'gaf-affaires', name: 'Affaires', description: 'Suivi des affaires', version: '1.0.0', deferredLanguages: [] }, docMappings: [], appDocDefault: false, mode: 'auto', projectPath: root, dryRun: false, ...overrides, } } /** A fully-defaulted source spec (as validate() would emit). */ export function sourceSpec(root: string, overrides: Partial = {}): ScaffoldDocInput { return { type: 'user', target: 'users', application: 'administration', namespace: 'docsAdministrationUsers', routePath: 'administration/users', componentName: 'UsersDocPage', pageImportPath: '@/pages/docs/business/platform/administration/users', i18n: I18N, manifest: { id: 'users', name: 'Users', description: '', version: '1.0.0', deferredLanguages: [] }, docMappings: ['administration/users', 'administration/users/list'], appDocDefault: false, mode: 'auto', projectPath: root, dryRun: false, ...overrides, } }