|
|
|
|
@ -112,6 +112,22 @@ function collectDataAttrs(source: string): Set<string> {
|
|
|
|
|
return attrs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toPascalName(kebab: string): string {
|
|
|
|
|
return kebab
|
|
|
|
|
.split('-')
|
|
|
|
|
.map((segment) => `${segment.charAt(0).toUpperCase()}${segment.slice(1)}`)
|
|
|
|
|
.join('');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function collectPublicSomaComponentDirs(): string[] {
|
|
|
|
|
const root = join(HERE, 'soma', 'components');
|
|
|
|
|
return readdirSync(root, { withFileTypes: true })
|
|
|
|
|
.filter((entry) => entry.isDirectory())
|
|
|
|
|
.map((entry) => entry.name)
|
|
|
|
|
.filter((name) => name !== 'internal' && existsSync(join(root, name, 'index.ts')))
|
|
|
|
|
.sort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function collectComponentLangPathViolations(source: string, file: string): string[] {
|
|
|
|
|
const clean = stripComments(source);
|
|
|
|
|
const violations: string[] = [];
|
|
|
|
|
@ -276,6 +292,20 @@ describe('UIX layer contracts', () => {
|
|
|
|
|
expect(violations).toEqual([]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('guards Soma component barrel exports all public component modules', () => {
|
|
|
|
|
const indexSource = readFileSync(join(HERE, 'soma', 'components', 'index.ts'), 'utf8');
|
|
|
|
|
const missingExports = collectPublicSomaComponentDirs()
|
|
|
|
|
.map((dir) => ({
|
|
|
|
|
dir,
|
|
|
|
|
name: toPascalName(dir),
|
|
|
|
|
exportRe: new RegExp(`export \\* as ${toPascalName(dir)} from ['"]\\./${dir}['"];`)
|
|
|
|
|
}))
|
|
|
|
|
.filter(({ exportRe }) => !exportRe.test(indexSource))
|
|
|
|
|
.map(({ dir, name }) => `${dir} -> ${name}`);
|
|
|
|
|
|
|
|
|
|
expect(missingExports).toEqual([]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('guards Soma from direct mutable DOM writes', () => {
|
|
|
|
|
const violations = grepSources(
|
|
|
|
|
join(HERE, 'soma'),
|
|
|
|
|
|