@rokkit/cli 1.0.0-next.147 → 1.0.0-next.150
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/dist/src/config.d.ts +10 -0
- package/dist/src/convert.d.ts +1 -1
- package/dist/src/init.d.ts +16 -14
- package/dist/src/skin.d.ts +2 -25
- package/dist/src/theme.d.ts +2 -29
- package/dist/src/upgrade.d.ts +1 -19
- package/package.json +1 -1
- package/src/config.js +23 -0
- package/src/convert.js +21 -22
- package/src/doctor.js +34 -9
- package/src/init.js +33 -13
- package/src/skin.js +28 -35
- package/src/theme.js +32 -41
- package/src/upgrade.js +68 -45
|
@@ -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>;
|
package/dist/src/convert.d.ts
CHANGED
|
@@ -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
|
|
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
|
package/dist/src/init.d.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
themes:
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
export function generateConfig({ palette, customColors, icons, iconPath, themes, defaultTheme, switcher }: {
|
|
2
|
+
palette: any;
|
|
3
|
+
customColors: any;
|
|
4
|
+
icons: any;
|
|
5
|
+
iconPath: any;
|
|
6
|
+
themes: any;
|
|
7
|
+
defaultTheme: any;
|
|
8
|
+
switcher: any;
|
|
9
|
+
}): {
|
|
10
|
+
colors: Record<string, unknown>;
|
|
11
|
+
themes: any;
|
|
12
|
+
defaultTheme: any;
|
|
13
|
+
switcher: any;
|
|
14
|
+
storageKey: string;
|
|
15
|
+
};
|
|
14
16
|
/**
|
|
15
17
|
* Generate the content for uno.config.js.
|
|
16
18
|
* @returns {string}
|
|
@@ -29,7 +31,7 @@ export function generateAppCssImports(themes: string[]): string[];
|
|
|
29
31
|
* @param {string} [storageKey='rokkit-theme']
|
|
30
32
|
* @returns {string | null}
|
|
31
33
|
*/
|
|
32
|
-
export function generateInitScript(switcher: string, storageKey?: string): string | null;
|
|
34
|
+
export function generateInitScript(switcher: string, storageKey?: string, defaultStyle?: string): string | null;
|
|
33
35
|
/**
|
|
34
36
|
* Interactive init command — prompts the user, writes config files.
|
|
35
37
|
*/
|
package/dist/src/skin.d.ts
CHANGED
|
@@ -16,31 +16,8 @@ export function addSkinToConfig(config: Record<string, unknown>, name: string):
|
|
|
16
16
|
* @returns {string}
|
|
17
17
|
*/
|
|
18
18
|
export function serializeConfig(config: Record<string, unknown>): string;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* @param {{
|
|
22
|
-
* readConfig?: () => Record<string, unknown>,
|
|
23
|
-
* cwd?: string
|
|
24
|
-
* }} adapters
|
|
25
|
-
*/
|
|
26
|
-
export function runSkinList(adapters?: {
|
|
27
|
-
readConfig?: () => Record<string, unknown>;
|
|
28
|
-
cwd?: string;
|
|
29
|
-
}): Promise<void>;
|
|
30
|
-
/**
|
|
31
|
-
* Create a new skin entry in rokkit.config.js.
|
|
32
|
-
* @param {string} name
|
|
33
|
-
* @param {{
|
|
34
|
-
* readConfig?: () => Record<string, unknown>,
|
|
35
|
-
* writeConfig?: (config: Record<string, unknown>) => void,
|
|
36
|
-
* cwd?: string
|
|
37
|
-
* }} adapters
|
|
38
|
-
*/
|
|
39
|
-
export function runSkinCreate(name: string, adapters?: {
|
|
40
|
-
readConfig?: () => Record<string, unknown>;
|
|
41
|
-
writeConfig?: (config: Record<string, unknown>) => void;
|
|
42
|
-
cwd?: string;
|
|
43
|
-
}): Promise<void>;
|
|
19
|
+
export function runSkinList(adapters?: {}): Promise<void>;
|
|
20
|
+
export function runSkinCreate(name: any, adapters?: {}): Promise<void>;
|
|
44
21
|
/**
|
|
45
22
|
* CLI entry point for `rokkit skin` subcommands.
|
|
46
23
|
* @param {string} sub — 'list' | 'create'
|
package/dist/src/theme.d.ts
CHANGED
|
@@ -5,35 +5,8 @@
|
|
|
5
5
|
* @returns {string}
|
|
6
6
|
*/
|
|
7
7
|
export function generateThemeStub(name: string): string;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* @param {{
|
|
11
|
-
* readConfig?: () => Record<string, unknown>,
|
|
12
|
-
* listFiles?: (dir: string) => string[],
|
|
13
|
-
* cwd?: string
|
|
14
|
-
* }} adapters
|
|
15
|
-
*/
|
|
16
|
-
export function runThemeList(adapters?: {
|
|
17
|
-
readConfig?: () => Record<string, unknown>;
|
|
18
|
-
listFiles?: (dir: string) => string[];
|
|
19
|
-
cwd?: string;
|
|
20
|
-
}): Promise<void>;
|
|
21
|
-
/**
|
|
22
|
-
* Create a new custom theme CSS stub at src/themes/<name>.css.
|
|
23
|
-
* @param {string} name
|
|
24
|
-
* @param {{
|
|
25
|
-
* writeFile?: (path: string, content: string) => void,
|
|
26
|
-
* exists?: (path: string) => boolean,
|
|
27
|
-
* mkdir?: (dir: string) => void,
|
|
28
|
-
* cwd?: string
|
|
29
|
-
* }} adapters
|
|
30
|
-
*/
|
|
31
|
-
export function runThemeCreate(name: string, adapters?: {
|
|
32
|
-
writeFile?: (path: string, content: string) => void;
|
|
33
|
-
exists?: (path: string) => boolean;
|
|
34
|
-
mkdir?: (dir: string) => void;
|
|
35
|
-
cwd?: string;
|
|
36
|
-
}): Promise<void>;
|
|
8
|
+
export function runThemeList(adapters?: {}): Promise<void>;
|
|
9
|
+
export function runThemeCreate(name: any, adapters?: {}): void;
|
|
37
10
|
/**
|
|
38
11
|
* CLI entry point for `rokkit theme` subcommands.
|
|
39
12
|
* @param {string} sub — 'list' | 'create'
|
package/dist/src/upgrade.d.ts
CHANGED
|
@@ -23,25 +23,7 @@ export function buildInstallCommand(pm: "bun" | "pnpm" | "yarn" | "npm", package
|
|
|
23
23
|
bin: string;
|
|
24
24
|
args: string[];
|
|
25
25
|
};
|
|
26
|
-
|
|
27
|
-
* Run upgrade check and optionally apply it.
|
|
28
|
-
*
|
|
29
|
-
* @param {{ apply?: boolean }} opts
|
|
30
|
-
* @param {{
|
|
31
|
-
* readFile?: (p: string) => string,
|
|
32
|
-
* exists?: (p: string) => boolean,
|
|
33
|
-
* fetchVersion?: (name: string) => string | null,
|
|
34
|
-
* runInstall?: (bin: string, args: string[]) => void
|
|
35
|
-
* }} adapters — injectable for testing
|
|
36
|
-
*/
|
|
37
|
-
export function runUpgrade(opts?: {
|
|
38
|
-
apply?: boolean;
|
|
39
|
-
}, adapters?: {
|
|
40
|
-
readFile?: (p: string) => string;
|
|
41
|
-
exists?: (p: string) => boolean;
|
|
42
|
-
fetchVersion?: (name: string) => string | null;
|
|
43
|
-
runInstall?: (bin: string, args: string[]) => void;
|
|
44
|
-
}): void;
|
|
26
|
+
export function runUpgrade(opts?: {}, adapters?: {}): void;
|
|
45
27
|
/**
|
|
46
28
|
* CLI entry point for `rokkit upgrade`
|
|
47
29
|
* @param {{ apply?: boolean }} opts
|
package/package.json
CHANGED
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}
|
|
183
|
+
* @param {import('./types').FolderOptions} opts
|
|
184
|
+
* @param {{ fn: Function, extra?: Record<string, unknown> }} processor
|
|
180
185
|
*/
|
|
181
|
-
|
|
186
|
+
function processFolders(folders, config, opts, { fn, extra = {} }) {
|
|
182
187
|
for (const folder of folders) {
|
|
183
|
-
|
|
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
|
-
|
|
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
|
|
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/
|
|
57
|
+
const cssHasBase = cssExists && fs.read(cssPath).includes('@rokkit/themes/base.css')
|
|
52
58
|
return {
|
|
53
59
|
id: 'css-imports',
|
|
54
|
-
label: 'app.css
|
|
60
|
+
label: 'app.css imports @rokkit/themes/base.css',
|
|
55
61
|
status: cssHasBase ? 'pass' : 'fail',
|
|
56
62
|
fixable: true,
|
|
57
|
-
fix: 'Append theme
|
|
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([
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
: { ...DEFAULT_COLORS, ...(SKIN_PRESETS[palette] || {}) }
|
|
39
|
+
function resolveColors(palette, customColors) {
|
|
40
|
+
if (palette === 'custom') return { ...DEFAULT_COLORS, ...customColors }
|
|
41
|
+
return { ...DEFAULT_COLORS, ...(SKIN_PRESETS[palette] || {}) }
|
|
42
|
+
}
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
export function generateConfig({ palette, customColors, icons, iconPath, themes, defaultTheme, switcher }) {
|
|
45
|
+
const config = {
|
|
46
|
+
colors: resolveColors(palette, customColors),
|
|
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/
|
|
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
|
|
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 || '
|
|
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
|
|
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',
|
|
@@ -52,10 +53,7 @@ export function serializeConfig(config) {
|
|
|
52
53
|
* cwd?: string
|
|
53
54
|
* }} adapters
|
|
54
55
|
*/
|
|
55
|
-
|
|
56
|
-
const config = await loadConfig(adapters)
|
|
57
|
-
if (!config) return
|
|
58
|
-
const skins = config.skins ?? {}
|
|
56
|
+
function printSkinNames(skins) {
|
|
59
57
|
const names = Object.keys(skins)
|
|
60
58
|
if (names.length === 0) {
|
|
61
59
|
console.info('No skins defined in rokkit.config.js.')
|
|
@@ -71,6 +69,15 @@ export async function runSkinList(adapters = {}) {
|
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
|
|
72
|
+
export async function runSkinList(adapters = {}) {
|
|
73
|
+
const config = await loadConfig(adapters)
|
|
74
|
+
if (!config) {
|
|
75
|
+
console.error('rokkit.config.js not found. Run `rokkit init` first.')
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
printSkinNames(config.skins ?? {})
|
|
79
|
+
}
|
|
80
|
+
|
|
74
81
|
/**
|
|
75
82
|
* Create a new skin entry in rokkit.config.js.
|
|
76
83
|
* @param {string} name
|
|
@@ -80,22 +87,27 @@ export async function runSkinList(adapters = {}) {
|
|
|
80
87
|
* cwd?: string
|
|
81
88
|
* }} adapters
|
|
82
89
|
*/
|
|
83
|
-
|
|
84
|
-
if (!name)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
function validateSkinCreate(name, config) {
|
|
91
|
+
if (!name) return 'Usage: rokkit skin create --name <name>'
|
|
92
|
+
if (!config) return 'rokkit.config.js not found. Run `rokkit init` first.'
|
|
93
|
+
if (config.skins?.[name]) return `Skin "${name}" already exists in rokkit.config.js.`
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
88
96
|
|
|
97
|
+
export async function runSkinCreate(name, adapters = {}) {
|
|
89
98
|
const config = await loadConfig(adapters)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
const error = validateSkinCreate(name, config)
|
|
100
|
+
if (error) {
|
|
101
|
+
if (error.includes('already exists')) {
|
|
102
|
+
console.warn(error)
|
|
103
|
+
} else {
|
|
104
|
+
console.error(error)
|
|
105
|
+
}
|
|
94
106
|
return
|
|
95
107
|
}
|
|
96
108
|
|
|
97
109
|
const updated = addSkinToConfig(config, name)
|
|
98
|
-
|
|
110
|
+
saveConfig(updated, adapters)
|
|
99
111
|
|
|
100
112
|
console.info(`Added skin "${name}" to rokkit.config.js`)
|
|
101
113
|
console.info(
|
|
@@ -104,31 +116,12 @@ export async function runSkinCreate(name, adapters = {}) {
|
|
|
104
116
|
)
|
|
105
117
|
}
|
|
106
118
|
|
|
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
119
|
/**
|
|
127
120
|
* Write updated config using injected adapter or real filesystem.
|
|
128
121
|
* @param {Record<string, unknown>} config
|
|
129
122
|
* @param {{ writeConfig?: (config: Record<string, unknown>) => void, cwd?: string }} adapters
|
|
130
123
|
*/
|
|
131
|
-
|
|
124
|
+
function saveConfig(config, adapters) {
|
|
132
125
|
if (adapters.writeConfig) {
|
|
133
126
|
adapters.writeConfig(config)
|
|
134
127
|
return
|
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.
|
|
@@ -62,33 +63,38 @@ export function generateThemeStub(name) {
|
|
|
62
63
|
* cwd?: string
|
|
63
64
|
* }} adapters
|
|
64
65
|
*/
|
|
65
|
-
|
|
66
|
-
const config = await loadConfig(adapters)
|
|
67
|
-
const builtIn = config?.themes ?? []
|
|
68
|
-
|
|
66
|
+
function getCustomThemes(adapters) {
|
|
69
67
|
const cwd = adapters.cwd ?? process.cwd()
|
|
70
68
|
const themesDir = resolve(cwd, 'src/themes')
|
|
71
69
|
const listFiles = adapters.listFiles ?? ((dir) => (existsSync(dir) ? readdirSync(dir) : []))
|
|
72
|
-
|
|
70
|
+
return listFiles(themesDir)
|
|
73
71
|
.filter((f) => f.endsWith('.css'))
|
|
74
72
|
.map((f) => f.replace(/\.css$/, ''))
|
|
73
|
+
}
|
|
75
74
|
|
|
76
|
-
|
|
75
|
+
function printThemeSection(label, items) {
|
|
76
|
+
if (items.length === 0) return
|
|
77
|
+
console.info(label)
|
|
78
|
+
for (const t of items) console.info(` ${t}`)
|
|
79
|
+
}
|
|
77
80
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (custom.length > 0) {
|
|
83
|
-
console.info('\n Custom (src/themes/):')
|
|
84
|
-
for (const t of custom) console.info(` ${t}`)
|
|
85
|
-
}
|
|
81
|
+
function printThemeList(builtIn, custom) {
|
|
82
|
+
console.info('Rokkit Themes\n')
|
|
83
|
+
printThemeSection(' Built-in (from @rokkit/themes):', builtIn)
|
|
84
|
+
printThemeSection('\n Custom (src/themes/):', custom)
|
|
86
85
|
if (builtIn.length === 0 && custom.length === 0) {
|
|
87
86
|
console.info(' No themes found.')
|
|
88
87
|
console.info(' Run `rokkit theme create --name <name>` to scaffold a custom theme.')
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
90
|
|
|
91
|
+
export async function runThemeList(adapters = {}) {
|
|
92
|
+
const config = await loadConfig(adapters)
|
|
93
|
+
const builtIn = config?.themes ?? []
|
|
94
|
+
const custom = getCustomThemes(adapters)
|
|
95
|
+
printThemeList(builtIn, custom)
|
|
96
|
+
}
|
|
97
|
+
|
|
92
98
|
/**
|
|
93
99
|
* Create a new custom theme CSS stub at src/themes/<name>.css.
|
|
94
100
|
* @param {string} name
|
|
@@ -99,17 +105,22 @@ export async function runThemeList(adapters = {}) {
|
|
|
99
105
|
* cwd?: string
|
|
100
106
|
* }} adapters
|
|
101
107
|
*/
|
|
102
|
-
|
|
108
|
+
function resolveThemeAdapters(adapters) {
|
|
109
|
+
return {
|
|
110
|
+
cwd: adapters.cwd ?? process.cwd(),
|
|
111
|
+
writeFile: adapters.writeFile ?? writeFileSync,
|
|
112
|
+
exists: adapters.exists ?? existsSync,
|
|
113
|
+
mkdir: adapters.mkdir ?? ((dir) => mkdirSync(dir, { recursive: true }))
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function runThemeCreate(name, adapters = {}) {
|
|
103
118
|
if (!name) {
|
|
104
119
|
console.error('Usage: rokkit theme create --name <name>')
|
|
105
120
|
return
|
|
106
121
|
}
|
|
107
122
|
|
|
108
|
-
const cwd
|
|
109
|
-
const writeFile = adapters.writeFile ?? writeFileSync
|
|
110
|
-
const exists = adapters.exists ?? existsSync
|
|
111
|
-
const mkdir = adapters.mkdir ?? ((dir) => mkdirSync(dir, { recursive: true }))
|
|
112
|
-
|
|
123
|
+
const { cwd, writeFile, exists, mkdir } = resolveThemeAdapters(adapters)
|
|
113
124
|
const themesDir = resolve(cwd, 'src/themes')
|
|
114
125
|
const filePath = join(themesDir, `${name}.css`)
|
|
115
126
|
|
|
@@ -128,26 +139,6 @@ export async function runThemeCreate(name, adapters = {}) {
|
|
|
128
139
|
)
|
|
129
140
|
}
|
|
130
141
|
|
|
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
142
|
/**
|
|
152
143
|
* CLI entry point for `rokkit theme` subcommands.
|
|
153
144
|
* @param {string} sub — 'list' | 'create'
|
|
@@ -155,7 +146,7 @@ async function loadConfig(adapters) {
|
|
|
155
146
|
*/
|
|
156
147
|
export async function theme(sub, opts = {}) {
|
|
157
148
|
if (sub === 'create') {
|
|
158
|
-
|
|
149
|
+
runThemeCreate(opts.name)
|
|
159
150
|
} else {
|
|
160
151
|
await runThemeList()
|
|
161
152
|
}
|
package/src/upgrade.js
CHANGED
|
@@ -51,69 +51,92 @@ export function buildInstallCommand(pm, packages) {
|
|
|
51
51
|
* runInstall?: (bin: string, args: string[]) => void
|
|
52
52
|
* }} adapters — injectable for testing
|
|
53
53
|
*/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
54
|
+
function resolveUpgradeAdapters(adapters) {
|
|
55
|
+
return {
|
|
56
|
+
readFile: adapters.readFile ?? ((p) => readFileSync(p, 'utf-8')),
|
|
57
|
+
exists: adapters.exists ?? ((p) => existsSync(p)),
|
|
58
|
+
fetchVersion:
|
|
59
|
+
adapters.fetchVersion ??
|
|
60
|
+
((name) => {
|
|
61
|
+
try {
|
|
62
|
+
return execFileSync('npm', ['view', name, 'version'], { encoding: 'utf-8' }).trim()
|
|
63
|
+
} catch {
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
}),
|
|
67
|
+
runInstall: adapters.runInstall ?? ((bin, args) => execFileSync(bin, args, { stdio: 'inherit' }))
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function formatPackageStatus(pkg, latest) {
|
|
72
|
+
if (!latest) return `OK ${pkg.name} ${pkg.current}`
|
|
73
|
+
if (latest === pkg.current) return `OK ${pkg.name} ${pkg.current}`
|
|
74
|
+
return `OUT ${pkg.name} ${pkg.current} → ${latest}`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function checkPackageVersion(pkg, fetchVersion, upgrades) {
|
|
78
|
+
const latest = fetchVersion(pkg.name)
|
|
79
|
+
console.info(` ${formatPackageStatus(pkg, latest)}`)
|
|
80
|
+
if (latest && latest !== pkg.current) upgrades.push(`${pkg.name}@${latest}`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function checkUpgrades(packages, fetchVersion) {
|
|
84
|
+
const upgrades = []
|
|
85
|
+
for (const pkg of packages) {
|
|
86
|
+
checkPackageVersion(pkg, fetchVersion, upgrades)
|
|
87
|
+
}
|
|
88
|
+
return upgrades
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function applyUpgrades(upgrades, pm, apply, runInstall) {
|
|
92
|
+
const { bin, args } = buildInstallCommand(pm, upgrades)
|
|
93
|
+
const cmdStr = `${bin} ${args.join(' ')}`
|
|
94
|
+
if (apply) {
|
|
95
|
+
console.info(`\nRunning: ${cmdStr}\n`)
|
|
96
|
+
runInstall(bin, args)
|
|
97
|
+
console.info('\nUpgrade complete.')
|
|
98
|
+
} else {
|
|
99
|
+
console.info(`\n${upgrades.length} package(s) can be upgraded.`)
|
|
100
|
+
console.info(`Run with --apply to install, or manually:\n ${cmdStr}`)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
70
103
|
|
|
104
|
+
function loadPackages(cwd, readFile, exists) {
|
|
71
105
|
const pkgPath = resolve(cwd, 'package.json')
|
|
72
106
|
if (!exists(pkgPath)) {
|
|
73
107
|
console.error('No package.json found in current directory.')
|
|
74
|
-
return
|
|
108
|
+
return null
|
|
75
109
|
}
|
|
76
|
-
|
|
77
|
-
const pkgJson = JSON.parse(readFile(pkgPath))
|
|
78
|
-
const packages = getRokkitPackages(pkgJson)
|
|
79
|
-
|
|
110
|
+
const packages = getRokkitPackages(JSON.parse(readFile(pkgPath)))
|
|
80
111
|
if (packages.length === 0) {
|
|
81
112
|
console.info('No @rokkit/* packages found in package.json.')
|
|
82
|
-
return
|
|
113
|
+
return null
|
|
83
114
|
}
|
|
115
|
+
return packages
|
|
116
|
+
}
|
|
84
117
|
|
|
118
|
+
function detectPm(cwd, exists) {
|
|
85
119
|
const LOCKFILES = ['bun.lock', 'bun.lockb', 'pnpm-lock.yaml', 'yarn.lock', 'package-lock.json']
|
|
86
|
-
|
|
87
|
-
|
|
120
|
+
return detectPackageManager(LOCKFILES.filter((f) => exists(resolve(cwd, f))))
|
|
121
|
+
}
|
|
88
122
|
|
|
89
|
-
|
|
123
|
+
export function runUpgrade(opts = {}, adapters = {}) {
|
|
124
|
+
const cwd = process.cwd()
|
|
125
|
+
const { readFile, exists, fetchVersion, runInstall } = resolveUpgradeAdapters(adapters)
|
|
90
126
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
const latest = fetchVersion(pkg.name)
|
|
94
|
-
const upToDate = latest && latest === pkg.current
|
|
95
|
-
const status = !latest ? '?' : upToDate ? 'up-to-date' : 'outdated'
|
|
96
|
-
const arrow = latest ? `${pkg.current} → ${latest}` : pkg.current
|
|
97
|
-
console.info(` ${status === 'outdated' ? 'OUT' : 'OK '} ${pkg.name} ${arrow}`)
|
|
98
|
-
if (!upToDate && latest) upgrades.push(`${pkg.name}@${latest}`)
|
|
99
|
-
}
|
|
127
|
+
const packages = loadPackages(cwd, readFile, exists)
|
|
128
|
+
if (!packages) return
|
|
100
129
|
|
|
130
|
+
const pm = detectPm(cwd, exists)
|
|
131
|
+
console.info('Rokkit Upgrade\n')
|
|
132
|
+
|
|
133
|
+
const upgrades = checkUpgrades(packages, fetchVersion)
|
|
101
134
|
if (upgrades.length === 0) {
|
|
102
135
|
console.info('\nAll packages are up to date.')
|
|
103
136
|
return
|
|
104
137
|
}
|
|
105
138
|
|
|
106
|
-
|
|
107
|
-
const cmdStr = `${bin} ${args.join(' ')}`
|
|
108
|
-
|
|
109
|
-
if (opts.apply) {
|
|
110
|
-
console.info(`\nRunning: ${cmdStr}\n`)
|
|
111
|
-
runInstall(bin, args)
|
|
112
|
-
console.info('\nUpgrade complete.')
|
|
113
|
-
} else {
|
|
114
|
-
console.info(`\n${upgrades.length} package(s) can be upgraded.`)
|
|
115
|
-
console.info(`Run with --apply to install, or manually:\n ${cmdStr}`)
|
|
116
|
-
}
|
|
139
|
+
applyUpgrades(upgrades, pm, opts.apply, runInstall)
|
|
117
140
|
}
|
|
118
141
|
|
|
119
142
|
/**
|