| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 |
- // 票 15 盘点(只读):仅扫描 <style> 块与 .css 文件中的裸尺寸(字号 / 间距 / 关键几何)
- import { readFileSync, readdirSync, statSync } from 'node:fs'
- import { join, relative } from 'node:path'
- const root = 'src/modules/cockpit'
- const files = []
- ;(function walk(dir) {
- for (const e of readdirSync(dir)) {
- const p = join(dir, e)
- if (statSync(p).isDirectory()) walk(p)
- else if (/\.(vue|css)$/.test(e)) files.push(p)
- }
- })(root)
- function styleBlocks(src) {
- const out = []
- for (const m of src.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/g)) out.push(m[1])
- if (/\.css$/.test('x.css') && !/<style/.test(src)) out.push(src)
- return out
- }
- const fontSizes = new Map()
- const decls = []
- for (const f of files) {
- const src = readFileSync(f, 'utf8')
- const rel = relative('.', f)
- for (const block of styleBlocks(src)) {
- for (const m of block.matchAll(/font-size:\s*([^;]+);/g)) {
- const v = m[1].trim()
- fontSizes.set(v, (fontSizes.get(v) || 0) + 1)
- }
- for (const m of block.matchAll(/(gap|padding|margin|width|height|min-height|min-width|max-height|max-width|letter-spacing):\s*([^;]+);/g)) {
- const v = m[2].trim()
- if (/var\(--/.test(v) || /^0$|^auto$|^100%$|^inherit$/.test(v)) continue
- if (!/\d+(px|u\b)/.test(v)) continue
- decls.push({ file: rel, prop: m[1], value: v })
- }
- }
- }
- console.log('=== font-size 声明(按出现次数)')
- console.log([...fontSizes.entries()].sort((a, b) => b[1] - a[1]).map(([k, v]) => `${k} ×${v}`).join('\n'))
- const byFile = new Map()
- for (const d of decls) {
- const cur = byFile.get(d.file) || []
- cur.push(`${d.prop}: ${d.value}`)
- byFile.set(d.file, cur)
- }
- console.log('\n=== 裸尺寸声明(非 var,按文件)')
- for (const [file, list] of [...byFile.entries()].sort((a, b) => b[1].length - a[1].length)) {
- console.log(`\n ${file} (${list.length})`)
- console.log(' ' + list.slice(0, 30).join('\n '))
- }
- console.log(`\n合计 ${decls.length} 条裸尺寸声明,跨 ${byFile.size} 个文件`)
|