@hanzo/design 0.5.6 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/scripts/lint.mjs +36 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/design",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "packageManager": "pnpm@11.17.0",
5
5
  "description": "Hanzo Design System \u2014 monochrome, dark-default tokens + components + brand assets, the single source of truth for every Hanzo surface. CSS + typed programmatic tokens.",
6
6
  "license": "MIT OR Apache-2.0",
package/scripts/lint.mjs CHANGED
@@ -106,9 +106,16 @@ function lintUnresolved(rel, src) {
106
106
  }
107
107
  }
108
108
 
109
+ /** A build product says so at the top, and there is no point telling anyone to
110
+ * fix it — the fix belongs in the generator. Every convention in use is
111
+ * accepted, and only the head of the file is read, so a file that merely
112
+ * MENTIONS the phrase is still linted. */
113
+ const GENERATED = /@generated|AUTO-?GENERATED|DO NOT EDIT|Generated by|autogenerated/i
114
+
109
115
  function lintFile(abs, root) {
110
116
  const rel = relative(root, abs).split(sep).join('/')
111
117
  const raw = readFileSync(abs, 'utf8')
118
+ if (GENERATED.test(raw.slice(0, 400))) return
112
119
  if (extname(rel) === '.md') {
113
120
  // Line numbers stay true: replace everything outside a css fence with
114
121
  // blanks rather than extracting the fences, so a reported line is the line.
@@ -248,10 +255,18 @@ function lintFile(abs, root) {
248
255
  // an org switcher, an app launcher and a footer are ONE set of controls
249
256
  // across every Hanzo surface — that is what makes them recognisable as
250
257
  // one product. A local copy is a control that drifts by a release.
251
- for (const m of src.matchAll(
252
- /\b(?:function|const)\s+(Hanzo?(?:Header|Footer)|(?:Site|App|Global|Main|Top)(?:Header|Nav|Footer|Bar)|(?:Header|Nav|Command|Search)(?:Search|Palette|Trigger|Bar)|OrgSwitcher|OrgHeader|AppLauncher|MegaMenu)\b/g))
253
- flag(rel, lineOf(src, m.index), 'shell-owned-chrome', m[1],
254
- 'import it from @hanzogui/shell the chrome is one set of controls, fleet-wide')
258
+ //
259
+ // A WRAPPER IS NOT A COPY, and the question is which one this is. A file
260
+ // that imports the shell and composes it — passing this site's pages into
261
+ // `HanzoHeader`is doing exactly what it should, and hanzo.ai's
262
+ // `SiteHeader`/`SiteFooter` are that. So the rule asks whether the shell
263
+ // is in the file at all; if it is, the name is a local alias for shell
264
+ // chrome and not a second implementation of it.
265
+ if (!/['"]@hanzogui\/shell/.test(src))
266
+ for (const m of src.matchAll(
267
+ /\b(?:function|const)\s+(Hanzo?(?:Header|Footer)|(?:Site|App|Global|Main|Top)(?:Header|Nav|Footer|Bar)|(?:Header|Nav|Command|Search)(?:Search|Palette|Trigger|Bar)|OrgSwitcher|OrgHeader|AppLauncher|MegaMenu)\b/g))
268
+ flag(rel, lineOf(src, m.index), 'shell-owned-chrome', m[1],
269
+ 'import it from @hanzogui/shell — the chrome is one set of controls, fleet-wide')
255
270
  }
256
271
 
257
272
  // ── run ──────────────────────────────────────────────────────────────────
@@ -276,10 +291,18 @@ const RULES = [
276
291
  *
277
292
  * { "raw-color": 412, "redeclared-token": 6 }
278
293
  *
279
- * A count that comes in UNDER its allowance fails too, and that is the ratchet
280
- * rather than a threshold: the fix has to delete its own exemption in the same
281
- * commit, or the next author inherits room to regress into. The same discipline
282
- * hanzo.ai's `audit-catalog.mjs` holds its KNOWN_UNSERVED list to.
294
+ * A count UNDER its allowance is REPORTED, loudly, and does not fail. That was
295
+ * the other way round for one release and it froze hanzo.app's: the count a tree
296
+ * produces is not identical everywhere a working copy holds files a fresh
297
+ * checkout does not so an allowance that has to match EXACTLY turns a
298
+ * difference of environment into a red release. Every gate this package exists
299
+ * to add has to be worth more than the shipping it stops, and a stale number is
300
+ * never worth that.
301
+ *
302
+ * So: over the allowance FAILS — that violation was written today and there is
303
+ * nothing environmental about it. Under it prints the number to write down. The
304
+ * ratchet is still a ratchet, because the allowance can only be lowered by hand
305
+ * and this line tells you exactly what to lower it to.
283
306
  *
284
307
  * A rule with no entry is allowed ZERO, so a new rule is live everywhere the
285
308
  * day it ships and no surface has to opt in.
@@ -318,15 +341,16 @@ if (unknown.length) {
318
341
  process.exit(1)
319
342
  }
320
343
 
321
- if (!over.length && !under.length) {
344
+ for (const r of under)
345
+ console.log(` ${r}: ${count[r]} found against ${allow[r] ?? 0} allowed — ${(allow[r] ?? 0) - count[r]} fixed. ` +
346
+ `Lower it to ${count[r]} in ${RATCHET}.`)
347
+
348
+ if (!over.length) {
322
349
  const owed = RULES.reduce((a, r) => a + (allow[r] ?? 0), 0)
323
350
  console.log(`hanzo-design-lint: ${scanned} files, clean${owed ? ` (${owed} allowed by ${RATCHET})` : ''}`)
324
351
  process.exit(0)
325
352
  }
326
353
  for (const r of over)
327
354
  console.log(`\n ${r}: ${count[r]} found, ${allow[r] ?? 0} allowed — a NEW violation. Fix it.`)
328
- for (const r of under)
329
- console.log(`\n ${r}: ${count[r]} found, ${allow[r] ?? 0} allowed — you fixed ${(allow[r] ?? 0) - count[r]}. ` +
330
- `Lower it in ${RATCHET}; the allowance only shrinks.`)
331
355
  console.log(`\n${findings.length} violation(s) across ${scanned} files`)
332
356
  process.exit(1)