@rokkit/cli 1.0.0-next.147 → 1.0.0-next.148

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.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Load rokkit.config.js using injected adapter or real filesystem.
3
+ * Returns null (not an error) if config is absent or fails to load.
4
+ * @param {{ readConfig?: () => Record<string, unknown>, cwd?: string }} adapters
5
+ * @returns {Promise<Record<string, unknown> | null>}
6
+ */
7
+ export function loadConfig(adapters: {
8
+ readConfig?: () => Record<string, unknown>;
9
+ cwd?: string;
10
+ }): Promise<Record<string, unknown> | null>;
@@ -50,7 +50,7 @@ export function getFolderNames(dir: string): string[];
50
50
  */
51
51
  export function bundleFolders(folders: string[], config: import("./types").IconBuilderOptions, opts: import("./types").FolderOptions): void;
52
52
  /**
53
- * Converts a folder of icons into an icon bundle
53
+ * Converts a folder of icons into individual SVG files
54
54
  * @param {string[]} folders
55
55
  * @param {import('./types').IconBuilderOptions} config
56
56
  * @param {import('./types').FolderOptions} opts
@@ -3,7 +3,7 @@
3
3
  * @param {{ palette: string, customColors?: Record<string, string>, icons: string, iconPath?: string, themes: string[], switcher: string }} opts
4
4
  * @returns {Record<string, unknown>}
5
5
  */
