@hanzo/gui 8.0.0 → 8.0.2
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/css-check.mjs +624 -0
- package/package.json +68 -43
- package/types/views/Anchor.d.ts +1 -7
- package/types/views/Anchor.d.ts.map +1 -1
- package/types/views/EnsureFlexed.d.ts +1 -1
- package/types/views/EnsureFlexed.d.ts.map +1 -1
- package/types/views/Fieldset.d.ts +1 -5
- package/types/views/Fieldset.d.ts.map +1 -1
- package/types/views/Text.d.ts +1 -3
- package/types/views/Text.d.ts.map +1 -1
- package/types/views/VisuallyHidden.d.ts +1 -4
- package/types/views/VisuallyHidden.d.ts.map +1 -1
- package/.turbo/turbo-build.log +0 -4
- package/tsconfig.json +0 -171
package/css-check.mjs
ADDED
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* gui-css-check — every class in the markup must have a rule in the CSS that
|
|
4
|
+
* ships with it.
|
|
5
|
+
*
|
|
6
|
+
* A build is green when the code compiles. Nothing in a compiler knows whether
|
|
7
|
+
* the stylesheet a document links actually contains the classes that document
|
|
8
|
+
* uses, so "classes without rules" ships silently and renders a blank-looking
|
|
9
|
+
* page. It has happened three times here: hanzo.app served 18KB of CSS holding
|
|
10
|
+
* zero of its ~240 gui atomic classes; @hanzogui/shell@8.0.3 published 966
|
|
11
|
+
* lines of Tailwind classes with no Tailwind anywhere in its dependencies.
|
|
12
|
+
* Both type-checked. Both built. Both were completely unstyled in production.
|
|
13
|
+
*
|
|
14
|
+
* This reads the built output and compares two sets:
|
|
15
|
+
* used — class tokens in class="..." attributes of the rendered markup
|
|
16
|
+
* defined — class tokens in selector position across every sheet the page
|
|
17
|
+
* delivers: linked .css files AND inline <style>
|
|
18
|
+
* Anything used and not defined is a miss and the process exits 1.
|
|
19
|
+
*
|
|
20
|
+
* Per page, not per build: a rule that exists in some sheet the page never
|
|
21
|
+
* links is not delivered, and the browser agrees.
|
|
22
|
+
*
|
|
23
|
+
* Zero dependencies, one file. It has to run in any app without dragging a
|
|
24
|
+
* toolchain behind it, and it has to keep working when the toolchain is the
|
|
25
|
+
* thing that broke.
|
|
26
|
+
*/
|
|
27
|
+
import {
|
|
28
|
+
readFileSync,
|
|
29
|
+
readdirSync,
|
|
30
|
+
statSync,
|
|
31
|
+
existsSync,
|
|
32
|
+
mkdirSync,
|
|
33
|
+
mkdtempSync,
|
|
34
|
+
writeFileSync,
|
|
35
|
+
} from 'node:fs'
|
|
36
|
+
import { join, resolve, relative, basename, sep } from 'node:path'
|
|
37
|
+
import { tmpdir } from 'node:os'
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------- extraction
|
|
40
|
+
|
|
41
|
+
const stripComments = (css) => css.replace(/\/\*[\s\S]*?\*\//g, ' ')
|
|
42
|
+
|
|
43
|
+
/** Strings and url() can hold braces and dots; neither is ever a selector. */
|
|
44
|
+
const stripLiterals = (css) =>
|
|
45
|
+
css
|
|
46
|
+
.replace(/"(?:[^"\\]|\\[\s\S])*"/g, '""')
|
|
47
|
+
.replace(/'(?:[^'\\]|\\[\s\S])*'/g, "''")
|
|
48
|
+
.replace(/url\([^)]*\)/g, 'url()')
|
|
49
|
+
|
|
50
|
+
/** `\3a ` and `\:` both mean a literal `:` in a class name. */
|
|
51
|
+
const unescapeIdent = (s) =>
|
|
52
|
+
s.replace(/\\([0-9a-fA-F]{1,6})[ \t]?|\\([\s\S])/g, (_, hex, ch) =>
|
|
53
|
+
hex ? String.fromCodePoint(parseInt(hex, 16)) : ch
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A class token. The hex-escape branch comes first and swallows its own
|
|
58
|
+
* terminating space (`.a\3a b` is the single class `a:b`) — without that the
|
|
59
|
+
* token would stop at the space and lose everything after it.
|
|
60
|
+
*/
|
|
61
|
+
const CLASS_IN_SELECTOR =
|
|
62
|
+
/\.((?:\\[0-9a-fA-F]{1,6}[ \t]?|\\[\s\S]|[^\s.,:;>+~()[\]{}#*%"'\\!])+)/g
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Class tokens in selector position. Walks the sheet tracking brace depth and
|
|
66
|
+
* collects only the text that PRECEDES a `{` — a declaration body never does,
|
|
67
|
+
* so `padding:.5rem` can't masquerade as a class, while rules nested inside
|
|
68
|
+
* `@media`/`@supports`/`@layer` still get read.
|
|
69
|
+
*/
|
|
70
|
+
export function definedClasses(css) {
|
|
71
|
+
const src = stripLiterals(stripComments(css))
|
|
72
|
+
const out = new Set()
|
|
73
|
+
let prelude = ''
|
|
74
|
+
let depth = 0
|
|
75
|
+
for (let i = 0; i < src.length; i++) {
|
|
76
|
+
const c = src[i]
|
|
77
|
+
if (c === '{') {
|
|
78
|
+
const sel = prelude.trim()
|
|
79
|
+
// at-rule preludes hold media queries and numbers, never class selectors
|
|
80
|
+
if (sel && !sel.startsWith('@')) {
|
|
81
|
+
for (const m of sel.matchAll(CLASS_IN_SELECTOR)) out.add(unescapeIdent(m[1]))
|
|
82
|
+
}
|
|
83
|
+
prelude = ''
|
|
84
|
+
depth++
|
|
85
|
+
} else if (c === '}') {
|
|
86
|
+
prelude = ''
|
|
87
|
+
depth = Math.max(0, depth - 1)
|
|
88
|
+
} else if (c === ';' && depth > 0) {
|
|
89
|
+
prelude = ''
|
|
90
|
+
} else {
|
|
91
|
+
prelude += c
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return out
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Scripts carry the RSC flight payload, which quotes markup; never markup. */
|
|
98
|
+
const stripNonMarkup = (html) =>
|
|
99
|
+
html
|
|
100
|
+
.replace(/<!--[\s\S]*?-->/g, ' ')
|
|
101
|
+
.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, ' ')
|
|
102
|
+
.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, ' ')
|
|
103
|
+
|
|
104
|
+
const ENTITIES = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'" }
|
|
105
|
+
|
|
106
|
+
export function usedClasses(html) {
|
|
107
|
+
const out = new Set()
|
|
108
|
+
const markup = stripNonMarkup(html)
|
|
109
|
+
for (const m of markup.matchAll(/\bclass(?:Name)?\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>]+))/gi)) {
|
|
110
|
+
const raw = (m[2] ?? m[3] ?? m[4] ?? '').replace(/&[a-z#0-9]+;/gi, (e) => ENTITIES[e] ?? e)
|
|
111
|
+
for (const cls of raw.split(/\s+/)) if (cls) out.add(cls)
|
|
112
|
+
}
|
|
113
|
+
return out
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function inlineStyles(html) {
|
|
117
|
+
return [...html.matchAll(/<style\b[^>]*>([\s\S]*?)<\/style>/gi)].map((m) => m[1])
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function stylesheetHrefs(html) {
|
|
121
|
+
const out = []
|
|
122
|
+
for (const m of html.matchAll(/<link\b[^>]*>/gi)) {
|
|
123
|
+
const tag = m[0]
|
|
124
|
+
if (!/\brel\s*=\s*["']?[^"'>]*\bstylesheet\b/i.test(tag)) continue
|
|
125
|
+
const href = tag.match(/\bhref\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>]+))/i)
|
|
126
|
+
if (href) out.push((href[2] ?? href[3] ?? href[4]).replace(/&/g, '&').split('?')[0])
|
|
127
|
+
}
|
|
128
|
+
return out
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ------------------------------------------------------------------ scanning
|
|
132
|
+
|
|
133
|
+
const SKIP_DIR = new Set(['node_modules', '.git', 'cache', 'sourcemaps'])
|
|
134
|
+
|
|
135
|
+
/** Pages the framework ships, not pages this app wrote. */
|
|
136
|
+
const BOILERPLATE = /^(404|500|_error|_not-found|not-found)\.html?$/i
|
|
137
|
+
|
|
138
|
+
function walk(dir, hit, depth = 0) {
|
|
139
|
+
if (depth > 12) return
|
|
140
|
+
let entries
|
|
141
|
+
try {
|
|
142
|
+
entries = readdirSync(dir, { withFileTypes: true })
|
|
143
|
+
} catch {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
for (const e of entries) {
|
|
147
|
+
if (e.name.startsWith('.') && depth > 0) continue
|
|
148
|
+
const p = join(dir, e.name)
|
|
149
|
+
if (e.isDirectory()) {
|
|
150
|
+
if (!SKIP_DIR.has(e.name)) walk(p, hit, depth + 1)
|
|
151
|
+
} else if (e.isFile()) {
|
|
152
|
+
hit(p)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Built output as a browser sees it: pages, and every sheet on disk. */
|
|
158
|
+
export function collect(roots) {
|
|
159
|
+
const pages = []
|
|
160
|
+
const sheets = new Map() // absolute path -> css text
|
|
161
|
+
for (const root of roots) {
|
|
162
|
+
const st = statSync(root)
|
|
163
|
+
const add = (p) => {
|
|
164
|
+
if (/\.html?$/i.test(p)) pages.push(p)
|
|
165
|
+
else if (/\.css$/i.test(p) && !/\.map$/.test(p)) sheets.set(resolve(p), readFileSync(p, 'utf8'))
|
|
166
|
+
}
|
|
167
|
+
if (st.isDirectory()) walk(root, add)
|
|
168
|
+
else add(root)
|
|
169
|
+
}
|
|
170
|
+
return { pages, sheets }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* `/_next/static/css/x.css` has to become a path on disk. Basename is the one
|
|
175
|
+
* key both sides agree on across Next, Vite and a plain static dir, and every
|
|
176
|
+
* bundler content-hashes the name, so it is unique in practice.
|
|
177
|
+
*/
|
|
178
|
+
function indexByBase(sheets) {
|
|
179
|
+
const byBase = new Map()
|
|
180
|
+
for (const [p, css] of sheets) {
|
|
181
|
+
const b = basename(p)
|
|
182
|
+
if (!byBase.has(b)) byBase.set(b, { path: p, css })
|
|
183
|
+
}
|
|
184
|
+
return byBase
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// -------------------------------------------------------------------- render
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* A client-rendered app ships an empty shell, so reading its HTML off disk
|
|
191
|
+
* measures nothing. Rendering it is the only honest way to ask the question,
|
|
192
|
+
* and the browser is also the only thing that knows the full sheet list once
|
|
193
|
+
* a runtime has injected into it.
|
|
194
|
+
*
|
|
195
|
+
* Playwright is imported lazily and is not a dependency of this package: an
|
|
196
|
+
* SSR app never needs it, and the apps that do need it already have it.
|
|
197
|
+
*/
|
|
198
|
+
export async function render(urls, { dir }) {
|
|
199
|
+
// @playwright/test is what most repos here install and it re-exports the same
|
|
200
|
+
// chromium; asking only for `playwright` would fail in almost all of them.
|
|
201
|
+
let chromium
|
|
202
|
+
for (const pkg of ['playwright', '@playwright/test']) {
|
|
203
|
+
try {
|
|
204
|
+
;({ chromium } = await import(pkg))
|
|
205
|
+
break
|
|
206
|
+
} catch {}
|
|
207
|
+
}
|
|
208
|
+
if (!chromium) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
`--render needs playwright, and neither "playwright" nor "@playwright/test"\n` +
|
|
211
|
+
` resolves from ${process.cwd()}.\n` +
|
|
212
|
+
` npm i -D @playwright/test && npx playwright install chromium\n` +
|
|
213
|
+
` Or point gui-css-check at pre-rendered HTML instead.`
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
mkdirSync(dir, { recursive: true })
|
|
217
|
+
const browser = await chromium.launch()
|
|
218
|
+
const page = await browser.newPage()
|
|
219
|
+
const out = []
|
|
220
|
+
try {
|
|
221
|
+
for (const [i, url] of urls.entries()) {
|
|
222
|
+
const res = await page.goto(url, { waitUntil: 'networkidle', timeout: 60_000 })
|
|
223
|
+
if (!res || !res.ok()) throw new Error(`${url} returned ${res ? res.status() : 'nothing'}`)
|
|
224
|
+
// Every rule the document actually has, including whatever a runtime
|
|
225
|
+
// injected after load. Linked sheets are kept as separate files under
|
|
226
|
+
// their own basenames and inline ones stay inline, so the shape on disk
|
|
227
|
+
// matches an SSR build exactly and one code path reads both — including
|
|
228
|
+
// the byte split between what a browser caches and what it re-fetches.
|
|
229
|
+
const { linked, inline, html } = await page.evaluate(() => {
|
|
230
|
+
const linked = {}
|
|
231
|
+
const inline = []
|
|
232
|
+
for (const sheet of document.styleSheets) {
|
|
233
|
+
let text
|
|
234
|
+
try {
|
|
235
|
+
text = [...sheet.cssRules].map((r) => r.cssText).join('\n')
|
|
236
|
+
} catch {
|
|
237
|
+
continue // cross-origin sheet, unreadable by design
|
|
238
|
+
}
|
|
239
|
+
const href = sheet.href
|
|
240
|
+
if (href) linked[new URL(href).pathname.split('/').pop()] = text
|
|
241
|
+
else inline.push(text)
|
|
242
|
+
}
|
|
243
|
+
return { linked, inline, html: document.documentElement.outerHTML }
|
|
244
|
+
})
|
|
245
|
+
const stem = `${String(i).padStart(3, '0')}-${url.replace(/[^\w.-]+/g, '_').slice(-60)}`
|
|
246
|
+
for (const [name, text] of Object.entries(linked)) writeFileSync(join(dir, name), text)
|
|
247
|
+
writeFileSync(
|
|
248
|
+
join(dir, `${stem}.html`),
|
|
249
|
+
inline.map((t) => `<style>${t}</style>`).join('') + html
|
|
250
|
+
)
|
|
251
|
+
out.push(url)
|
|
252
|
+
}
|
|
253
|
+
} finally {
|
|
254
|
+
await browser.close()
|
|
255
|
+
}
|
|
256
|
+
// the directory, not the files: the pages' <link> tags still point at the
|
|
257
|
+
// sheets, which now sit beside them under the same basenames
|
|
258
|
+
return [dir]
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// -------------------------------------------------------------------- config
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Classes a library provably stamps as an IDENTITY MARKER and never styles.
|
|
265
|
+
* Each line cites where it is emitted — an allowance without a source is a
|
|
266
|
+
* guess, and a guess here is how a real miss gets waved through.
|
|
267
|
+
*/
|
|
268
|
+
const DEFAULT_ALLOW = [
|
|
269
|
+
// gui, getSplitStyles.tsx: `is_${componentNameFinal}` on every styled
|
|
270
|
+
// component so devtools and tests can name it. Carries no rule by design.
|
|
271
|
+
'is_*',
|
|
272
|
+
// gui, Theme.tsx: marks a nested (non-root) Theme. The styling comes from
|
|
273
|
+
// the t_* theme class beside it, which does have rules.
|
|
274
|
+
't_sub_theme',
|
|
275
|
+
// lucide-react, Icon.js: `mergeClasses("lucide", className)` and
|
|
276
|
+
// createLucideIcon.js: `lucide-${name}` — hooks for consumers to target.
|
|
277
|
+
'lucide',
|
|
278
|
+
'lucide-*',
|
|
279
|
+
// next/font mints these to carry a font-family custom property. The rule is
|
|
280
|
+
// emitted by next/font's own pipeline, not by anything in this repo.
|
|
281
|
+
'__variable_*',
|
|
282
|
+
'__className_*',
|
|
283
|
+
// @hanzo/ui, backends/gui/button.js `buttonVariants` and backends/gui/badge.js
|
|
284
|
+
// `badgeVariants`. core/cn.cjs states the contract: "There is no Tailwind
|
|
285
|
+
// conflict-resolution step because there are no Tailwind utilities to resolve:
|
|
286
|
+
// styling lives in gui style props and the token scale, and class names are
|
|
287
|
+
// only stable handles (`hanzo-button`, `hz-mono`) a host may select on."
|
|
288
|
+
// The look comes from gui props on the same element.
|
|
289
|
+
'hanzo-button',
|
|
290
|
+
'hanzo-button--*',
|
|
291
|
+
'hanzo-badge',
|
|
292
|
+
'hanzo-badge--*',
|
|
293
|
+
]
|
|
294
|
+
// NOT allowed, and deliberately: `hz-mono`, `hz-tnum`, `hz-row`. The same
|
|
295
|
+
// comment calls them handles, but gui-config.js says who writes the rule —
|
|
296
|
+
// "The host self-hosts both faces (its own fonts.css) — this only names them."
|
|
297
|
+
// They are @hanzo/ui's INTERFACE to the host, so an app that stamps them and
|
|
298
|
+
// defines nothing renders ids and figures in the proportional face with
|
|
299
|
+
// proportional digits. That is a miss, and it should read as one.
|
|
300
|
+
|
|
301
|
+
const globToRe = (g) =>
|
|
302
|
+
new RegExp('^' + g.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.') + '$')
|
|
303
|
+
|
|
304
|
+
export function loadConfig(cwd) {
|
|
305
|
+
for (const name of ['gui-css-check.json', '.gui-css-check.json']) {
|
|
306
|
+
const p = join(cwd, name)
|
|
307
|
+
if (existsSync(p)) return { ...JSON.parse(readFileSync(p, 'utf8')), _from: p }
|
|
308
|
+
}
|
|
309
|
+
const pkgPath = join(cwd, 'package.json')
|
|
310
|
+
if (existsSync(pkgPath)) {
|
|
311
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
312
|
+
if (pkg['gui-css-check']) return { ...pkg['gui-css-check'], _from: pkgPath }
|
|
313
|
+
}
|
|
314
|
+
return {}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// --------------------------------------------------------------------- check
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @returns {{pages: Array, used: Set, missing: Map, sheets: Map, bytes: object}}
|
|
321
|
+
*/
|
|
322
|
+
export function check({ roots, allow = [], extraCss = [] }) {
|
|
323
|
+
const { pages, sheets } = collect(roots)
|
|
324
|
+
const byBase = indexByBase(sheets)
|
|
325
|
+
const allowRe = [...DEFAULT_ALLOW, ...allow].map(globToRe)
|
|
326
|
+
const allowed = (c) => allowRe.some((re) => re.test(c))
|
|
327
|
+
|
|
328
|
+
const shared = new Set()
|
|
329
|
+
for (const p of extraCss) for (const c of definedClasses(readFileSync(p, 'utf8'))) shared.add(c)
|
|
330
|
+
|
|
331
|
+
// Sheets are parsed once and reused; a page just unions the ones it links.
|
|
332
|
+
const parsed = new Map()
|
|
333
|
+
const classesOf = (path, css) => {
|
|
334
|
+
if (!parsed.has(path)) parsed.set(path, definedClasses(css))
|
|
335
|
+
return parsed.get(path)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const results = []
|
|
339
|
+
const allUsed = new Set()
|
|
340
|
+
const missing = new Map() // class -> [page, ...]
|
|
341
|
+
const linkedOnce = new Map() // path -> bytes, counted once however many pages link it
|
|
342
|
+
let inlineBytes = 0
|
|
343
|
+
|
|
344
|
+
for (const page of pages) {
|
|
345
|
+
const html = readFileSync(page, 'utf8')
|
|
346
|
+
const used = usedClasses(html)
|
|
347
|
+
const defined = new Set(shared)
|
|
348
|
+
|
|
349
|
+
for (const css of inlineStyles(html)) {
|
|
350
|
+
inlineBytes += Buffer.byteLength(css)
|
|
351
|
+
for (const c of definedClasses(css)) defined.add(c)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const unresolved = []
|
|
355
|
+
const linked = []
|
|
356
|
+
for (const href of stylesheetHrefs(html)) {
|
|
357
|
+
if (/^(https?:)?\/\//i.test(href)) continue // remote sheet, not ours to verify
|
|
358
|
+
const hit = byBase.get(basename(href))
|
|
359
|
+
if (!hit) {
|
|
360
|
+
unresolved.push(href)
|
|
361
|
+
continue
|
|
362
|
+
}
|
|
363
|
+
linked.push(hit.path)
|
|
364
|
+
linkedOnce.set(hit.path, Buffer.byteLength(hit.css))
|
|
365
|
+
for (const c of classesOf(hit.path, hit.css)) defined.add(c)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const miss = []
|
|
369
|
+
for (const c of used) {
|
|
370
|
+
allUsed.add(c)
|
|
371
|
+
if (defined.has(c) || allowed(c)) continue
|
|
372
|
+
miss.push(c)
|
|
373
|
+
if (!missing.has(c)) missing.set(c, [])
|
|
374
|
+
missing.get(c).push(page)
|
|
375
|
+
}
|
|
376
|
+
results.push({ page, used: used.size, missing: miss.sort(), linked, unresolved })
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// A document with no classes at all proves nothing: it is either an empty
|
|
380
|
+
// shell a client will fill in, or a render that failed. Counting it as a
|
|
381
|
+
// pass is how a checker becomes decoration.
|
|
382
|
+
const empty = results.filter((r) => !r.used)
|
|
383
|
+
// Same trap one step along: an app whose routes are all server-rendered
|
|
384
|
+
// prerenders nothing but the framework's own error pages, and scoring THOSE
|
|
385
|
+
// says nothing whatever about the app. Seen on hanzo.id, where the single
|
|
386
|
+
// checked page was Next's built-in 500.
|
|
387
|
+
const boilerplate = results.filter((r) => BOILERPLATE.test(basename(r.page)))
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
results,
|
|
391
|
+
pages,
|
|
392
|
+
sheets,
|
|
393
|
+
empty,
|
|
394
|
+
boilerplate,
|
|
395
|
+
used: allUsed,
|
|
396
|
+
missing,
|
|
397
|
+
bytes: {
|
|
398
|
+
// cached once by the browser, however many pages link it
|
|
399
|
+
static: [...linkedOnce.values()].reduce((a, b) => a + b, 0),
|
|
400
|
+
staticFiles: linkedOnce.size,
|
|
401
|
+
// re-sent with every single document and cacheable by nobody — the
|
|
402
|
+
// number a static extractor exists to drive to zero
|
|
403
|
+
inlinePerPage: pages.length ? Math.round(inlineBytes / pages.length) : 0,
|
|
404
|
+
inlineTotal: inlineBytes,
|
|
405
|
+
},
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ------------------------------------------------------------------------ cli
|
|
410
|
+
|
|
411
|
+
const HELP = `gui-css-check — fail the build when the markup uses a class the CSS never defines
|
|
412
|
+
|
|
413
|
+
gui-css-check [dir|file ...] [options]
|
|
414
|
+
|
|
415
|
+
With no path it looks for .next, dist, out, build, storybook-static in cwd.
|
|
416
|
+
|
|
417
|
+
--render <url> render a running route in a browser and check THAT (repeatable).
|
|
418
|
+
The only honest measurement for a client-rendered app, whose
|
|
419
|
+
built HTML is an empty shell. Needs playwright in the app.
|
|
420
|
+
--css <file> an extra sheet every page gets (repeatable)
|
|
421
|
+
--allow <glob> a class pattern that needs no rule (repeatable)
|
|
422
|
+
--json machine-readable result on stdout
|
|
423
|
+
--quiet only print on failure
|
|
424
|
+
|
|
425
|
+
Allowances also load from gui-css-check.json or a "gui-css-check" key in
|
|
426
|
+
package.json: { "allow": ["swiper-*"] }
|
|
427
|
+
`
|
|
428
|
+
|
|
429
|
+
const DEFAULT_ROOTS = ['.next', 'dist', 'out', 'build', 'storybook-static']
|
|
430
|
+
|
|
431
|
+
/** Utility-framework shapes: `px-3`, `text-white/40`, `hover:bg-white/[0.06]`. */
|
|
432
|
+
const UTILITY =
|
|
433
|
+
/^(-?[a-z][\w-]*:)*-?((p|m|px|py|pt|pb|pl|pr|mx|my|mt|mb|ml|mr|w|h|z|gap|text|bg|border|ring|max|min|top|left|right|bottom|inset|col|row|order|basis|space|leading|tracking|font|opacity|shadow|rounded|aspect|overflow|translate|scale|rotate|duration|delay|ease|from|via|to|fill|stroke|divide|placeholder|outline|decoration|indent|justify|items|content|self|place|object|cursor|backdrop|prose|grid|flex)(-[\w./[\]#%()-]+)?|(flex|grid|block|inline|hidden|absolute|relative|fixed|sticky|static|truncate|italic|underline|uppercase|antialiased|container|prose|sr-only|isolate|visible|invisible))$/
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Misses are grouped by CAUSE, because the fix differs completely and a flat
|
|
437
|
+
* list of 300 class names tells you nothing about which one you have.
|
|
438
|
+
*/
|
|
439
|
+
const KINDS = [
|
|
440
|
+
{
|
|
441
|
+
test: (c) => c.startsWith('_'),
|
|
442
|
+
title: 'gui atomic classes — the atomic sheet was never authored',
|
|
443
|
+
cause:
|
|
444
|
+
` Nothing produced these rules. Exactly one of the two paths has to run:\n` +
|
|
445
|
+
` · GuiProvider injects at runtime — do NOT set disableInjectCSS, or\n` +
|
|
446
|
+
` · a compiler plugin extracts them at build time —\n` +
|
|
447
|
+
` @hanzogui/next-plugin withGui() / @hanzogui/vite-plugin gui().\n` +
|
|
448
|
+
` disableInjectCSS with no plugin configured leaves nobody writing it.`,
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
test: (c) => UTILITY.test(c),
|
|
452
|
+
title: 'utility-framework classes — no such framework is installed',
|
|
453
|
+
cause:
|
|
454
|
+
` These are Tailwind-shaped, and nothing in this build compiles them.\n` +
|
|
455
|
+
` They have never rendered. Delete them and use gui props instead;\n` +
|
|
456
|
+
` adding Tailwind back is not the fix.`,
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
test: () => true,
|
|
460
|
+
title: 'authored classes with no stylesheet behind them',
|
|
461
|
+
cause:
|
|
462
|
+
` Someone wrote these class names and no delivered sheet defines them.\n` +
|
|
463
|
+
` Either ship the stylesheet that styles them or drop the class.`,
|
|
464
|
+
},
|
|
465
|
+
]
|
|
466
|
+
|
|
467
|
+
const num = (n) => n.toLocaleString('en-US')
|
|
468
|
+
const kb = (n) => `${num(Math.round(n / 1024))} KB`
|
|
469
|
+
|
|
470
|
+
async function main(argv) {
|
|
471
|
+
const roots = []
|
|
472
|
+
const extraCss = []
|
|
473
|
+
const allow = []
|
|
474
|
+
const urls = []
|
|
475
|
+
let json = false
|
|
476
|
+
let quiet = false
|
|
477
|
+
for (let i = 0; i < argv.length; i++) {
|
|
478
|
+
const a = argv[i]
|
|
479
|
+
if (a === '--help' || a === '-h') return console.log(HELP), 0
|
|
480
|
+
else if (a === '--json') json = true
|
|
481
|
+
else if (a === '--quiet') quiet = true
|
|
482
|
+
else if (a === '--css') extraCss.push(argv[++i])
|
|
483
|
+
else if (a === '--allow') allow.push(argv[++i])
|
|
484
|
+
else if (a === '--render') urls.push(argv[++i])
|
|
485
|
+
else if (a.startsWith('-')) return console.error(`unknown option ${a}\n${HELP}`), 2
|
|
486
|
+
else roots.push(a)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const cwd = process.cwd()
|
|
490
|
+
const cfg = loadConfig(cwd)
|
|
491
|
+
const wanted = [...urls, ...(urls.length ? [] : cfg.render ?? [])]
|
|
492
|
+
|
|
493
|
+
let found = roots
|
|
494
|
+
if (wanted.length) {
|
|
495
|
+
const dir = mkdtempSync(join(tmpdir(), 'gui-css-check-'))
|
|
496
|
+
try {
|
|
497
|
+
found = await render(wanted, { dir })
|
|
498
|
+
} catch (err) {
|
|
499
|
+
console.error(`gui-css-check: ${err.message}`)
|
|
500
|
+
return 2
|
|
501
|
+
}
|
|
502
|
+
} else if (!found.length) {
|
|
503
|
+
found = DEFAULT_ROOTS.filter((d) => existsSync(join(cwd, d)))
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (!found.length) {
|
|
507
|
+
console.error(
|
|
508
|
+
`gui-css-check: no built output.\n` +
|
|
509
|
+
` Looked for ${DEFAULT_ROOTS.join(', ')} in ${cwd}\n` +
|
|
510
|
+
` Build first, or pass the directory holding the rendered HTML and CSS,\n` +
|
|
511
|
+
` or --render <url> against a running server.`
|
|
512
|
+
)
|
|
513
|
+
return 2
|
|
514
|
+
}
|
|
515
|
+
for (const r of found)
|
|
516
|
+
if (!existsSync(r)) return console.error(`gui-css-check: no such path: ${r}`), 2
|
|
517
|
+
|
|
518
|
+
const res = check({
|
|
519
|
+
roots: found,
|
|
520
|
+
allow: [...(cfg.allow ?? []), ...allow],
|
|
521
|
+
extraCss: [...(cfg.css ?? []), ...extraCss],
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
const nothingMeasured =
|
|
525
|
+
!res.pages.length ||
|
|
526
|
+
res.empty.length === res.pages.length ||
|
|
527
|
+
res.boilerplate.length === res.pages.length
|
|
528
|
+
if (nothingMeasured) {
|
|
529
|
+
console.error(
|
|
530
|
+
`gui-css-check: ` +
|
|
531
|
+
(!res.pages.length
|
|
532
|
+
? `found no rendered HTML under ${found.join(', ')}.\n`
|
|
533
|
+
: res.empty.length === res.pages.length
|
|
534
|
+
? `all ${res.pages.length} document(s) under ${found.join(', ')} use ZERO classes.\n`
|
|
535
|
+
: `the only prerendered page(s) here are the framework's own error pages\n` +
|
|
536
|
+
` (${res.boilerplate.map((r) => basename(r.page)).join(', ')}).\n`) +
|
|
537
|
+
` Nothing about this app was checked, so nothing is proven — that is a\n` +
|
|
538
|
+
` failure, not a pass. Either the routes are all server-rendered or the app\n` +
|
|
539
|
+
` renders on the client. Check the real thing:\n` +
|
|
540
|
+
` gui-css-check --render http://localhost:3000/`
|
|
541
|
+
)
|
|
542
|
+
return 2
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const unresolved = res.results.flatMap((r) => r.unresolved.map((h) => [r.page, h]))
|
|
546
|
+
const total = res.used.size
|
|
547
|
+
const missCount = res.missing.size
|
|
548
|
+
const covered = total - missCount
|
|
549
|
+
const pct = total ? (covered / total) * 100 : 100
|
|
550
|
+
|
|
551
|
+
if (json) {
|
|
552
|
+
console.log(
|
|
553
|
+
JSON.stringify(
|
|
554
|
+
{
|
|
555
|
+
pages: res.pages.length,
|
|
556
|
+
sheets: res.sheets.size,
|
|
557
|
+
bytes: res.bytes,
|
|
558
|
+
used: total,
|
|
559
|
+
covered,
|
|
560
|
+
coverage: Number(pct.toFixed(2)),
|
|
561
|
+
missing: Object.fromEntries(
|
|
562
|
+
[...res.missing].map(([c, pages]) => [c, pages.map((p) => relative(cwd, p))])
|
|
563
|
+
),
|
|
564
|
+
unresolved: unresolved.map(([p, h]) => ({ page: relative(cwd, p), href: h })),
|
|
565
|
+
},
|
|
566
|
+
null,
|
|
567
|
+
2
|
|
568
|
+
)
|
|
569
|
+
)
|
|
570
|
+
return missCount || unresolved.length ? 1 : 0
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const ok = !missCount && !unresolved.length
|
|
574
|
+
if (!ok || !quiet) {
|
|
575
|
+
const b = res.bytes
|
|
576
|
+
console.log(
|
|
577
|
+
`gui-css-check ${num(res.pages.length)} page(s) · ` +
|
|
578
|
+
`${b.staticFiles} cached sheet(s), ${kb(b.static)} · ` +
|
|
579
|
+
`${kb(b.inlinePerPage)} inline per document\n` +
|
|
580
|
+
` ${num(covered)}/${num(total)} classes covered (${pct.toFixed(1)}%)` +
|
|
581
|
+
// 100% of nothing is still 100%, and that is the shape of a page that
|
|
582
|
+
// failed to render. Say so on the same line as the score.
|
|
583
|
+
(res.empty.length ? `\n ${num(res.empty.length)} page(s) use no classes at all` : '')
|
|
584
|
+
)
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (unresolved.length) {
|
|
588
|
+
console.error(`\nFAIL ${unresolved.length} stylesheet link(s) point at a file that is not there:`)
|
|
589
|
+
for (const [page, href] of unresolved.slice(0, 20))
|
|
590
|
+
console.error(` ${relative(cwd, page)} -> ${href}`)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (missCount) {
|
|
594
|
+
console.error(
|
|
595
|
+
`\nFAIL ${num(missCount)} of ${num(total)} classes in the markup have NO rule in any\n` +
|
|
596
|
+
` stylesheet these pages deliver. They render with no styling at all.`
|
|
597
|
+
)
|
|
598
|
+
const bins = KINDS.map(() => [])
|
|
599
|
+
for (const c of res.missing.keys()) bins[KINDS.findIndex((k) => k.test(c))].push(c)
|
|
600
|
+
for (const [i, kind] of KINDS.entries()) {
|
|
601
|
+
const classes = bins[i]
|
|
602
|
+
if (!classes.length) continue
|
|
603
|
+
const where = relative(cwd, res.missing.get(classes[0])[0])
|
|
604
|
+
console.error(`\n ${classes.length}× ${kind.title}`)
|
|
605
|
+
console.error(classes.slice(0, 10).map((c) => ` ${c}`).join('\n'))
|
|
606
|
+
if (classes.length > 10) console.error(` … ${classes.length - 10} more`)
|
|
607
|
+
console.error(` first in ${where}\n${kind.cause}`)
|
|
608
|
+
}
|
|
609
|
+
console.error(
|
|
610
|
+
` A class that really is styled by something this check cannot see belongs\n` +
|
|
611
|
+
` in gui-css-check.json {"allow": [...]}, with a note saying who styles it.\n`
|
|
612
|
+
)
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return ok ? 0 : 1
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const invokedDirectly =
|
|
619
|
+
process.argv[1] &&
|
|
620
|
+
(import.meta.url === `file://${process.argv[1]}` ||
|
|
621
|
+
resolve(process.argv[1]).endsWith(`${sep}css-check.mjs`) ||
|
|
622
|
+
basename(process.argv[1]) === 'gui-css-check')
|
|
623
|
+
|
|
624
|
+
if (invokedDirectly) process.exit(await main(process.argv.slice(2)))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/gui",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.2",
|
|
4
4
|
"gitHead": "a49cc7ea6b93ba384e77a4880ae48ac4a5635c14",
|
|
5
5
|
"description": "Style and UI for React (web and native) meet an optimizing compiler",
|
|
6
6
|
"repository": {
|
|
@@ -9,6 +9,25 @@
|
|
|
9
9
|
"directory": "pkgs/ui/hanzogui"
|
|
10
10
|
},
|
|
11
11
|
"source": "src/index.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"bundle-native.mjs",
|
|
14
|
+
"css-check.mjs",
|
|
15
|
+
"dist",
|
|
16
|
+
"hanzogui.config.ts",
|
|
17
|
+
"linear-gradient",
|
|
18
|
+
"native-test.cjs",
|
|
19
|
+
"native-test.d.ts",
|
|
20
|
+
"native-test",
|
|
21
|
+
"native.cjs",
|
|
22
|
+
"native.d.ts",
|
|
23
|
+
"native",
|
|
24
|
+
"react-native-web.cjs",
|
|
25
|
+
"react-native-web",
|
|
26
|
+
"src",
|
|
27
|
+
"types",
|
|
28
|
+
"web.cjs",
|
|
29
|
+
"web"
|
|
30
|
+
],
|
|
12
31
|
"type": "module",
|
|
13
32
|
"sideEffects": [
|
|
14
33
|
"setup.*"
|
|
@@ -16,8 +35,12 @@
|
|
|
16
35
|
"main": "dist/cjs",
|
|
17
36
|
"module": "dist/esm",
|
|
18
37
|
"types": "./types/index.d.ts",
|
|
38
|
+
"bin": {
|
|
39
|
+
"gui-css-check": "./css-check.mjs"
|
|
40
|
+
},
|
|
19
41
|
"exports": {
|
|
20
42
|
"./package.json": "./package.json",
|
|
43
|
+
"./css-check": "./css-check.mjs",
|
|
21
44
|
".": {
|
|
22
45
|
"types": "./types/index.d.ts",
|
|
23
46
|
"react-native": "./dist/esm/index.native.js",
|
|
@@ -74,71 +97,73 @@
|
|
|
74
97
|
"clean": "hanzogui-build clean",
|
|
75
98
|
"clean:build": "hanzogui-build clean:build",
|
|
76
99
|
"check": "bun run check-circular-deps",
|
|
77
|
-
"check-circular-deps": "npx madge --circular ./src/index.ts"
|
|
100
|
+
"check-circular-deps": "npx madge --circular ./src/index.ts",
|
|
101
|
+
"test": "node --test css-check.test.mjs"
|
|
78
102
|
},
|
|
79
103
|
"dependencies": {
|
|
80
|
-
"@hanzogui/accordion": "8.0.
|
|
81
|
-
"@hanzogui/adapt": "8.0.
|
|
82
|
-
"@hanzogui/alert-dialog": "8.0.
|
|
104
|
+
"@hanzogui/accordion": "8.0.1",
|
|
105
|
+
"@hanzogui/adapt": "8.0.1",
|
|
106
|
+
"@hanzogui/alert-dialog": "8.0.1",
|
|
83
107
|
"@hanzogui/animate": "8.0.0",
|
|
84
108
|
"@hanzogui/animate-presence": "8.0.0",
|
|
85
|
-
"@hanzogui/avatar": "8.0.
|
|
86
|
-
"@hanzogui/button": "8.0.
|
|
87
|
-
"@hanzogui/card": "8.0.
|
|
88
|
-
"@hanzogui/checkbox": "8.0.
|
|
89
|
-
"@hanzogui/collapsible": "8.0.
|
|
109
|
+
"@hanzogui/avatar": "8.0.1",
|
|
110
|
+
"@hanzogui/button": "8.0.1",
|
|
111
|
+
"@hanzogui/card": "8.0.1",
|
|
112
|
+
"@hanzogui/checkbox": "8.0.1",
|
|
113
|
+
"@hanzogui/collapsible": "8.0.1",
|
|
114
|
+
"@hanzogui/component-helpers": "8.0.0",
|
|
90
115
|
"@hanzogui/compose-refs": "8.0.0",
|
|
91
116
|
"@hanzogui/constants": "8.0.0",
|
|
92
|
-
"@hanzogui/context-menu": "8.0.
|
|
93
|
-
"@hanzogui/core": "8.0.
|
|
117
|
+
"@hanzogui/context-menu": "8.0.1",
|
|
118
|
+
"@hanzogui/core": "8.0.1",
|
|
94
119
|
"@hanzogui/create-context": "8.0.0",
|
|
95
|
-
"@hanzogui/create-menu": "8.0.
|
|
96
|
-
"@hanzogui/dialog": "8.0.
|
|
120
|
+
"@hanzogui/create-menu": "8.0.1",
|
|
121
|
+
"@hanzogui/dialog": "8.0.1",
|
|
97
122
|
"@hanzogui/element": "8.0.0",
|
|
98
|
-
"@hanzogui/elements": "8.0.
|
|
123
|
+
"@hanzogui/elements": "8.0.1",
|
|
99
124
|
"@hanzogui/fake-react-native": "8.0.0",
|
|
100
125
|
"@hanzogui/focusable": "8.0.0",
|
|
101
|
-
"@hanzogui/
|
|
126
|
+
"@hanzogui/font-size": "8.0.1",
|
|
127
|
+
"@hanzogui/form": "8.0.1",
|
|
102
128
|
"@hanzogui/get-button-sized": "8.0.0",
|
|
103
129
|
"@hanzogui/get-font-sized": "8.0.0",
|
|
104
130
|
"@hanzogui/get-token": "8.0.0",
|
|
105
|
-
"@hanzogui/group": "8.0.
|
|
106
|
-
"@hanzogui/
|
|
107
|
-
"@hanzogui/
|
|
108
|
-
"@hanzogui/input": "8.0.0",
|
|
131
|
+
"@hanzogui/group": "8.0.1",
|
|
132
|
+
"@hanzogui/image": "8.0.1",
|
|
133
|
+
"@hanzogui/input": "8.0.1",
|
|
109
134
|
"@hanzogui/label": "8.0.0",
|
|
110
|
-
"@hanzogui/linear-gradient": "8.0.
|
|
111
|
-
"@hanzogui/list-item": "8.0.
|
|
112
|
-
"@hanzogui/menu": "8.0.
|
|
135
|
+
"@hanzogui/linear-gradient": "8.0.1",
|
|
136
|
+
"@hanzogui/list-item": "8.0.1",
|
|
137
|
+
"@hanzogui/menu": "8.0.1",
|
|
113
138
|
"@hanzogui/polyfill-dev": "8.0.0",
|
|
114
|
-
"@hanzogui/popover": "8.0.
|
|
115
|
-
"@hanzogui/popper": "8.0.
|
|
116
|
-
"@hanzogui/portal": "8.0.
|
|
117
|
-
"@hanzogui/progress": "8.0.
|
|
118
|
-
"@hanzogui/radio-group": "8.0.
|
|
139
|
+
"@hanzogui/popover": "8.0.1",
|
|
140
|
+
"@hanzogui/popper": "8.0.1",
|
|
141
|
+
"@hanzogui/portal": "8.0.1",
|
|
142
|
+
"@hanzogui/progress": "8.0.1",
|
|
143
|
+
"@hanzogui/radio-group": "8.0.1",
|
|
119
144
|
"@hanzogui/react-native-media-driver": "8.0.0",
|
|
120
|
-
"@hanzogui/scroll-view": "8.0.
|
|
121
|
-
"@hanzogui/select": "8.0.
|
|
122
|
-
"@hanzogui/separator": "8.0.
|
|
123
|
-
"@hanzogui/shapes": "8.0.
|
|
124
|
-
"@hanzogui/sheet": "8.0.
|
|
125
|
-
"@hanzogui/slider": "8.0.
|
|
145
|
+
"@hanzogui/scroll-view": "8.0.1",
|
|
146
|
+
"@hanzogui/select": "8.0.1",
|
|
147
|
+
"@hanzogui/separator": "8.0.1",
|
|
148
|
+
"@hanzogui/shapes": "8.0.1",
|
|
149
|
+
"@hanzogui/sheet": "8.0.1",
|
|
150
|
+
"@hanzogui/slider": "8.0.1",
|
|
126
151
|
"@hanzogui/spacer": "8.0.0",
|
|
127
|
-
"@hanzogui/spinner": "8.0.
|
|
128
|
-
"@hanzogui/stacks": "8.0.
|
|
129
|
-
"@hanzogui/switch": "8.0.
|
|
130
|
-
"@hanzogui/tabs": "8.0.
|
|
152
|
+
"@hanzogui/spinner": "8.0.1",
|
|
153
|
+
"@hanzogui/stacks": "8.0.1",
|
|
154
|
+
"@hanzogui/switch": "8.0.1",
|
|
155
|
+
"@hanzogui/tabs": "8.0.1",
|
|
131
156
|
"@hanzogui/text": "8.0.0",
|
|
132
157
|
"@hanzogui/theme": "8.0.0",
|
|
133
|
-
"@hanzogui/toast": "8.0.
|
|
134
|
-
"@hanzogui/toggle-group": "8.0.
|
|
135
|
-
"@hanzogui/tooltip": "8.0.
|
|
158
|
+
"@hanzogui/toast": "8.0.1",
|
|
159
|
+
"@hanzogui/toggle-group": "8.0.1",
|
|
160
|
+
"@hanzogui/tooltip": "8.0.1",
|
|
136
161
|
"@hanzogui/use-controllable-state": "8.0.0",
|
|
137
162
|
"@hanzogui/use-debounce": "8.0.0",
|
|
138
163
|
"@hanzogui/use-force-update": "8.0.0",
|
|
139
164
|
"@hanzogui/use-window-dimensions": "8.0.0",
|
|
140
165
|
"@hanzogui/visually-hidden": "8.0.0",
|
|
141
|
-
"@hanzogui/
|
|
166
|
+
"@hanzogui/web": "8.0.0",
|
|
142
167
|
"@hanzogui/z-index-stack": "8.0.0"
|
|
143
168
|
},
|
|
144
169
|
"devDependencies": {
|
|
@@ -155,4 +180,4 @@
|
|
|
155
180
|
],
|
|
156
181
|
"module:jsx": "dist/jsx",
|
|
157
182
|
"removeSideEffects": true
|
|
158
|
-
}
|
|
183
|
+
}
|
package/types/views/Anchor.d.ts
CHANGED
|
@@ -5,11 +5,5 @@ export interface AnchorExtraProps {
|
|
|
5
5
|
rel?: string;
|
|
6
6
|
}
|
|
7
7
|
export type AnchorProps = SizableTextProps & AnchorExtraProps;
|
|
8
|
-
export declare const Anchor:
|
|
9
|
-
unstyled?: boolean | undefined;
|
|
10
|
-
size?: import("@hanzogui/core").FontSizeTokens | undefined;
|
|
11
|
-
}>, keyof AnchorExtraProps> & AnchorExtraProps, import("@hanzogui/core").GuiTextElement, import("@hanzogui/core").TextNonStyleProps & AnchorExtraProps, import("@hanzogui/core").TextStylePropsBase, {
|
|
12
|
-
unstyled?: boolean | undefined;
|
|
13
|
-
size?: import("@hanzogui/core").FontSizeTokens | undefined;
|
|
14
|
-
}, import("@hanzogui/core").StaticConfigPublic>;
|
|
8
|
+
export declare const Anchor: any;
|
|
15
9
|
//# sourceMappingURL=Anchor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Anchor.d.ts","sourceRoot":"","sources":["../../src/views/Anchor.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAItD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,gBAAgB,CAAA;AAQ7D,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"Anchor.d.ts","sourceRoot":"","sources":["../../src/views/Anchor.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAItD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,gBAAgB,CAAA;AAQ7D,eAAO,MAAM,MAAM,KAuBlB,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const EnsureFlexed:
|
|
1
|
+
export declare const EnsureFlexed: any;
|
|
2
2
|
//# sourceMappingURL=EnsureFlexed.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnsureFlexed.d.ts","sourceRoot":"","sources":["../../src/views/EnsureFlexed.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"EnsureFlexed.d.ts","sourceRoot":"","sources":["../../src/views/EnsureFlexed.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,KAQvB,CAAA"}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import type { GetProps } from '@hanzogui/core';
|
|
2
|
-
export declare const Fieldset:
|
|
3
|
-
elevation?: number | import("@hanzogui/core").SizeTokens | undefined;
|
|
4
|
-
fullscreen?: boolean | undefined;
|
|
5
|
-
horizontal?: boolean | undefined;
|
|
6
|
-
}, import("@hanzogui/core").StaticConfigPublic>;
|
|
2
|
+
export declare const Fieldset: any;
|
|
7
3
|
export type FieldsetProps = GetProps<typeof Fieldset>;
|
|
8
4
|
//# sourceMappingURL=Fieldset.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Fieldset.d.ts","sourceRoot":"","sources":["../../src/views/Fieldset.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAI9C,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"Fieldset.d.ts","sourceRoot":"","sources":["../../src/views/Fieldset.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAI9C,eAAO,MAAM,QAAQ,KAenB,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,QAAQ,CAAC,CAAA"}
|
package/types/views/Text.d.ts
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
export declare const Text:
|
|
2
|
-
unstyled?: boolean | undefined;
|
|
3
|
-
}, import("@hanzogui/core").StaticConfigPublic>;
|
|
1
|
+
export declare const Text: any;
|
|
4
2
|
//# sourceMappingURL=Text.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Text.d.ts","sourceRoot":"","sources":["../../src/views/Text.tsx"],"names":[],"mappings":"AAEA,eAAO,MAAM,IAAI
|
|
1
|
+
{"version":3,"file":"Text.d.ts","sourceRoot":"","sources":["../../src/views/Text.tsx"],"names":[],"mappings":"AAEA,eAAO,MAAM,IAAI,KAYf,CAAA"}
|
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
export declare const VisuallyHidden:
|
|
2
|
-
visible?: boolean | undefined;
|
|
3
|
-
preserveDimensions?: boolean | undefined;
|
|
4
|
-
}, import("@hanzogui/core").StaticConfigPublic>;
|
|
1
|
+
export declare const VisuallyHidden: any;
|
|
5
2
|
//# sourceMappingURL=VisuallyHidden.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VisuallyHidden.d.ts","sourceRoot":"","sources":["../../src/views/VisuallyHidden.tsx"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"VisuallyHidden.d.ts","sourceRoot":"","sources":["../../src/views/VisuallyHidden.tsx"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,KAgCzB,CAAA"}
|
package/.turbo/turbo-build.log
DELETED
package/tsconfig.json
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"composite": true
|
|
5
|
-
},
|
|
6
|
-
"exclude": [
|
|
7
|
-
"hanzogui.config.ts",
|
|
8
|
-
"test",
|
|
9
|
-
"dist",
|
|
10
|
-
"types",
|
|
11
|
-
"./linear-gradient",
|
|
12
|
-
"./native.d.ts",
|
|
13
|
-
"./native-test.d.ts",
|
|
14
|
-
"**/__tests__"
|
|
15
|
-
],
|
|
16
|
-
"references": [
|
|
17
|
-
{
|
|
18
|
-
"path": "../../core/compose-refs"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"path": "../../core/constants"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"path": "../../core/core"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"path": "../../core/create-context"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"path": "../../core/font-size"
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"path": "../../core/get-button-sized"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"path": "../../core/get-font-sized"
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"path": "../../core/get-token"
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"path": "../../core/component-helpers"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"path": "../../core/react-native-media-driver"
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"path": "../../core/theme"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"path": "../../core/use-controllable-state"
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"path": "../../core/use-debounce"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"path": "../../core/use-force-update"
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"path": "../../core/use-window-dimensions"
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
"path": "../../element"
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"path": "../accordion"
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
"path": "../adapt"
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
"path": "../alert-dialog"
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"path": "../animate-presence"
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
"path": "../avatar"
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"path": "../button"
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"path": "../card"
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
"path": "../checkbox"
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
"path": "../dialog"
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"path": "../elements"
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
"path": "../focusable"
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
"path": "../form"
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
"path": "../group"
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
"path": "../image"
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
"path": "../label"
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
"path": "../linear-gradient"
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
"path": "../list-item"
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
"path": "../popover"
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
"path": "../popper"
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
"path": "../portal"
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
"path": "../progress"
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
"path": "../radio-group"
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
"path": "../scroll-view"
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
"path": "../select"
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
"path": "../separator"
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
"path": "../shapes"
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
"path": "../sheet"
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
"path": "../slider"
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
"path": "../stacks"
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
"path": "../switch"
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
"path": "../tabs"
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
"path": "../text"
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
"path": "../toggle-group"
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
"path": "../tooltip"
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
"path": "../visually-hidden"
|
|
169
|
-
}
|
|
170
|
-
]
|
|
171
|
-
}
|