@webdesignhot/design-md 0.4.0 → 0.5.1
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/README.md +4 -7
- package/package.json +15 -3
- package/src/commands/add.mjs +9 -2
- package/src/commands/diff.mjs +21 -2
- package/src/commands/export.mjs +26 -19
- package/src/commands/help.mjs +1 -1
- package/src/commands/lint.mjs +59 -14
- package/src/commands/submit.mjs +10 -6
- package/src/commands/theme.mjs +19 -3
- package/src/index.mjs +1 -1
package/README.md
CHANGED
|
@@ -64,12 +64,9 @@ export <file> --to <tailwind|css-tailwind|json-tailwind|css|dtcg|figma> [--tailw
|
|
|
64
64
|
Pass --tailwind-version v3 for the legacy
|
|
65
65
|
module.exports = { theme: { extend: { ... } } } shape.
|
|
66
66
|
css-tailwind — v4 @theme {} CSS block (alias of `--to tailwind`,
|
|
67
|
-
|
|
68
|
-
naming from PR #64 so commands transfer between
|
|
69
|
-
the two CLIs without re-learning flag conventions).
|
|
67
|
+
version-explicit naming for clarity in scripts).
|
|
70
68
|
json-tailwind — v3 module.exports JSON (alias of
|
|
71
|
-
`--to tailwind --tailwind-version v3
|
|
72
|
-
Google CLI compat reason as above).
|
|
69
|
+
`--to tailwind --tailwind-version v3`).
|
|
73
70
|
css — :root { --color-bg, --radius-card, … }
|
|
74
71
|
dtcg — W3C Design Tokens Community Group JSON
|
|
75
72
|
figma — Figma Variables import format
|
|
@@ -79,8 +76,8 @@ extract <url> [-o <path>] [--token-only]
|
|
|
79
76
|
(Requires a webdesignhot session — opens the browser flow.)
|
|
80
77
|
|
|
81
78
|
import <url>
|
|
82
|
-
Alias of `extract`.
|
|
83
|
-
|
|
79
|
+
Alias of `extract`. Common `import` verb so the muscle memory carries
|
|
80
|
+
over from other dev tools.
|
|
84
81
|
|
|
85
82
|
theme <slug> [--dark|--light]
|
|
86
83
|
Compute a dark/light variant of any design.
|
package/package.json
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webdesignhot/design-md",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Drop any DESIGN.md into your repo in one command. Browse, install, lint, diff, and export design systems extracted from real production sites.",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"design.md",
|
|
7
|
+
"design-system",
|
|
8
|
+
"design-tokens",
|
|
9
|
+
"ai-agents",
|
|
10
|
+
"cli",
|
|
11
|
+
"tailwind",
|
|
12
|
+
"figma",
|
|
13
|
+
"dtcg"
|
|
14
|
+
],
|
|
6
15
|
"author": "webdesignhot",
|
|
7
16
|
"license": "MIT",
|
|
8
17
|
"homepage": "https://www.webdesignhot.com/design.md/",
|
|
9
18
|
"repository": {
|
|
10
19
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/WebDesignHot/design-md"
|
|
20
|
+
"url": "git+https://github.com/WebDesignHot/design-md.git"
|
|
12
21
|
},
|
|
13
22
|
"bin": {
|
|
14
23
|
"design-md": "bin/design-md.mjs"
|
|
@@ -23,6 +32,9 @@
|
|
|
23
32
|
"engines": {
|
|
24
33
|
"node": ">=18"
|
|
25
34
|
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"prepublishOnly": "node -e \"const p=require('./package.json');const e=require('child_process').execSync;let r='0.0.0';try{r=e('npm view '+p.name+' version 2>/dev/null').toString().trim()||'0.0.0'}catch(_){};if(p.version===r){console.error('\\n\\x1b[31m✗ '+p.name+'@'+p.version+' already on npm. Bump version before publishing.\\x1b[0m\\n');process.exit(1)}console.log('→ '+p.name+': '+r+' → '+p.version)\""
|
|
37
|
+
},
|
|
26
38
|
"dependencies": {
|
|
27
39
|
"@clack/prompts": "^0.7.0",
|
|
28
40
|
"kleur": "^4.1.5",
|
package/src/commands/add.mjs
CHANGED
|
@@ -8,8 +8,15 @@ function parseFlags(args) {
|
|
|
8
8
|
const positional = []
|
|
9
9
|
for (let i = 0; i < args.length; i++) {
|
|
10
10
|
const a = args[i]
|
|
11
|
-
if (a === '-o' || a === '--out')
|
|
12
|
-
|
|
11
|
+
if (a === '-o' || a === '--out') {
|
|
12
|
+
const value = args[++i]
|
|
13
|
+
if (value === undefined || value.startsWith('-')) {
|
|
14
|
+
console.error(`Usage: design-md add <slug> [-o <path>] [-f]`)
|
|
15
|
+
console.error(`Error: ${a} requires a <path> value.`)
|
|
16
|
+
process.exit(2)
|
|
17
|
+
}
|
|
18
|
+
flags.out = value
|
|
19
|
+
} else if (a === '-f' || a === '--force') flags.force = true
|
|
13
20
|
else positional.push(a)
|
|
14
21
|
}
|
|
15
22
|
return { flags, positional }
|
package/src/commands/diff.mjs
CHANGED
|
@@ -14,6 +14,19 @@ function parseFlags(args) {
|
|
|
14
14
|
return { flags, positional }
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function flatten(obj, prefix = '') {
|
|
18
|
+
const out = {}
|
|
19
|
+
if (!obj || typeof obj !== 'object') return out
|
|
20
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
21
|
+
const key = prefix ? `${prefix}.${k}` : k
|
|
22
|
+
if (v == null) continue
|
|
23
|
+
if (Array.isArray(v)) out[key] = `[${v.join(', ')}]`
|
|
24
|
+
else if (typeof v === 'object') Object.assign(out, flatten(v, key))
|
|
25
|
+
else out[key] = String(v)
|
|
26
|
+
}
|
|
27
|
+
return out
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
function diffMaps(left, right) {
|
|
18
31
|
const result = { added: {}, removed: {}, modified: {} }
|
|
19
32
|
const keys = new Set([...Object.keys(left), ...Object.keys(right)])
|
|
@@ -39,10 +52,13 @@ export const diff = {
|
|
|
39
52
|
const A = await readDesignMd(resolve(process.cwd(), a))
|
|
40
53
|
const B = await readDesignMd(resolve(process.cwd(), b))
|
|
41
54
|
const colors = diffMaps(flattenColors(A.data), flattenColors(B.data))
|
|
42
|
-
const radii = diffMaps(A.data.radius ?? {}, B.data.radius ?? {})
|
|
55
|
+
const radii = diffMaps(flatten(A.data.radius ?? {}), flatten(B.data.radius ?? {}))
|
|
56
|
+
const typography = diffMaps(flatten(A.data.typography ?? {}), flatten(B.data.typography ?? {}))
|
|
57
|
+
const spacing = diffMaps(flatten(A.data.spacing ?? {}), flatten(B.data.spacing ?? {}))
|
|
58
|
+
const components = diffMaps(flatten(A.data.components ?? {}), flatten(B.data.components ?? {}))
|
|
43
59
|
|
|
44
60
|
if (flags.format === 'json') {
|
|
45
|
-
console.log(JSON.stringify({ colors, radii }, null, 2))
|
|
61
|
+
console.log(JSON.stringify({ colors, radii, typography, spacing, components }, null, 2))
|
|
46
62
|
return
|
|
47
63
|
}
|
|
48
64
|
|
|
@@ -61,5 +77,8 @@ export const diff = {
|
|
|
61
77
|
|
|
62
78
|
printSection('Colors', colors)
|
|
63
79
|
printSection('Radii', radii)
|
|
80
|
+
printSection('Typography', typography)
|
|
81
|
+
printSection('Spacing', spacing)
|
|
82
|
+
printSection('Components', components)
|
|
64
83
|
},
|
|
65
84
|
}
|
package/src/commands/export.mjs
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
2
|
import { readDesignMd } from '../lib/parse.mjs'
|
|
3
3
|
|
|
4
|
-
// Format
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
4
|
+
// Format flag accepts `tailwind` (our default v4 CSS) plus the explicit
|
|
5
|
+
// `css-tailwind` / `json-tailwind` aliases so callers can be version-
|
|
6
|
+
// explicit without re-learning conventions. Underlying renderer is the
|
|
7
|
+
// same — only the output shape changes.
|
|
8
8
|
//
|
|
9
|
-
// `--to tailwind` →
|
|
10
|
-
//
|
|
11
|
-
// `--to
|
|
12
|
-
// `--to json-tailwind` → v3 JSON theme.extend (Google compat)
|
|
9
|
+
// `--to tailwind` → v4 CSS @theme block (default)
|
|
10
|
+
// `--to css-tailwind` → v4 CSS @theme block (explicit)
|
|
11
|
+
// `--to json-tailwind` → v3 JSON theme.extend
|
|
13
12
|
// `--to tailwind --tailwind-version v3` → v3 JSON (legacy flag, kept)
|
|
14
13
|
const FORMATS = new Set(['tailwind', 'css-tailwind', 'json-tailwind', 'css', 'dtcg', 'figma'])
|
|
15
14
|
const TAILWIND_VERSIONS = new Set(['v3', 'v4'])
|
|
@@ -29,10 +28,12 @@ function parseFlags(args) {
|
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
function toCss(data) {
|
|
32
|
-
const c = data.colors ?? {}
|
|
31
|
+
const c = flattenColors(data.colors ?? {})
|
|
33
32
|
const r = data.radius ?? {}
|
|
34
33
|
const lines = [':root {']
|
|
35
|
-
for (const [k, v] of Object.entries(c))
|
|
34
|
+
for (const [k, v] of Object.entries(c)) {
|
|
35
|
+
if (typeof v === 'string') lines.push(` --color-${k}: ${v};`)
|
|
36
|
+
}
|
|
36
37
|
for (const [k, v] of Object.entries(r)) lines.push(` --radius-${k}: ${typeof v === 'number' ? `${v}px` : v};`)
|
|
37
38
|
lines.push('}')
|
|
38
39
|
return lines.join('\n')
|
|
@@ -90,11 +91,15 @@ function toTailwind(data, version = 'v4') {
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
function toDtcg(data) {
|
|
93
|
-
const c = data.colors ?? {}
|
|
94
|
+
const c = flattenColors(data.colors ?? {})
|
|
94
95
|
const r = data.radius ?? {}
|
|
95
96
|
const out = {
|
|
96
97
|
$schema: 'https://design-tokens.github.io/community-group/format/',
|
|
97
|
-
color: Object.fromEntries(
|
|
98
|
+
color: Object.fromEntries(
|
|
99
|
+
Object.entries(c)
|
|
100
|
+
.filter(([, v]) => typeof v === 'string')
|
|
101
|
+
.map(([k, v]) => [k, { $value: v, $type: 'color' }]),
|
|
102
|
+
),
|
|
98
103
|
radius: Object.fromEntries(
|
|
99
104
|
Object.entries(r).map(([k, v]) => [
|
|
100
105
|
k,
|
|
@@ -106,14 +111,16 @@ function toDtcg(data) {
|
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
function toFigma(data) {
|
|
109
|
-
const c = data.colors ?? {}
|
|
114
|
+
const c = flattenColors(data.colors ?? {})
|
|
110
115
|
const out = {
|
|
111
|
-
variables: Object.entries(c)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
variables: Object.entries(c)
|
|
117
|
+
.filter(([, v]) => typeof v === 'string')
|
|
118
|
+
.map(([k, v]) => ({
|
|
119
|
+
name: k,
|
|
120
|
+
type: 'COLOR',
|
|
121
|
+
value: v,
|
|
122
|
+
description: `${data.name} · ${k}`,
|
|
123
|
+
})),
|
|
117
124
|
}
|
|
118
125
|
return JSON.stringify(out, null, 2)
|
|
119
126
|
}
|
package/src/commands/help.mjs
CHANGED
|
@@ -21,7 +21,7 @@ ${kleur.dim('COMMANDS')}
|
|
|
21
21
|
dtcg W3C Design Tokens JSON
|
|
22
22
|
figma Figma Variables import format
|
|
23
23
|
extract <url> Extract a draft DESIGN.md from a production URL
|
|
24
|
-
import <url> Alias of extract
|
|
24
|
+
import <url> Alias of extract
|
|
25
25
|
theme <slug> --dark Compute a dark-mode counterpart of a light design
|
|
26
26
|
preview <slug> Open the directory detail page in your browser
|
|
27
27
|
submit <file> Open a PR to add your DESIGN.md to the public catalog
|
package/src/commands/lint.mjs
CHANGED
|
@@ -1,10 +1,52 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
2
3
|
import kleur from 'kleur'
|
|
4
|
+
import yaml from 'yaml'
|
|
3
5
|
import { readDesignMd } from '../lib/parse.mjs'
|
|
4
6
|
|
|
5
7
|
const REQUIRED = ['name', 'tagline', 'spec', 'colors', 'typography']
|
|
6
8
|
const SECTIONS_RE = /^##\s+(.+)$/gm
|
|
7
9
|
|
|
10
|
+
// Pure function: lint raw DESIGN.md content (string).
|
|
11
|
+
// Returns { findings, summary } shaped for programmatic callers
|
|
12
|
+
// (the extract pipeline uses this to surface warnings on drafts).
|
|
13
|
+
export function lintContent(content) {
|
|
14
|
+
const findings = []
|
|
15
|
+
let data = {}
|
|
16
|
+
let body = content
|
|
17
|
+
|
|
18
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/)
|
|
19
|
+
if (fmMatch) {
|
|
20
|
+
try {
|
|
21
|
+
data = yaml.parse(fmMatch[1]) ?? {}
|
|
22
|
+
body = fmMatch[2]
|
|
23
|
+
} catch (err) {
|
|
24
|
+
findings.push({ level: 'error', code: 'invalid-frontmatter', msg: `YAML parse error: ${err.message}` })
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
findings.push({ level: 'error', code: 'missing-frontmatter', msg: 'No YAML frontmatter found.' })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const k of REQUIRED) {
|
|
31
|
+
if (data[k] == null) findings.push({ level: 'error', code: 'missing-field', field: k, msg: `Required field "${k}" is missing.` })
|
|
32
|
+
}
|
|
33
|
+
if (data.colors) {
|
|
34
|
+
if (!data.colors.bg) findings.push({ level: 'warn', code: 'missing-color-role', msg: 'colors.bg is recommended.' })
|
|
35
|
+
if (!data.colors.text) findings.push({ level: 'warn', code: 'missing-color-role', msg: 'colors.text is recommended.' })
|
|
36
|
+
if (!data.colors.brand && !data.colors.primary) findings.push({ level: 'warn', code: 'missing-primary', msg: 'A primary brand color (colors.brand or colors.primary) is recommended.' })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const sections = [...body.matchAll(SECTIONS_RE)].map((m) => m[1].trim())
|
|
40
|
+
if (sections.length < 3) findings.push({ level: 'warn', code: 'few-sections', msg: `Only ${sections.length} ## sections — recommend 5+.` })
|
|
41
|
+
|
|
42
|
+
const summary = {
|
|
43
|
+
errors: findings.filter((f) => f.level === 'error').length,
|
|
44
|
+
warnings: findings.filter((f) => f.level === 'warn').length,
|
|
45
|
+
infos: findings.filter((f) => f.level === 'info').length,
|
|
46
|
+
}
|
|
47
|
+
return { findings, summary, sections }
|
|
48
|
+
}
|
|
49
|
+
|
|
8
50
|
function parseFlags(args) {
|
|
9
51
|
const flags = { format: 'text' }
|
|
10
52
|
const positional = []
|
|
@@ -26,22 +68,25 @@ export const lint = {
|
|
|
26
68
|
console.error('Usage: design-md lint <file>')
|
|
27
69
|
process.exit(2)
|
|
28
70
|
}
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
for
|
|
33
|
-
|
|
71
|
+
const filePath = resolve(process.cwd(), file)
|
|
72
|
+
const content = await readFile(filePath, 'utf8')
|
|
73
|
+
const { findings, summary, sections } = lintContent(content)
|
|
74
|
+
// Also resolve via readDesignMd for the success-line color/type counts.
|
|
75
|
+
// readDesignMd → parseDesignMd THROWS on missing/invalid frontmatter —
|
|
76
|
+
// exactly the malformed inputs lint exists to diagnose. Swallow that here
|
|
77
|
+
// so findings + summary always print (and --format=json always emits);
|
|
78
|
+
// the findings already record those problems. Exit code stays driven off
|
|
79
|
+
// findings below, not this parse.
|
|
80
|
+
let data = {}
|
|
81
|
+
try {
|
|
82
|
+
;({ data } = await readDesignMd(filePath))
|
|
83
|
+
} catch {
|
|
84
|
+
data = {}
|
|
34
85
|
}
|
|
35
|
-
if (data.colors && !data.colors.bg) findings.push({ level: 'warn', code: 'missing-color-role', msg: 'colors.bg is recommended.' })
|
|
36
|
-
if (data.colors && !data.colors.text) findings.push({ level: 'warn', code: 'missing-color-role', msg: 'colors.text is recommended.' })
|
|
37
|
-
if (data.colors && !data.colors.brand) findings.push({ level: 'warn', code: 'missing-color-role', msg: 'colors.brand is recommended.' })
|
|
38
|
-
|
|
39
|
-
const sections = [...body.matchAll(SECTIONS_RE)].map((m) => m[1].trim())
|
|
40
|
-
if (sections.length < 3) findings.push({ level: 'warn', code: 'few-sections', msg: `Only ${sections.length} ## sections — recommend 5+.` })
|
|
41
86
|
|
|
42
87
|
if (flags.format === 'json') {
|
|
43
|
-
console.log(JSON.stringify({ file, findings, sections }, null, 2))
|
|
44
|
-
process.exit(
|
|
88
|
+
console.log(JSON.stringify({ file, findings, summary, sections }, null, 2))
|
|
89
|
+
process.exit(summary.errors ? 1 : 0)
|
|
45
90
|
}
|
|
46
91
|
|
|
47
92
|
if (!findings.length) {
|
|
@@ -53,6 +98,6 @@ export const lint = {
|
|
|
53
98
|
const tag = f.level === 'error' ? kleur.red('✗ error') : kleur.yellow('⚠ warn ')
|
|
54
99
|
console.log(`${tag} ${kleur.dim(f.code)} ${f.msg}`)
|
|
55
100
|
}
|
|
56
|
-
if (
|
|
101
|
+
if (summary.errors) process.exit(1)
|
|
57
102
|
},
|
|
58
103
|
}
|
package/src/commands/submit.mjs
CHANGED
|
@@ -37,9 +37,14 @@ function parseFlags(args) {
|
|
|
37
37
|
return { flags, positional }
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
// Cross-platform tool detection: probe the tool itself rather than relying on
|
|
41
|
+
// POSIX `which`. Native Windows shells (cmd.exe / PowerShell) have no `which`
|
|
42
|
+
// (they use `where`), so `spawnSync('which', ...)` returns ENOENT and would
|
|
43
|
+
// wrongly report an installed tool as missing. Running `<cmd> --version` and
|
|
44
|
+
// checking exit 0 (with no spawn error) works on macOS, Linux, and Windows.
|
|
45
|
+
function isInstalled(cmd) {
|
|
46
|
+
const r = spawnSync(cmd, ['--version'], { encoding: 'utf8' })
|
|
47
|
+
return !r.error && r.status === 0
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
function run(cmd, args, opts = {}) {
|
|
@@ -113,9 +118,8 @@ export const submit = {
|
|
|
113
118
|
|
|
114
119
|
console.log(kleur.dim(`→ Submitting ${kleur.bold(data.name)} as slug "${kleur.bold(slug)}"`))
|
|
115
120
|
|
|
116
|
-
// 3. Check `gh` CLI
|
|
117
|
-
|
|
118
|
-
if (!ghPath) {
|
|
121
|
+
// 3. Check `gh` CLI (cross-platform; spawn resolves `gh` from PATH later)
|
|
122
|
+
if (!isInstalled('gh')) {
|
|
119
123
|
console.error()
|
|
120
124
|
console.error(kleur.yellow('⚠ The GitHub CLI (`gh`) is not installed.'))
|
|
121
125
|
console.error()
|
package/src/commands/theme.mjs
CHANGED
|
@@ -13,8 +13,21 @@ function parseFlags(args) {
|
|
|
13
13
|
return { flags, positional }
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
function flattenColors(c) {
|
|
17
|
+
// Multi-theme: pick the default theme (or first one) so we invert the
|
|
18
|
+
// actual token strings rather than copying theme objects verbatim.
|
|
19
|
+
// Mirrors flattenColors in export.mjs.
|
|
20
|
+
const values = Object.values(c)
|
|
21
|
+
if (values.length > 0 && values.every((v) => v && typeof v === 'object')) {
|
|
22
|
+
return values[0]
|
|
23
|
+
}
|
|
24
|
+
return c
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
function hexToRgb(h) {
|
|
17
|
-
|
|
28
|
+
// Expand 3-digit shorthand (e.g. `fff` → `ffffff`) before matching.
|
|
29
|
+
const expanded = h.replace(/^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i, '#$1$1$2$2$3$3')
|
|
30
|
+
const m = expanded.match(/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i)
|
|
18
31
|
if (!m) return null
|
|
19
32
|
return [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)]
|
|
20
33
|
}
|
|
@@ -43,7 +56,7 @@ export const theme = {
|
|
|
43
56
|
|
|
44
57
|
const raw = await fetchRawMd(slug)
|
|
45
58
|
const { data } = parseDesignMd(raw)
|
|
46
|
-
const c = data.colors ?? {}
|
|
59
|
+
const c = flattenColors(data.colors ?? {})
|
|
47
60
|
const flipped = {}
|
|
48
61
|
for (const [k, v] of Object.entries(c)) {
|
|
49
62
|
if (typeof v === 'string' && v.startsWith('#')) flipped[k] = invertLuma(v)
|
|
@@ -52,7 +65,10 @@ export const theme = {
|
|
|
52
65
|
console.log(kleur.dim(`# ${data.name} · ${flags.dark ? 'dark' : 'light'} variant (computed)`))
|
|
53
66
|
console.log()
|
|
54
67
|
console.log('colors:')
|
|
55
|
-
for (const [k, v] of Object.entries(flipped))
|
|
68
|
+
for (const [k, v] of Object.entries(flipped)) {
|
|
69
|
+
if (typeof v !== 'string') continue
|
|
70
|
+
console.log(` ${k}: '${v}'`)
|
|
71
|
+
}
|
|
56
72
|
console.log()
|
|
57
73
|
console.log(kleur.yellow('Note: this is a naive luminance flip. Refine the result by hand for production.'))
|
|
58
74
|
},
|
package/src/index.mjs
CHANGED