@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.
- package/package.json +1 -1
- 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.
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
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
|
-
|
|
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)
|