@webdesignhot/design-md 0.2.0 → 0.4.0
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 +23 -9
- package/package.json +1 -1
- package/src/commands/export.mjs +24 -6
- package/src/commands/help.mjs +10 -3
- package/src/commands/submit.mjs +240 -0
- package/src/index.mjs +2 -0
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Drop any DESIGN.md into your repo in one command. Browse, install, lint, diff, and export design systems extracted from real production sites.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
$ npx @webdesignhot/design-md add
|
|
6
|
+
$ npx @webdesignhot/design-md add stripe
|
|
7
7
|
✓ Wrote DESIGN.md (8.2 KB · linear)
|
|
8
8
|
|
|
9
9
|
$ npx @webdesignhot/design-md list
|
|
@@ -31,7 +31,7 @@ npx @webdesignhot/design-md <command>
|
|
|
31
31
|
# global
|
|
32
32
|
npm i -g @webdesignhot/design-md
|
|
33
33
|
# the binary is `design-md` regardless of scope:
|
|
34
|
-
design-md add
|
|
34
|
+
design-md add stripe
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
Requires Node 18+.
|
|
@@ -58,14 +58,21 @@ lint <file> [--format=text|json]
|
|
|
58
58
|
diff <a> <b> [--format=text|json]
|
|
59
59
|
Token-level diff between two DESIGN.md files.
|
|
60
60
|
|
|
61
|
-
export <file> --to <tailwind|css|dtcg|figma> [--tailwind-version <v3|v4>]
|
|
61
|
+
export <file> --to <tailwind|css-tailwind|json-tailwind|css|dtcg|figma> [--tailwind-version <v3|v4>]
|
|
62
62
|
Convert tokens to one of:
|
|
63
|
-
tailwind
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
css
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
tailwind — @theme {} CSS block (Tailwind v4 default).
|
|
64
|
+
Pass --tailwind-version v3 for the legacy
|
|
65
|
+
module.exports = { theme: { extend: { ... } } } shape.
|
|
66
|
+
css-tailwind — v4 @theme {} CSS block (alias of `--to tailwind`,
|
|
67
|
+
matches @google/design.md's `--format css-tailwind`
|
|
68
|
+
naming from PR #64 so commands transfer between
|
|
69
|
+
the two CLIs without re-learning flag conventions).
|
|
70
|
+
json-tailwind — v3 module.exports JSON (alias of
|
|
71
|
+
`--to tailwind --tailwind-version v3`, same
|
|
72
|
+
Google CLI compat reason as above).
|
|
73
|
+
css — :root { --color-bg, --radius-card, … }
|
|
74
|
+
dtcg — W3C Design Tokens Community Group JSON
|
|
75
|
+
figma — Figma Variables import format
|
|
69
76
|
|
|
70
77
|
extract <url> [-o <path>] [--token-only]
|
|
71
78
|
Extract a draft DESIGN.md from any production URL.
|
|
@@ -78,6 +85,13 @@ import <url>
|
|
|
78
85
|
theme <slug> [--dark|--light]
|
|
79
86
|
Compute a dark/light variant of any design.
|
|
80
87
|
|
|
88
|
+
submit <file> [--dry-run]
|
|
89
|
+
Submit your DESIGN.md as a PR to the public catalog at
|
|
90
|
+
github.com/WebDesignHot/design-md. Auto-forks via the `gh` CLI,
|
|
91
|
+
syncs the fork with upstream main, branches, commits, pushes,
|
|
92
|
+
and opens a PR with templated body. Requires `gh` installed and
|
|
93
|
+
authed (https://cli.github.com).
|
|
94
|
+
|
|
81
95
|
preview <slug>
|
|
82
96
|
Open the directory detail page in your browser.
|
|
83
97
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webdesignhot/design-md",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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
5
|
"keywords": ["design.md", "design-system", "design-tokens", "ai-agents", "cli", "tailwind", "figma", "dtcg"],
|
|
6
6
|
"author": "webdesignhot",
|
package/src/commands/export.mjs
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
2
|
import { readDesignMd } from '../lib/parse.mjs'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// Format aliases align our CLI with @google/design.md's PR #64 naming
|
|
5
|
+
// (`--format css-tailwind` / `--format json-tailwind`) so users can move
|
|
6
|
+
// between the two CLIs without re-learning flag conventions. Both shapes
|
|
7
|
+
// are accepted; the underlying renderer is the same.
|
|
8
|
+
//
|
|
9
|
+
// `--to tailwind` → defaults to v4 CSS (was v3 JSON pre-0.2;
|
|
10
|
+
// same default we set in 0.2 still applies)
|
|
11
|
+
// `--to css-tailwind` → v4 CSS @theme block (Google compat)
|
|
12
|
+
// `--to json-tailwind` → v3 JSON theme.extend (Google compat)
|
|
13
|
+
// `--to tailwind --tailwind-version v3` → v3 JSON (legacy flag, kept)
|
|
14
|
+
const FORMATS = new Set(['tailwind', 'css-tailwind', 'json-tailwind', 'css', 'dtcg', 'figma'])
|
|
5
15
|
const TAILWIND_VERSIONS = new Set(['v3', 'v4'])
|
|
6
16
|
|
|
7
17
|
function parseFlags(args) {
|
|
@@ -111,12 +121,12 @@ function toFigma(data) {
|
|
|
111
121
|
const RENDERERS = { tailwind: toTailwind, css: toCss, dtcg: toDtcg, figma: toFigma }
|
|
112
122
|
|
|
113
123
|
export const exportTokens = {
|
|
114
|
-
usage: 'export <file> --to <tailwind|css|dtcg|figma> [--tailwind-version v3|v4]',
|
|
124
|
+
usage: 'export <file> --to <tailwind|css-tailwind|json-tailwind|css|dtcg|figma> [--tailwind-version v3|v4]',
|
|
115
125
|
async run(args) {
|
|
116
126
|
const { flags, positional } = parseFlags(args)
|
|
117
127
|
const file = positional[0]
|
|
118
128
|
if (!file || !flags.to) {
|
|
119
|
-
console.error('Usage: design-md export <file> --to <tailwind|css|dtcg|figma>')
|
|
129
|
+
console.error('Usage: design-md export <file> --to <tailwind|css-tailwind|json-tailwind|css|dtcg|figma>')
|
|
120
130
|
process.exit(2)
|
|
121
131
|
}
|
|
122
132
|
if (!FORMATS.has(flags.to)) {
|
|
@@ -128,9 +138,17 @@ export const exportTokens = {
|
|
|
128
138
|
process.exit(2)
|
|
129
139
|
}
|
|
130
140
|
const { data } = await readDesignMd(resolve(process.cwd(), file))
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
141
|
+
|
|
142
|
+
let out
|
|
143
|
+
if (flags.to === 'tailwind') {
|
|
144
|
+
out = toTailwind(data, flags.tailwindVersion)
|
|
145
|
+
} else if (flags.to === 'css-tailwind') {
|
|
146
|
+
out = toTailwindV4(data)
|
|
147
|
+
} else if (flags.to === 'json-tailwind') {
|
|
148
|
+
out = toTailwindV3(data)
|
|
149
|
+
} else {
|
|
150
|
+
out = RENDERERS[flags.to](data)
|
|
151
|
+
}
|
|
134
152
|
process.stdout.write(out + '\n')
|
|
135
153
|
},
|
|
136
154
|
}
|
package/src/commands/help.mjs
CHANGED
|
@@ -13,21 +13,28 @@ ${kleur.dim('COMMANDS')}
|
|
|
13
13
|
init Interactive picker (default if no command)
|
|
14
14
|
lint <file> Validate a DESIGN.md for spec compliance
|
|
15
15
|
diff <a> <b> Token-level diff between two DESIGN.md files
|
|
16
|
-
export <file> --to <f> Convert tokens
|
|
17
|
-
|
|
16
|
+
export <file> --to <f> Convert tokens — formats:
|
|
17
|
+
tailwind v4 CSS @theme (default; --tailwind-version v3 for legacy)
|
|
18
|
+
css-tailwind v4 CSS @theme (Google CLI alias)
|
|
19
|
+
json-tailwind v3 JSON theme.extend (Google CLI alias)
|
|
20
|
+
css :root { --color-* / --radius-* }
|
|
21
|
+
dtcg W3C Design Tokens JSON
|
|
22
|
+
figma Figma Variables import format
|
|
18
23
|
extract <url> Extract a draft DESIGN.md from a production URL
|
|
19
24
|
import <url> Alias of extract — name aligned with Google Labs
|
|
20
25
|
theme <slug> --dark Compute a dark-mode counterpart of a light design
|
|
21
26
|
preview <slug> Open the directory detail page in your browser
|
|
27
|
+
submit <file> Open a PR to add your DESIGN.md to the public catalog
|
|
22
28
|
help This screen
|
|
23
29
|
|
|
24
30
|
${kleur.dim('EXAMPLES')}
|
|
25
|
-
$ design-md add
|
|
31
|
+
$ design-md add stripe ${kleur.dim('# writes DESIGN.md to CWD')}
|
|
26
32
|
$ design-md list | grep editorial
|
|
27
33
|
$ design-md export DESIGN.md --to tailwind > theme.css
|
|
28
34
|
$ design-md export DESIGN.md --to tailwind --tailwind-version v3 > tailwind.config.js
|
|
29
35
|
$ design-md import https://stripe.com
|
|
30
36
|
$ design-md diff DESIGN.md old.md
|
|
37
|
+
$ design-md submit ./DESIGN.md ${kleur.dim('# auto-fork + open PR via `gh` CLI')}
|
|
31
38
|
|
|
32
39
|
${kleur.dim('Spec:')} https://www.webdesignhot.com/design.md/spec ${kleur.dim('(webdesignhot/0.1)')}
|
|
33
40
|
${kleur.dim('Catalog:')} https://www.webdesignhot.com/design.md/
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Submit a DESIGN.md to the public catalog at github.com/WebDesignHot/design-md.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Validate file (lint).
|
|
6
|
+
* 2. Resolve slug from frontmatter (or prompt).
|
|
7
|
+
* 3. Verify `gh` CLI is installed and authed.
|
|
8
|
+
* 4. Fork WebDesignHot/design-md to user's account (idempotent).
|
|
9
|
+
* 5. Clone fork to a temp dir.
|
|
10
|
+
* 6. Copy file to design-md/<slug>.md.
|
|
11
|
+
* 7. Branch + commit + push.
|
|
12
|
+
* 8. Open a PR against WebDesignHot/design-md:main with a templated body.
|
|
13
|
+
* 9. Print the PR URL.
|
|
14
|
+
*
|
|
15
|
+
* Aborts gracefully if `gh` is missing or unauthed; falls back to printing
|
|
16
|
+
* the manual-PR instructions (file path + raw upload guide).
|
|
17
|
+
*/
|
|
18
|
+
import { resolve, join, basename } from 'node:path'
|
|
19
|
+
import { readFile, writeFile, mkdir, stat } from 'node:fs/promises'
|
|
20
|
+
import { tmpdir } from 'node:os'
|
|
21
|
+
import { execSync, spawnSync } from 'node:child_process'
|
|
22
|
+
import kleur from 'kleur'
|
|
23
|
+
import * as p from '@clack/prompts'
|
|
24
|
+
import { readDesignMd } from '../lib/parse.mjs'
|
|
25
|
+
|
|
26
|
+
const UPSTREAM = 'WebDesignHot/design-md'
|
|
27
|
+
const UPSTREAM_BRANCH = 'main'
|
|
28
|
+
|
|
29
|
+
function parseFlags(args) {
|
|
30
|
+
const flags = { dryRun: false }
|
|
31
|
+
const positional = []
|
|
32
|
+
for (let i = 0; i < args.length; i++) {
|
|
33
|
+
const a = args[i]
|
|
34
|
+
if (a === '--dry-run') flags.dryRun = true
|
|
35
|
+
else positional.push(a)
|
|
36
|
+
}
|
|
37
|
+
return { flags, positional }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function which(cmd) {
|
|
41
|
+
const r = spawnSync('which', [cmd], { encoding: 'utf8' })
|
|
42
|
+
return r.status === 0 ? r.stdout.trim() : null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function run(cmd, args, opts = {}) {
|
|
46
|
+
const r = spawnSync(cmd, args, { encoding: 'utf8', ...opts })
|
|
47
|
+
if (r.status !== 0) {
|
|
48
|
+
throw new Error(`${cmd} ${args.join(' ')} failed:\n${r.stderr || r.stdout || '(no output)'}`)
|
|
49
|
+
}
|
|
50
|
+
return r.stdout?.trim() ?? ''
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function deriveSlug(name) {
|
|
54
|
+
return String(name)
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
57
|
+
.replace(/^-+|-+$/g, '')
|
|
58
|
+
.replace(/--+/g, '-')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function exists(p) {
|
|
62
|
+
try {
|
|
63
|
+
await stat(p)
|
|
64
|
+
return true
|
|
65
|
+
} catch {
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const submit = {
|
|
71
|
+
usage: 'submit <file> [--dry-run]',
|
|
72
|
+
async run(args) {
|
|
73
|
+
const { flags, positional } = parseFlags(args)
|
|
74
|
+
const file = positional[0]
|
|
75
|
+
if (!file) {
|
|
76
|
+
console.error('Usage: design-md submit <file>')
|
|
77
|
+
console.error(' Submits your DESIGN.md as a PR to github.com/WebDesignHot/design-md.')
|
|
78
|
+
process.exit(2)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const filePath = resolve(process.cwd(), file)
|
|
82
|
+
if (!(await exists(filePath))) {
|
|
83
|
+
console.error(kleur.red(`✗ File not found: ${filePath}`))
|
|
84
|
+
process.exit(1)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 1. Lint inline (cheap re-check)
|
|
88
|
+
const { data } = await readDesignMd(filePath)
|
|
89
|
+
const errors = []
|
|
90
|
+
if (!data.name) errors.push('frontmatter.name is required')
|
|
91
|
+
if (!data.spec) errors.push('frontmatter.spec is required (e.g. webdesignhot/0.1)')
|
|
92
|
+
if (!data.source_url) errors.push('frontmatter.source_url is required')
|
|
93
|
+
if (!data.colors?.bg || !data.colors?.text || !data.colors?.brand) {
|
|
94
|
+
errors.push('frontmatter.colors must include bg, text, and brand')
|
|
95
|
+
}
|
|
96
|
+
if (errors.length) {
|
|
97
|
+
console.error(kleur.red('✗ File has issues that must be fixed before submitting:'))
|
|
98
|
+
for (const e of errors) console.error(` · ${e}`)
|
|
99
|
+
console.error(kleur.dim(' Run `design-md lint <file>` for full diagnostics.'))
|
|
100
|
+
process.exit(1)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 2. Resolve slug
|
|
104
|
+
let slug = data.slug
|
|
105
|
+
if (!slug) {
|
|
106
|
+
const fromFile = basename(filePath, '.md')
|
|
107
|
+
slug = fromFile === 'DESIGN' ? deriveSlug(data.name) : fromFile
|
|
108
|
+
}
|
|
109
|
+
if (!slug || !/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
|
|
110
|
+
console.error(kleur.red(`✗ Invalid slug "${slug}" — must be kebab-case lowercase.`))
|
|
111
|
+
process.exit(1)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log(kleur.dim(`→ Submitting ${kleur.bold(data.name)} as slug "${kleur.bold(slug)}"`))
|
|
115
|
+
|
|
116
|
+
// 3. Check `gh` CLI
|
|
117
|
+
const ghPath = which('gh')
|
|
118
|
+
if (!ghPath) {
|
|
119
|
+
console.error()
|
|
120
|
+
console.error(kleur.yellow('⚠ The GitHub CLI (`gh`) is not installed.'))
|
|
121
|
+
console.error()
|
|
122
|
+
console.error(' This command auto-forks + opens a PR for you. Install with:')
|
|
123
|
+
console.error(kleur.cyan(' brew install gh # macOS'))
|
|
124
|
+
console.error(kleur.cyan(' winget install GitHub.cli # Windows'))
|
|
125
|
+
console.error(kleur.cyan(' https://cli.github.com — other platforms'))
|
|
126
|
+
console.error()
|
|
127
|
+
console.error(' Or submit manually:')
|
|
128
|
+
console.error(` 1. Fork ${kleur.cyan(`https://github.com/${UPSTREAM}`)}`)
|
|
129
|
+
console.error(` 2. Add your file at ${kleur.cyan(`design-md/${slug}.md`)}`)
|
|
130
|
+
console.error(' 3. Open a PR against `main`.')
|
|
131
|
+
process.exit(1)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Verify gh is authed
|
|
135
|
+
try {
|
|
136
|
+
run('gh', ['auth', 'status'], { stdio: ['ignore', 'pipe', 'pipe'] })
|
|
137
|
+
} catch {
|
|
138
|
+
console.error(kleur.yellow('⚠ `gh` is installed but not authenticated.'))
|
|
139
|
+
console.error(` Run: ${kleur.cyan('gh auth login')}`)
|
|
140
|
+
process.exit(1)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (flags.dryRun) {
|
|
144
|
+
console.log(kleur.green(`✓ Dry-run passed. Would submit ${data.name} as ${slug}.`))
|
|
145
|
+
console.log(kleur.dim(` File: ${filePath}`))
|
|
146
|
+
console.log(kleur.dim(` Target: ${UPSTREAM}/design-md/${slug}.md`))
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 4. Fork (idempotent — gh handles already-forked case)
|
|
151
|
+
const ghUser = run('gh', ['api', 'user', '-q', '.login'])
|
|
152
|
+
console.log(kleur.dim(`→ Forking ${UPSTREAM} to ${ghUser}/design-md (no-op if already forked)`))
|
|
153
|
+
try {
|
|
154
|
+
run('gh', ['repo', 'fork', UPSTREAM, '--clone=false'], { stdio: ['ignore', 'pipe', 'pipe'] })
|
|
155
|
+
} catch {
|
|
156
|
+
// gh fork prints "already exists" to stderr but exits 0 sometimes; treat as ok
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 5. Clone fork to a temp dir
|
|
160
|
+
const work = join(tmpdir(), `design-md-submit-${slug}-${Date.now()}`)
|
|
161
|
+
await mkdir(work, { recursive: true })
|
|
162
|
+
console.log(kleur.dim(`→ Cloning fork to ${work}`))
|
|
163
|
+
run('gh', ['repo', 'clone', `${ghUser}/design-md`, work, '--', '--depth=1'])
|
|
164
|
+
|
|
165
|
+
// Sync fork with upstream's main (in case fork is stale)
|
|
166
|
+
try {
|
|
167
|
+
run('git', ['-C', work, 'remote', 'add', 'upstream', `https://github.com/${UPSTREAM}.git`])
|
|
168
|
+
run('git', ['-C', work, 'fetch', '--depth=1', 'upstream', UPSTREAM_BRANCH])
|
|
169
|
+
run('git', ['-C', work, 'reset', '--hard', `upstream/${UPSTREAM_BRANCH}`])
|
|
170
|
+
} catch (err) {
|
|
171
|
+
console.error(kleur.yellow(`⚠ Could not sync fork with upstream: ${err.message}`))
|
|
172
|
+
console.error(kleur.dim(' Continuing with current fork state.'))
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 6. Copy file
|
|
176
|
+
const targetPath = join(work, 'design-md', `${slug}.md`)
|
|
177
|
+
if (await exists(targetPath)) {
|
|
178
|
+
const overwrite = await p.confirm({
|
|
179
|
+
message: `design-md/${slug}.md already exists in the catalog. Open a PR to update it?`,
|
|
180
|
+
initialValue: false,
|
|
181
|
+
})
|
|
182
|
+
if (p.isCancel(overwrite) || !overwrite) {
|
|
183
|
+
console.log(kleur.yellow('Aborted.'))
|
|
184
|
+
process.exit(0)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const fileContent = await readFile(filePath, 'utf8')
|
|
188
|
+
await writeFile(targetPath, fileContent)
|
|
189
|
+
|
|
190
|
+
// 7. Branch + commit + push
|
|
191
|
+
const branch = `submit/${slug}-${Date.now().toString(36).slice(-4)}`
|
|
192
|
+
console.log(kleur.dim(`→ Branch: ${branch}`))
|
|
193
|
+
run('git', ['-C', work, 'checkout', '-b', branch])
|
|
194
|
+
run('git', ['-C', work, 'add', `design-md/${slug}.md`])
|
|
195
|
+
const commitMsg = `submit: ${data.name} (${slug})\n\nFrom: ${data.source_url ?? 'unspecified'}`
|
|
196
|
+
run('git', ['-C', work, '-c', 'commit.gpgsign=false', 'commit', '-m', commitMsg])
|
|
197
|
+
console.log(kleur.dim(`→ Pushing to ${ghUser}/design-md:${branch}`))
|
|
198
|
+
run('git', ['-C', work, 'push', '-u', 'origin', branch])
|
|
199
|
+
|
|
200
|
+
// 8. Open PR
|
|
201
|
+
const prTitle = `submit: ${data.name}`
|
|
202
|
+
const prBody = `## New entry: ${data.name}
|
|
203
|
+
|
|
204
|
+
**Slug**: \`${slug}\`
|
|
205
|
+
**Source URL**: ${data.source_url ?? '(not provided)'}
|
|
206
|
+
**Categories**: ${(data.categories ?? []).join(', ') || '(none)'}
|
|
207
|
+
**Tags**: ${(data.tags ?? []).join(', ') || '(none)'}
|
|
208
|
+
|
|
209
|
+
${data.tagline ? `> ${data.tagline}\n` : ''}
|
|
210
|
+
${data.description ?? ''}
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
Submitted via \`design-md submit\`.
|
|
215
|
+
|
|
216
|
+
### Reviewer checklist
|
|
217
|
+
- [ ] \`design-md lint\` passes
|
|
218
|
+
- [ ] All 15 sections present in body
|
|
219
|
+
- [ ] Tokens reflect actual production site
|
|
220
|
+
- [ ] Lineage block names real influences with URLs
|
|
221
|
+
- [ ] Categories + tags use existing taxonomy
|
|
222
|
+
`
|
|
223
|
+
console.log(kleur.dim('→ Opening PR'))
|
|
224
|
+
const prUrl = run('gh', [
|
|
225
|
+
'pr', 'create',
|
|
226
|
+
'--repo', UPSTREAM,
|
|
227
|
+
'--base', UPSTREAM_BRANCH,
|
|
228
|
+
'--head', `${ghUser}:${branch}`,
|
|
229
|
+
'--title', prTitle,
|
|
230
|
+
'--body', prBody,
|
|
231
|
+
])
|
|
232
|
+
|
|
233
|
+
// 9. Done
|
|
234
|
+
console.log()
|
|
235
|
+
console.log(kleur.green('✓ Submitted.'))
|
|
236
|
+
console.log(` ${kleur.cyan(prUrl)}`)
|
|
237
|
+
console.log()
|
|
238
|
+
console.log(kleur.dim('Your design will appear on webdesignhot.com once we review and merge.'))
|
|
239
|
+
},
|
|
240
|
+
}
|
package/src/index.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import { exportTokens } from './commands/export.mjs'
|
|
|
14
14
|
import { extract } from './commands/extract.mjs'
|
|
15
15
|
import { theme } from './commands/theme.mjs'
|
|
16
16
|
import { preview } from './commands/preview.mjs'
|
|
17
|
+
import { submit } from './commands/submit.mjs'
|
|
17
18
|
import { help } from './commands/help.mjs'
|
|
18
19
|
|
|
19
20
|
const COMMANDS = {
|
|
@@ -28,6 +29,7 @@ const COMMANDS = {
|
|
|
28
29
|
import: extract, // alias — name aligned with Google Labs PR #40
|
|
29
30
|
theme,
|
|
30
31
|
preview,
|
|
32
|
+
submit,
|
|
31
33
|
help,
|
|
32
34
|
'--help': help,
|
|
33
35
|
'-h': help,
|