|
|
|
|
@ -29,6 +29,7 @@ import { EngineSemantic, defineEngineSemantic } from './sema';
|
|
|
|
|
import { SemaConfigError } from './sema/errors';
|
|
|
|
|
import { createSomaRuntime } from './soma/runtime.svelte';
|
|
|
|
|
import { UIX_LAYER_CONTRACTS } from './contracts';
|
|
|
|
|
import { componentLangs } from './langs';
|
|
|
|
|
|
|
|
|
|
const minimalLang = { schema: {} satisfies LangNode, defaultLocale: 'es' as const };
|
|
|
|
|
const prefsProjectionSchema = standardPrefsDimensions({
|
|
|
|
|
@ -111,6 +112,36 @@ function collectDataAttrs(source: string): Set<string> {
|
|
|
|
|
return attrs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function collectComponentLangPathViolations(source: string, file: string): string[] {
|
|
|
|
|
const clean = stripComments(source);
|
|
|
|
|
const violations: string[] = [];
|
|
|
|
|
const refRe = /#\?components\.([^|'"`\s]+)/g;
|
|
|
|
|
let match: RegExpExecArray | null;
|
|
|
|
|
|
|
|
|
|
while ((match = refRe.exec(clean))) {
|
|
|
|
|
const path = match[1];
|
|
|
|
|
const segments = path.split('.');
|
|
|
|
|
if (segments.some((segment) => /[A-Z]/.test(segment))) {
|
|
|
|
|
violations.push(`${file.replace(REPO_ROOT, '')}: components.${path}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return violations;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function collectLangCatalogKeyViolations(
|
|
|
|
|
node: unknown,
|
|
|
|
|
prefix = 'components'
|
|
|
|
|
): string[] {
|
|
|
|
|
if (!node || typeof node !== 'object') return [];
|
|
|
|
|
|
|
|
|
|
return Object.entries(node as Record<string, unknown>).flatMap(([key, value]) => {
|
|
|
|
|
const path = `${prefix}.${key}`;
|
|
|
|
|
const ownViolation = /[A-Z]/.test(key) ? [path] : [];
|
|
|
|
|
return [...ownViolation, ...collectLangCatalogKeyViolations(value, path)];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('UIX layer contracts', () => {
|
|
|
|
|
it('pins ActiveUix public service names and rejects legacy aliases', () => {
|
|
|
|
|
const uix = createActiveUix({ langs: minimalLang });
|
|
|
|
|
@ -266,6 +297,21 @@ describe('UIX layer contracts', () => {
|
|
|
|
|
expect(violations).toEqual([]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('guards component translation namespaces as kebab-case', () => {
|
|
|
|
|
const sourceRoots = [
|
|
|
|
|
join(HERE, 'soma', 'components'),
|
|
|
|
|
join(HERE, 'morfo', 'components')
|
|
|
|
|
];
|
|
|
|
|
const pathViolations = sourceRoots.flatMap((root) =>
|
|
|
|
|
listSourceFiles(root).flatMap((file) =>
|
|
|
|
|
collectComponentLangPathViolations(readFileSync(file, 'utf8'), file)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(pathViolations).toEqual([]);
|
|
|
|
|
expect(collectLangCatalogKeyViolations(componentLangs)).toEqual([]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('events/sema uses events as service name and requires dom or projector for visual projection', () => {
|
|
|
|
|
const factory = defineEngineSemantic({ visual: false });
|
|
|
|
|
expect(factory.name).toBe(UIX_LAYER_CONTRACTS.events.publicServiceName);
|
|
|
|
|
|