6
- export function generateConfig({ palette, customColors, icons, iconPath, themes, switcher }: {
6
+ export function generateConfig({ palette, customColors, icons, iconPath, themes, defaultTheme, switcher }: {
7
7
  palette: string;
8
8
  customColors?: Record<string, string>;
9
9
  icons: string;
@@ -29,7 +29,7 @@ export function generateAppCssImports(themes: string[]): string[];
29
29
  * @param {string} [storageKey='rokkit-theme']
30
30
  * @returns {string | null}
31
31
  */
32
- export function generateInitScript(switcher: string, storageKey?: string): string | null;
32
+ export function generateInitScript(switcher: string, storageKey?: string, defaultStyle?: string): string | null;
33
33
  /**
34
34
  * Interactive init command — prompts the user, writes config files.
35
35
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokkit/cli",
3
- "version": "1.0.0-next.147",
3
+ "version": "1.0.0-next.148",
4
4
  "description": "Command line utilities for icon bundling and conversion.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/config.js ADDED
@@ -0,0 +1,23 @@
1
+ import { existsSync } from 'fs'
2
+ import { resolve } from 'path'
3
+
4
+ /**
5
+ * Load rokkit.config.js using injected adapter or real filesystem.
6
+ * Returns null (not an error) if config is absent or fails to load.
7
+ * @param {{ readConfig?: () => Record<string, unknown>, cwd?: string }} adapters
8
+ * @returns {Promise<Record<string, unknown> | null>}
9
+ */
10
+ export async function loadConfig(adapters) {
11
+ if (adapters.readConfig) return adapters.readConfig()
12
+
13
+ const cwd = adapters.cwd ?? process.cwd()
14
+ const configPath = resolve(cwd, 'rokkit.config.js')
15
+ if (!existsSync(configPath)) return null
16
+
17
+ try {
18
+ const { default: config } = await import(`file://${configPath}?t=${Date.now()}`)
19
+ return config
20
+ } catch {
21
+ return null
22
+ }
23
+ }
package/src/convert.js CHANGED
@@ -172,40 +172,39 @@ export function getFolderNames(dir) {
172
172
  }
173
173
  }
174
174
 
175
+ function folderOpts(folder, config, opts, extra) {
176
+ const folderConfig = config.bundles?.[folder] || {}
177
+ return { target: opts.output, color: folderConfig.color || false, prefix: folder, ...extra }
178
+ }
179
+
175
180
  /**
176
- * Converts a folder of icons into an icon bundle
177
181
  * @param {string[]} folders
178
182
  * @param {import('./types').IconBuilderOptions} config
179
- * @param {import('./types').FolderOptions} opts
183
+ * @param {import('./types').FolderOptions} opts
184
+ * @param {{ fn: Function, extra?: Record<string, unknown> }} processor
180
185
  */
181
- export function bundleFolders(folders, config, opts) {
186
+ function processFolders(folders, config, opts, { fn, extra = {} }) {
182
187
  for (const folder of folders) {
183
- const folderPath = path.join(opts.input, folder)
184
- const folderConfig = config.bundles?.[folder] || {}
185
-
186
- bundle(folderPath, {
187
- target: opts.output,
188
- color: folderConfig.color || false,
189
- prefix: folder
190
- })
188
+ fn(path.join(opts.input, folder), folderOpts(folder, config, opts, extra))
191
189
  }
192
190
  }
191
+
193
192
  /**
194
193
  * Converts a folder of icons into an icon bundle
194
+ * @param {string[]} folders
195
+ * @param {import('./types').IconBuilderOptions} config
196
+ * @param {import('./types').FolderOptions} opts
197
+ */
198
+ export function bundleFolders(folders, config, opts) {
199
+ processFolders(folders, config, opts, { fn: bundle })
200
+ }
201
+
202
+ /**
203
+ * Converts a folder of icons into individual SVG files
195
204
  * @param {string[]} folders
196
205
  * @param {import('./types').IconBuilderOptions} config
197
206
  * @param {import('./types').FolderOptions} opts
198
207
  */
199
208
  export function convertFolders(folders, config, opts) {
200
- for (const folder of folders) {
201
- const folderPath = path.join(opts.input, folder)
202
- const folderConfig = config.bundles[folder] || {}
203
-
204
- convert(folderPath, {
205
- target: opts.output,
206
- color: folderConfig.color || false,
207
- prefix: folder,
208
- package: config.package || {}
209
- })
210
- }
209
+ processFolders(folders, config, opts, { fn: convert, extra: { package: config.package || {} } })
211
210
  }
package/src/doctor.js CHANGED
@@ -3,6 +3,11 @@ import { existsSync, readFileSync, writeFileSync } from 'fs'
3
3
  import { resolve } from 'path'
4
4
  import { generateUnoConfig, generateAppCssImports, generateInitScript } from './init.js'
5
5
 
6
+ const KNOWN_THEMES = [
7
+ 'rokkit', 'minimal', 'material', 'glass', 'grada-ui',
8
+ 'shadcn', 'daisy-ui', 'bits-ui', 'carbon', 'ant-design'
9
+ ]
10
+
6
11
  /**
7
12
  * Check config file existence
8
13
  * @param {Object} fs
@@ -28,10 +33,11 @@ function checkConfig(fs) {
28
33
  function checkUnoPreset(fs) {
29
34
  const unoPath = fs.resolve('uno.config.js')
30
35
  const unoExists = fs.exists(unoPath)
31
- const unoUsesPreset = unoExists && fs.read(unoPath).includes('presetRokkit')
36
+ const unoContent = unoExists ? fs.read(unoPath) : ''
37
+ const unoUsesPreset = unoContent.includes('presetRokkit') && unoContent.includes('rokkit.config')
32
38
  return {
33
39
  id: 'uno-uses-preset',
34
- label: 'uno.config.js uses presetRokkit()',
40
+ label: 'uno.config.js uses presetRokkit(config)',
35
41
  status: unoUsesPreset ? 'pass' : 'fail',
36
42
  fixable: false,
37
43
  fix: unoExists
@@ -48,17 +54,36 @@ function checkUnoPreset(fs) {
48
54
  function checkCssImports(fs) {
49
55
  const cssPath = fs.resolve('src/app.css')
50
56
  const cssExists = fs.exists(cssPath)
51
- const cssHasBase = cssExists && fs.read(cssPath).includes('@rokkit/themes/dist/base')
57
+ const cssHasBase = cssExists && fs.read(cssPath).includes('@rokkit/themes/base.css')
52
58
  return {
53
59
  id: 'css-imports',
54
- label: 'app.css has theme imports',
60
+ label: 'app.css imports @rokkit/themes/base.css',
55
61
  status: cssHasBase ? 'pass' : 'fail',
56
62
  fixable: true,
57
- fix: 'Append theme imports to src/app.css',
63
+ fix: 'Append base theme import to src/app.css',
58
64
  autoFix: 'patch-css'
59
65
  }
60
66
  }
61
67
 
68
+ /**
69
+ * Check app.css has at least one theme style
70
+ * @param {Object} fs
71
+ * @returns {Object}
72
+ */
73
+ function checkCssTheme(fs) {
74
+ const cssPath = fs.resolve('src/app.css')
75
+ const cssExists = fs.exists(cssPath)
76
+ const css = cssExists ? fs.read(cssPath) : ''
77
+ const hasTheme = KNOWN_THEMES.some((t) => css.includes(`@rokkit/themes/${t}.css`))
78
+ return {
79
+ id: 'css-theme',
80
+ label: 'app.css has a theme style',
81
+ status: hasTheme ? 'pass' : 'warn',
82
+ fixable: false,
83
+ fix: `Add a theme import to src/app.css, e.g. @import '@rokkit/themes/rokkit.css'\n Available: ${KNOWN_THEMES.join(', ')}, or use a custom theme`
84
+ }
85
+ }
86
+
62
87
  /**
63
88
  * Check app.html has init script
64
89
  * @param {Object} fs
@@ -86,7 +111,7 @@ function checkHtmlScript(fs) {
86
111
  * @returns {Array<{ id: string, label: string, status: 'pass'|'fail', fixable: boolean, fix: string, autoFix?: string }>}
87
112
  */
88
113
  export function runChecks(fs) {
89
- return [checkConfig(fs), checkUnoPreset(fs), checkCssImports(fs), checkHtmlScript(fs)]
114
+ return [checkConfig(fs), checkUnoPreset(fs), checkCssImports(fs), checkCssTheme(fs), checkHtmlScript(fs)]
90
115
  }
91
116
 
92
117
  /**
@@ -119,7 +144,7 @@ function applyGenerateConfig(cwd, label) {
119
144
  */
120
145
  function applyPatchCss(cwd, label) {
121
146
  const cssPath = resolve(cwd, 'src/app.css')
122
- const imports = generateAppCssImports(['rokkit'])
147
+ const imports = generateAppCssImports([])
123
148
  if (existsSync(cssPath)) {
124
149
  const existing = readFileSync(cssPath, 'utf-8')
125
150
  const missing = imports.filter((line) => !existing.includes(line))
@@ -189,7 +214,7 @@ function autoFix(checks, cwd) {
189
214
  */
190
215
  function printFixHints(checks) {
191
216
  for (const check of checks) {
192
- if (check.status === 'fail') {
217
+ if (check.status === 'fail' || check.status === 'warn') {
193
218
  console.info(` ${check.fixable ? '(auto-fixable) ' : ''}${check.fix}`)
194
219
  }
195
220
  }
@@ -215,7 +240,7 @@ function printManualItems(checks) {
215
240
  function printChecks(checks) {
216
241
  let failures = 0
217
242
  for (const check of checks) {
218
- const icon = check.status === 'pass' ? 'PASS' : 'FAIL'
243
+ const icon = check.status === 'pass' ? 'PASS' : check.status === 'warn' ? 'WARN' : 'FAIL'
219
244
  console.info(` ${icon} ${check.label}`)
220
245
  if (check.status === 'fail') failures++
221
246
  }
package/src/init.js CHANGED
@@ -36,13 +36,19 @@ const DEFAULT_COLORS = {
36
36
  * @param {{ palette: string, customColors?: Record<string, string>, icons: string, iconPath?: string, themes: string[], switcher: string }} opts
37
37
  * @returns {Record<string, unknown>}
38
38
  */
39
- export function generateConfig({ palette, customColors, icons, iconPath, themes, switcher }) {
39
+ export function generateConfig({ palette, customColors, icons, iconPath, themes, defaultTheme, switcher }) {
40
40
  const colors =
41
41
  palette === 'custom'
42
42
  ? { ...DEFAULT_COLORS, ...customColors }
43
43
  : { ...DEFAULT_COLORS, ...(SKIN_PRESETS[palette] || {}) }
44
44
 
45
- const config = { colors, themes, switcher, storageKey: 'rokkit-theme' }
45
+ const config = {
46
+ colors,
47
+ themes,
48
+ defaultTheme: defaultTheme || themes[0],
49
+ switcher,
50
+ storageKey: 'rokkit-theme'
51
+ }
46
52
 
47
53
  if (icons === 'custom' && iconPath) {
48
54
  config.icons = { custom: iconPath }
@@ -58,9 +64,10 @@ export function generateConfig({ palette, customColors, icons, iconPath, themes,
58
64
  export function generateUnoConfig() {
59
65
  return `import { defineConfig } from 'unocss'
60
66
  import { presetRokkit } from '@rokkit/unocss'
67
+ import config from './rokkit.config.js'
61
68
 
62
69
  export default defineConfig({
63
- presets: [presetRokkit()]
70
+ presets: [presetRokkit(config)]
64
71
  })
65
72
  `
66
73
  }
@@ -71,9 +78,9 @@ export default defineConfig({
71
78
  * @returns {string[]}
72
79
  */
73
80
  export function generateAppCssImports(themes) {
74
- const lines = ["@import '@unocss/reset/tailwind.css';", "@import '@rokkit/themes/dist/base';"]
81
+ const lines = ["@import '@unocss/reset/tailwind.css';", "@import '@rokkit/themes/base.css';"]
75
82
  for (const theme of themes) {
76
- lines.push(`@import '@rokkit/themes/dist/${theme}';`)
83
+ lines.push(`@import '@rokkit/themes/${theme}.css';`)
77
84
  }
78
85
  return lines
79
86
  }
@@ -85,10 +92,10 @@ export function generateAppCssImports(themes) {
85
92
  * @param {string} [storageKey='rokkit-theme']
86
93
  * @returns {string | null}
87
94
  */
88
- export function generateInitScript(switcher, storageKey = 'rokkit-theme') {
95
+ export function generateInitScript(switcher, storageKey = 'rokkit-theme', defaultStyle = 'rokkit') {
89
96
  if (switcher === 'system') return null
90
97
 
91
- const setStyle = switcher === 'full' ? `b.dataset.style = t.style || 'rokkit'\n ` : ''
98
+ const setStyle = switcher === 'full' ? `b.dataset.style = t.style || '${defaultStyle}'\n ` : ''
92
99
 
93
100
  return ` <script>
94
101
  (function () {
@@ -159,10 +166,23 @@ const PROMPTS_CONFIG = [
159
166
  choices: [
160
167
  { title: 'Rokkit', value: 'rokkit', selected: true },
161
168
  { title: 'Minimal', value: 'minimal' },
162
- { title: 'Material', value: 'material' }
169
+ { title: 'Material', value: 'material' },
170
+ { title: 'Glass', value: 'glass' },
171
+ { title: 'Grada UI', value: 'grada-ui' },
172
+ { value: 'shadcn', title: 'shadcn', hint: 'flat borders + ring focus' },
173
+ { value: 'daisy-ui', title: 'daisy-ui', hint: 'rounded-full + bold fills' },
174
+ { value: 'bits-ui', title: 'bits-ui', hint: 'rounded-lg + polished' },
175
+ { value: 'carbon', title: 'carbon', hint: 'square edges + enterprise' },
176
+ { value: 'ant-design', title: 'ant-design', hint: 'thin borders + dense' }
163
177
  ],
164
178
  min: 1
165
179
  },
180
+ {
181
+ type: (prev) => (prev?.length > 1 ? 'select' : null),
182
+ name: 'defaultTheme',
183
+ message: 'Default theme style',
184
+ choices: (prev) => prev.map((t) => ({ title: t.charAt(0).toUpperCase() + t.slice(1), value: t }))
185
+ },
166
186
  {
167
187
  type: 'select',
168
188
  name: 'switcher',
@@ -269,7 +289,7 @@ export async function init() {
269
289
  writeUnoConfig(cwd)
270
290
  writeAppCss(cwd, generateAppCssImports(config.themes))
271
291
 
272
- const initScript = generateInitScript(config.switcher, config.storageKey)
292
+ const initScript = generateInitScript(config.switcher, config.storageKey, config.defaultTheme)
273
293
  if (initScript) writeAppHtml(cwd, initScript, config.storageKey)
274
294
 
275
295
  console.info('\nDone! Run `rokkit doctor` to verify your setup.')
package/src/skin.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable no-console */
2
- import { writeFileSync, existsSync } from 'fs'
2
+ import { writeFileSync } from 'fs'
3
3
  import { resolve } from 'path'
4
+ import { loadConfig } from './config.js'
4
5
 
5
6
  const SKIN_TOKEN_DEFAULTS = {
6
7
  primary: 'orange',
@@ -54,7 +55,10 @@ export function serializeConfig(config) {
54
55
  */
55
56
  export async function runSkinList(adapters = {}) {
56
57
  const config = await loadConfig(adapters)
57
- if (!config) return
58
+ if (!config) {
59
+ console.error('rokkit.config.js not found. Run `rokkit init` first.')
60
+ return
61
+ }
58
62
  const skins = config.skins ?? {}
59
63
  const names = Object.keys(skins)
60
64
  if (names.length === 0) {
@@ -87,7 +91,10 @@ export async function runSkinCreate(name, adapters = {}) {
87
91
  }
88
92
 
89
93
  const config = await loadConfig(adapters)
90
- if (!config) return
94
+ if (!config) {
95
+ console.error('rokkit.config.js not found. Run `rokkit init` first.')
96
+ return
97
+ }
91
98
 
92
99
  if (config.skins?.[name]) {
93
100
  console.warn(`Skin "${name}" already exists in rokkit.config.js.`)
@@ -104,25 +111,6 @@ export async function runSkinCreate(name, adapters = {}) {
104
111
  )
105
112
  }
106
113
 
107
- /**
108
- * Load rokkit.config.js using injected adapter or real filesystem.
109
- * @param {{ readConfig?: () => Record<string, unknown>, cwd?: string }} adapters
110
- * @returns {Promise<Record<string, unknown> | null>}
111
- */
112
- async function loadConfig(adapters) {
113
- if (adapters.readConfig) return adapters.readConfig()
114
-
115
- const cwd = adapters.cwd ?? process.cwd()
116
- const configPath = resolve(cwd, 'rokkit.config.js')
117
- if (!existsSync(configPath)) {
118
- console.error('rokkit.config.js not found. Run `rokkit init` first.')
119
- return null
120
- }
121
-
122
- const { default: config } = await import(`file://${configPath}?t=${Date.now()}`)
123
- return config
124
- }
125
-
126
114
  /**
127
115
  * Write updated config using injected adapter or real filesystem.
128
116
  * @param {Record<string, unknown>} config
package/src/theme.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable no-console */
2
2
  import { writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs'
3
3
  import { resolve, join } from 'path'
4
+ import { loadConfig } from './config.js'
4
5
 
5
6
  /**
6
7
  * All component names that ship with Rokkit — one CSS block will be generated for each.
@@ -128,26 +129,6 @@ export async function runThemeCreate(name, adapters = {}) {
128
129
  )
129
130
  }
130
131
 
131
- /**
132
- * Load rokkit.config.js using injected adapter or real filesystem.
133
- * Returns null (not an error) if config is absent — theme list can still show custom files.
134
- * @param {{ readConfig?: () => Record<string, unknown>, cwd?: string }} adapters
135
- */
136
- async function loadConfig(adapters) {
137
- if (adapters.readConfig) return adapters.readConfig()
138
-
139
- const cwd = adapters.cwd ?? process.cwd()
140
- const configPath = resolve(cwd, 'rokkit.config.js')
141
- if (!existsSync(configPath)) return null
142
-
143
- try {
144
- const { default: config } = await import(`file://${configPath}?t=${Date.now()}`)
145
- return config
146
- } catch {
147
- return null
148
- }
149
- }
150
-
151
132
  /**
152
133
  * CLI entry point for `rokkit theme` subcommands.
153
134
  * @param {string} sub — 'list' | 'create'