diff design_system/test/design_system_policy_test.js @ 258:60a876c4587a

[ui] Add semantic primitive ownership Build a layered Zenbu token and sizing system, make authored controls use native-underneath primitives, migrate mrjunejune without imposing visual surfaces, and document/enforce HTML ownership in the catalog and wiki. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Wed, 05 Aug 2026 05:25:40 -0700
parents 2b6e732087ff
children
line wrap: on
line diff
--- a/design_system/test/design_system_policy_test.js	Tue Aug 04 16:49:11 2026 -0700
+++ b/design_system/test/design_system_policy_test.js	Wed Aug 05 05:25:40 2026 -0700
@@ -7,7 +7,8 @@
 assert.ok(RUNFILES);
 assert.ok(WORKSPACE);
 
-const sourceRoot = path.join(RUNFILES, WORKSPACE, 'design_system/src');
+const designRoot = path.join(RUNFILES, WORKSPACE, 'design_system');
+const sourceRoot = path.join(designRoot, 'src');
 const files = [];
 
 function walk(directory) {
@@ -19,6 +20,26 @@
 }
 
 walk(sourceRoot);
+function listMarkdown(directory) {
+  const markdown = [];
+  for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
+    const entryPath = path.join(directory, entry.name);
+    if (entry.isDirectory()) markdown.push(...listMarkdown(entryPath));
+    else if (entry.name.endsWith('.md')) markdown.push(entryPath);
+  }
+  return markdown;
+}
+const documentationFiles = [
+  ...fs.readdirSync(designRoot)
+    .filter(name => name.endsWith('.md'))
+    .map(name => path.join(designRoot, name)),
+  ...listMarkdown(path.join(designRoot, 'wiki')),
+];
+const namingFiles = [
+  ...files,
+  ...documentationFiles,
+  path.join(designRoot, 'main.c'),
+];
 
 function validateCallableDocumentation(
   relative,
@@ -58,13 +79,18 @@
 ].join('|'), 'gi');
 const forbiddenGlyph = /[×←→↑↓+◆◇✓✔✕✖★☆⚠]/gu;
 const pictographic = /\p{Extended_Pictographic}/gu;
+const borrowedProductName =
+  /(?:shadcn|storybook|sonner|radix|tailwind|react)/gi;
 const failures = [];
 
 for (const file of files) {
   const relative = path.relative(sourceRoot, file);
   const source = fs.readFileSync(file, 'utf8');
 
-  if (relative !== 'styles/tokens.css') {
+  if (![
+    'styles/reference.css',
+    'styles/tokens.css',
+  ].includes(relative)) {
     for (const match of source.matchAll(rawColor)) {
       failures.push(
         `${relative}:${source.slice(0, match.index).split('\n').length} ` +
@@ -157,7 +183,7 @@
            !/(?:collapse|image|radius|spacing|style|width)/.test(property));
         if (!colorProperty) continue;
         const withoutTokens = value.replace(
-          /var\(\s*--zen-[\w-]+\s*\)/g,
+          /var\(\s*--(?:zenbu|zen)-[\w-]+\s*\)/g,
           '',
         );
         const words = withoutTokens.toLowerCase()
@@ -168,7 +194,7 @@
             )) {
           failures.push(
             `${relative}:${css.slice(0, declaration.index).split('\n').length} ` +
-            `${property} must use a --zen-* token`,
+            `${property} must use a --zenbu-sys-* token`,
           );
         }
       }
@@ -223,6 +249,54 @@
   }
 }
 
+const cssFiles = files.filter(file => file.endsWith('.css'));
+const zenbuDeclarations = new Set();
+for (const file of cssFiles) {
+  const source = fs.readFileSync(file, 'utf8');
+  for (const match of source.matchAll(/(--zenbu-[\w-]+)\s*:/g)) {
+    zenbuDeclarations.add(match[1]);
+  }
+}
+for (const file of cssFiles) {
+  const relative = path.relative(sourceRoot, file);
+  const source = fs.readFileSync(file, 'utf8');
+  for (const match of source.matchAll(/var\(\s*(--zenbu-[\w-]+)/g)) {
+    if (!zenbuDeclarations.has(match[1])) {
+      failures.push(`${relative} references unknown token ${match[1]}`);
+    }
+  }
+  if (![
+    'styles/reference.css',
+    'styles/semantic.css',
+    'styles/themes.css',
+    'styles/tokens.css',
+  ].includes(relative)) {
+    for (const match of source.matchAll(
+      /var\(\s*(--zenbu-ref-color-[\w-]+)/g,
+    )) {
+      failures.push(
+        `${relative} consumes reference color ${match[1]} directly`,
+      );
+    }
+  }
+  if (![
+    'styles/themes.css',
+    'styles/tokens.css',
+  ].includes(relative) && /var\(\s*--zen-/.test(source)) {
+    failures.push(`${relative} consumes deprecated --zen-* tokens`);
+  }
+}
+
+for (const file of namingFiles) {
+  const source = fs.readFileSync(file, 'utf8');
+  for (const match of source.matchAll(borrowedProductName)) {
+    failures.push(
+      `${path.relative(designRoot, file)} uses borrowed product name ` +
+      JSON.stringify(match[0]),
+    );
+  }
+}
+
 for (const file of files.filter(file =>
   file.endsWith('.js')
 )) {