@vue-skuilder/cli 0.1.3 ā 0.1.5
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/cli.js +7 -11
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +41 -11
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/pack.d.ts +3 -0
- package/dist/commands/pack.d.ts.map +1 -0
- package/dist/commands/pack.js +124 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/types.d.ts +24 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +192 -16
- package/dist/types.js.map +1 -1
- package/dist/utils/prompts.d.ts +5 -2
- package/dist/utils/prompts.d.ts.map +1 -1
- package/dist/utils/prompts.js +67 -52
- package/dist/utils/prompts.js.map +1 -1
- package/dist/utils/template.d.ts +10 -0
- package/dist/utils/template.d.ts.map +1 -1
- package/dist/utils/template.js +239 -8
- package/dist/utils/template.js.map +1 -1
- package/package.json +5 -3
- package/src/cli.ts +8 -12
- package/src/commands/init.ts +71 -21
- package/src/commands/pack.ts +163 -0
- package/src/types.ts +224 -18
- package/src/utils/prompts.ts +81 -54
- package/src/utils/template.ts +275 -37
package/src/utils/prompts.ts
CHANGED
|
@@ -1,6 +1,78 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { CliOptions, ProjectConfig, PREDEFINED_THEMES
|
|
3
|
+
import { CliOptions, ProjectConfig, PREDEFINED_THEMES } from '../types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert hex color to closest ANSI color code
|
|
7
|
+
*/
|
|
8
|
+
function hexToAnsi(hex: string): string {
|
|
9
|
+
// Remove # if present
|
|
10
|
+
hex = hex.replace('#', '');
|
|
11
|
+
|
|
12
|
+
// Convert hex to RGB
|
|
13
|
+
const r = parseInt(hex.substr(0, 2), 16);
|
|
14
|
+
const g = parseInt(hex.substr(2, 2), 16);
|
|
15
|
+
const b = parseInt(hex.substr(4, 2), 16);
|
|
16
|
+
|
|
17
|
+
// Convert to 256-color ANSI
|
|
18
|
+
const ansiCode = 16 + (36 * Math.round(r / 255 * 5)) + (6 * Math.round(g / 255 * 5)) + Math.round(b / 255 * 5);
|
|
19
|
+
return `\x1b[48;5;${ansiCode}m`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a color swatch for terminal display
|
|
24
|
+
*/
|
|
25
|
+
function createColorSwatch(hex: string, label: string): string {
|
|
26
|
+
const colorCode = hexToAnsi(hex);
|
|
27
|
+
const reset = '\x1b[0m';
|
|
28
|
+
return `${colorCode} ${reset} ${label}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create theme preview with color swatches
|
|
33
|
+
*/
|
|
34
|
+
function createThemePreview(themeName: string): string {
|
|
35
|
+
const theme = PREDEFINED_THEMES[themeName];
|
|
36
|
+
const lightColors = theme.light.colors;
|
|
37
|
+
|
|
38
|
+
const primarySwatch = createColorSwatch(lightColors.primary, 'Primary');
|
|
39
|
+
const secondarySwatch = createColorSwatch(lightColors.secondary, 'Secondary');
|
|
40
|
+
const accentSwatch = createColorSwatch(lightColors.accent, 'Accent');
|
|
41
|
+
|
|
42
|
+
return `${primarySwatch} ${secondarySwatch} ${accentSwatch}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Display comprehensive theme preview after selection
|
|
47
|
+
*/
|
|
48
|
+
export function displayThemePreview(themeName: string): void {
|
|
49
|
+
const theme = PREDEFINED_THEMES[themeName];
|
|
50
|
+
|
|
51
|
+
console.log(chalk.cyan('\nšØ Theme Color Palette:'));
|
|
52
|
+
console.log(chalk.white(` ${theme.name.toUpperCase()} THEME`));
|
|
53
|
+
|
|
54
|
+
// Light theme colors
|
|
55
|
+
console.log(chalk.white('\n Light Mode:'));
|
|
56
|
+
const lightColors = theme.light.colors;
|
|
57
|
+
console.log(` ${createColorSwatch(lightColors.primary, `Primary: ${lightColors.primary}`)}`);
|
|
58
|
+
console.log(` ${createColorSwatch(lightColors.secondary, `Secondary: ${lightColors.secondary}`)}`);
|
|
59
|
+
console.log(` ${createColorSwatch(lightColors.accent, `Accent: ${lightColors.accent}`)}`);
|
|
60
|
+
console.log(` ${createColorSwatch(lightColors.success, `Success: ${lightColors.success}`)}`);
|
|
61
|
+
console.log(` ${createColorSwatch(lightColors.warning, `Warning: ${lightColors.warning}`)}`);
|
|
62
|
+
console.log(` ${createColorSwatch(lightColors.error, `Error: ${lightColors.error}`)}`);
|
|
63
|
+
|
|
64
|
+
// Dark theme colors
|
|
65
|
+
console.log(chalk.white('\n Dark Mode:'));
|
|
66
|
+
const darkColors = theme.dark.colors;
|
|
67
|
+
console.log(` ${createColorSwatch(darkColors.primary, `Primary: ${darkColors.primary}`)}`);
|
|
68
|
+
console.log(` ${createColorSwatch(darkColors.secondary, `Secondary: ${darkColors.secondary}`)}`);
|
|
69
|
+
console.log(` ${createColorSwatch(darkColors.accent, `Accent: ${darkColors.accent}`)}`);
|
|
70
|
+
console.log(` ${createColorSwatch(darkColors.success, `Success: ${darkColors.success}`)}`);
|
|
71
|
+
console.log(` ${createColorSwatch(darkColors.warning, `Warning: ${darkColors.warning}`)}`);
|
|
72
|
+
console.log(` ${createColorSwatch(darkColors.error, `Error: ${darkColors.error}`)}`);
|
|
73
|
+
|
|
74
|
+
console.log(chalk.gray(`\n Default mode: ${theme.defaultMode}`));
|
|
75
|
+
}
|
|
4
76
|
|
|
5
77
|
export async function gatherProjectConfig(
|
|
6
78
|
projectName: string,
|
|
@@ -65,19 +137,19 @@ export async function gatherProjectConfig(
|
|
|
65
137
|
message: 'Select theme:',
|
|
66
138
|
choices: [
|
|
67
139
|
{
|
|
68
|
-
name:
|
|
140
|
+
name: `Default (Material Blue) ${createThemePreview('default')}`,
|
|
69
141
|
value: 'default'
|
|
70
142
|
},
|
|
71
143
|
{
|
|
72
|
-
name:
|
|
144
|
+
name: `Medical (Healthcare Green) ${createThemePreview('medical')}`,
|
|
73
145
|
value: 'medical'
|
|
74
146
|
},
|
|
75
147
|
{
|
|
76
|
-
name:
|
|
148
|
+
name: `Educational (Academic Orange) ${createThemePreview('educational')}`,
|
|
77
149
|
value: 'educational'
|
|
78
150
|
},
|
|
79
151
|
{
|
|
80
|
-
name:
|
|
152
|
+
name: `Corporate (Professional Gray) ${createThemePreview('corporate')}`,
|
|
81
153
|
value: 'corporate'
|
|
82
154
|
}
|
|
83
155
|
],
|
|
@@ -93,6 +165,9 @@ export async function gatherProjectConfig(
|
|
|
93
165
|
course: answers.courseId,
|
|
94
166
|
theme: PREDEFINED_THEMES[answers.themeName]
|
|
95
167
|
};
|
|
168
|
+
|
|
169
|
+
// Show comprehensive theme preview
|
|
170
|
+
displayThemePreview(answers.themeName);
|
|
96
171
|
} else {
|
|
97
172
|
// Non-interactive mode: use provided options
|
|
98
173
|
config = {
|
|
@@ -130,7 +205,7 @@ export async function confirmProjectCreation(
|
|
|
130
205
|
console.log(` Course ID: ${chalk.white(config.course)}`);
|
|
131
206
|
}
|
|
132
207
|
|
|
133
|
-
console.log(` Theme: ${chalk.white(config.theme.name)}`);
|
|
208
|
+
console.log(` Theme: ${chalk.white(config.theme.name)} ${createThemePreview(config.theme.name)}`);
|
|
134
209
|
console.log(` Directory: ${chalk.white(projectPath)}`);
|
|
135
210
|
|
|
136
211
|
const { confirmed } = await inquirer.prompt([
|
|
@@ -145,48 +220,7 @@ export async function confirmProjectCreation(
|
|
|
145
220
|
return confirmed;
|
|
146
221
|
}
|
|
147
222
|
|
|
148
|
-
export async function promptForCustomTheme(): Promise<ThemeConfig> {
|
|
149
|
-
console.log(chalk.cyan('\nšØ Custom Theme Configuration\n'));
|
|
150
|
-
|
|
151
|
-
const answers = await inquirer.prompt([
|
|
152
|
-
{
|
|
153
|
-
type: 'input',
|
|
154
|
-
name: 'name',
|
|
155
|
-
message: 'Theme name:',
|
|
156
|
-
validate: (input: string) => input.trim().length > 0 || 'Theme name is required'
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
type: 'input',
|
|
160
|
-
name: 'primary',
|
|
161
|
-
message: 'Primary color (hex):',
|
|
162
|
-
default: '#1976D2',
|
|
163
|
-
validate: validateHexColor
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
type: 'input',
|
|
167
|
-
name: 'secondary',
|
|
168
|
-
message: 'Secondary color (hex):',
|
|
169
|
-
default: '#424242',
|
|
170
|
-
validate: validateHexColor
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
type: 'input',
|
|
174
|
-
name: 'accent',
|
|
175
|
-
message: 'Accent color (hex):',
|
|
176
|
-
default: '#82B1FF',
|
|
177
|
-
validate: validateHexColor
|
|
178
|
-
}
|
|
179
|
-
]);
|
|
180
223
|
|
|
181
|
-
return {
|
|
182
|
-
name: answers.name,
|
|
183
|
-
colors: {
|
|
184
|
-
primary: answers.primary,
|
|
185
|
-
secondary: answers.secondary,
|
|
186
|
-
accent: answers.accent
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
224
|
|
|
191
225
|
function formatProjectName(projectName: string): string {
|
|
192
226
|
return projectName
|
|
@@ -195,10 +229,3 @@ function formatProjectName(projectName: string): string {
|
|
|
195
229
|
.join(' ');
|
|
196
230
|
}
|
|
197
231
|
|
|
198
|
-
function validateHexColor(input: string): boolean | string {
|
|
199
|
-
const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
|
200
|
-
if (!hexColorRegex.test(input)) {
|
|
201
|
-
return 'Please enter a valid hex color (e.g., #1976D2)';
|
|
202
|
-
}
|
|
203
|
-
return true;
|
|
204
|
-
}
|
package/src/utils/template.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import { existsSync } from 'fs';
|
|
1
|
+
import { promises as fs, existsSync } from 'fs';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import { fileURLToPath } from 'url';
|
|
5
4
|
import chalk from 'chalk';
|
|
@@ -14,7 +13,7 @@ const __dirname = path.dirname(__filename);
|
|
|
14
13
|
export async function findStandaloneUiPath(): Promise<string> {
|
|
15
14
|
// Start from CLI package root and work upward
|
|
16
15
|
let currentDir = path.join(__dirname, '..', '..');
|
|
17
|
-
|
|
16
|
+
|
|
18
17
|
while (currentDir !== path.dirname(currentDir)) {
|
|
19
18
|
const nodeModulesPath = path.join(currentDir, 'node_modules', '@vue-skuilder', 'standalone-ui');
|
|
20
19
|
if (existsSync(nodeModulesPath)) {
|
|
@@ -22,8 +21,10 @@ export async function findStandaloneUiPath(): Promise<string> {
|
|
|
22
21
|
}
|
|
23
22
|
currentDir = path.dirname(currentDir);
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
throw new Error(
|
|
24
|
+
|
|
25
|
+
throw new Error(
|
|
26
|
+
'Could not find @vue-skuilder/standalone-ui package. Please ensure it is installed.'
|
|
27
|
+
);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
/**
|
|
@@ -35,18 +36,18 @@ export async function copyDirectory(
|
|
|
35
36
|
excludePatterns: string[] = ['node_modules', 'dist', '.git', 'cypress']
|
|
36
37
|
): Promise<void> {
|
|
37
38
|
const entries = await fs.readdir(source, { withFileTypes: true });
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
await fs.mkdir(destination, { recursive: true });
|
|
40
|
-
|
|
41
|
+
|
|
41
42
|
for (const entry of entries) {
|
|
42
43
|
const sourcePath = path.join(source, entry.name);
|
|
43
44
|
const destPath = path.join(destination, entry.name);
|
|
44
|
-
|
|
45
|
+
|
|
45
46
|
// Skip excluded patterns
|
|
46
|
-
if (excludePatterns.some(pattern => entry.name.includes(pattern))) {
|
|
47
|
+
if (excludePatterns.some((pattern) => entry.name.includes(pattern))) {
|
|
47
48
|
continue;
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
+
|
|
50
51
|
if (entry.isDirectory()) {
|
|
51
52
|
await copyDirectory(sourcePath, destPath, excludePatterns);
|
|
52
53
|
} else {
|
|
@@ -65,12 +66,12 @@ export async function transformPackageJson(
|
|
|
65
66
|
): Promise<void> {
|
|
66
67
|
const content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
67
68
|
const packageJson = JSON.parse(content);
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
// Update basic project info
|
|
70
71
|
packageJson.name = projectName;
|
|
71
72
|
packageJson.description = `Skuilder course application: ${projectName}`;
|
|
72
73
|
packageJson.version = '1.0.0';
|
|
73
|
-
|
|
74
|
+
|
|
74
75
|
// Transform workspace dependencies to published versions
|
|
75
76
|
if (packageJson.dependencies) {
|
|
76
77
|
for (const [depName, version] of Object.entries(packageJson.dependencies)) {
|
|
@@ -80,13 +81,87 @@ export async function transformPackageJson(
|
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
|
-
|
|
84
|
+
|
|
85
|
+
// Add missing terser devDependency for build minification
|
|
86
|
+
if (packageJson.devDependencies && !packageJson.devDependencies['terser']) {
|
|
87
|
+
packageJson.devDependencies['terser'] = '^5.39.0';
|
|
88
|
+
}
|
|
89
|
+
|
|
84
90
|
// Remove CLI-specific fields that don't belong in generated projects
|
|
85
91
|
delete packageJson.publishConfig;
|
|
86
|
-
|
|
92
|
+
|
|
87
93
|
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
88
94
|
}
|
|
89
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Create a vite.config.ts to work with published packages instead of workspace sources
|
|
98
|
+
*
|
|
99
|
+
* // [ ] This should be revised so that it works from the existing vite.config.ts in standalone-ui. As is, it recreates 95% of the same config.
|
|
100
|
+
*/
|
|
101
|
+
export async function createViteConfig(viteConfigPath: string): Promise<void> {
|
|
102
|
+
// Create a clean vite config for standalone projects
|
|
103
|
+
const transformedContent = `// packages/standalone-ui/vite.config.ts
|
|
104
|
+
import { defineConfig } from 'vite';
|
|
105
|
+
import vue from '@vitejs/plugin-vue';
|
|
106
|
+
import { fileURLToPath, URL } from 'node:url';
|
|
107
|
+
|
|
108
|
+
export default defineConfig({
|
|
109
|
+
plugins: [vue()],
|
|
110
|
+
resolve: {
|
|
111
|
+
alias: {
|
|
112
|
+
// Alias for internal src paths
|
|
113
|
+
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
|
114
|
+
|
|
115
|
+
// Add events alias if needed (often required by dependencies)
|
|
116
|
+
events: 'events',
|
|
117
|
+
},
|
|
118
|
+
extensions: ['.js', '.ts', '.json', '.vue'],
|
|
119
|
+
dedupe: [
|
|
120
|
+
// Ensure single instances of core libs and published packages
|
|
121
|
+
'vue',
|
|
122
|
+
'vuetify',
|
|
123
|
+
'pinia',
|
|
124
|
+
'vue-router',
|
|
125
|
+
'@vue-skuilder/db',
|
|
126
|
+
'@vue-skuilder/common',
|
|
127
|
+
'@vue-skuilder/common-ui',
|
|
128
|
+
'@vue-skuilder/courses',
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
// --- Dependencies optimization ---
|
|
132
|
+
optimizeDeps: {
|
|
133
|
+
// Help Vite pre-bundle dependencies from published packages
|
|
134
|
+
include: [
|
|
135
|
+
'@vue-skuilder/common-ui',
|
|
136
|
+
'@vue-skuilder/db',
|
|
137
|
+
'@vue-skuilder/common',
|
|
138
|
+
'@vue-skuilder/courses',
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
server: {
|
|
142
|
+
port: 5173, // Use standard Vite port for standalone projects
|
|
143
|
+
},
|
|
144
|
+
build: {
|
|
145
|
+
sourcemap: true,
|
|
146
|
+
target: 'es2020',
|
|
147
|
+
minify: 'terser',
|
|
148
|
+
terserOptions: {
|
|
149
|
+
keep_classnames: true,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
// Add define block for process polyfills
|
|
153
|
+
define: {
|
|
154
|
+
global: 'window',
|
|
155
|
+
'process.env': process.env,
|
|
156
|
+
'process.browser': true,
|
|
157
|
+
'process.version': JSON.stringify(process.version),
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
`;
|
|
161
|
+
|
|
162
|
+
await fs.writeFile(viteConfigPath, transformedContent);
|
|
163
|
+
}
|
|
164
|
+
|
|
90
165
|
/**
|
|
91
166
|
* Generate skuilder.config.json based on project configuration
|
|
92
167
|
*/
|
|
@@ -96,35 +171,156 @@ export async function generateSkuilderConfig(
|
|
|
96
171
|
): Promise<void> {
|
|
97
172
|
const skuilderConfig: SkuilderConfig = {
|
|
98
173
|
title: config.title,
|
|
99
|
-
dataLayerType: config.dataLayerType
|
|
174
|
+
dataLayerType: config.dataLayerType,
|
|
100
175
|
};
|
|
101
|
-
|
|
176
|
+
|
|
102
177
|
if (config.course) {
|
|
103
178
|
skuilderConfig.course = config.course;
|
|
104
179
|
}
|
|
105
|
-
|
|
180
|
+
|
|
106
181
|
if (config.couchdbUrl) {
|
|
107
182
|
skuilderConfig.couchdbUrl = config.couchdbUrl;
|
|
108
183
|
}
|
|
109
|
-
|
|
184
|
+
|
|
110
185
|
if (config.theme) {
|
|
111
186
|
skuilderConfig.theme = config.theme;
|
|
112
187
|
}
|
|
113
|
-
|
|
188
|
+
|
|
114
189
|
await fs.writeFile(configPath, JSON.stringify(skuilderConfig, null, 2));
|
|
115
190
|
}
|
|
116
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Generate .gitignore file for the project
|
|
194
|
+
*/
|
|
195
|
+
export async function generateGitignore(gitignorePath: string): Promise<void> {
|
|
196
|
+
const gitignoreContent = `# Dependencies
|
|
197
|
+
node_modules/
|
|
198
|
+
/.pnp
|
|
199
|
+
.pnp.js
|
|
200
|
+
|
|
201
|
+
# Production builds
|
|
202
|
+
/dist
|
|
203
|
+
/build
|
|
204
|
+
|
|
205
|
+
# Local env files
|
|
206
|
+
.env
|
|
207
|
+
.env.local
|
|
208
|
+
.env.development.local
|
|
209
|
+
.env.test.local
|
|
210
|
+
.env.production.local
|
|
211
|
+
|
|
212
|
+
# Log files
|
|
213
|
+
npm-debug.log*
|
|
214
|
+
yarn-debug.log*
|
|
215
|
+
yarn-error.log*
|
|
216
|
+
pnpm-debug.log*
|
|
217
|
+
lerna-debug.log*
|
|
218
|
+
|
|
219
|
+
# Runtime data
|
|
220
|
+
pids
|
|
221
|
+
*.pid
|
|
222
|
+
*.seed
|
|
223
|
+
*.pid.lock
|
|
224
|
+
|
|
225
|
+
# Coverage directory used by tools like istanbul
|
|
226
|
+
coverage/
|
|
227
|
+
*.lcov
|
|
228
|
+
|
|
229
|
+
# nyc test coverage
|
|
230
|
+
.nyc_output
|
|
231
|
+
|
|
232
|
+
# Dependency directories
|
|
233
|
+
jspm_packages/
|
|
234
|
+
|
|
235
|
+
# TypeScript cache
|
|
236
|
+
*.tsbuildinfo
|
|
237
|
+
|
|
238
|
+
# Optional npm cache directory
|
|
239
|
+
.npm
|
|
240
|
+
|
|
241
|
+
# Optional eslint cache
|
|
242
|
+
.eslintcache
|
|
243
|
+
|
|
244
|
+
# Microbundle cache
|
|
245
|
+
.rpt2_cache/
|
|
246
|
+
.rts2_cache_cjs/
|
|
247
|
+
.rts2_cache_es/
|
|
248
|
+
.rts2_cache_umd/
|
|
249
|
+
|
|
250
|
+
# Optional REPL history
|
|
251
|
+
.node_repl_history
|
|
252
|
+
|
|
253
|
+
# Output of 'npm pack'
|
|
254
|
+
*.tgz
|
|
255
|
+
|
|
256
|
+
# Yarn Integrity file
|
|
257
|
+
.yarn-integrity
|
|
258
|
+
|
|
259
|
+
# parcel-bundler cache (https://parceljs.org/)
|
|
260
|
+
.cache
|
|
261
|
+
.parcel-cache
|
|
262
|
+
|
|
263
|
+
# Next.js build output
|
|
264
|
+
.next
|
|
265
|
+
|
|
266
|
+
# Nuxt.js build / generate output
|
|
267
|
+
.nuxt
|
|
268
|
+
dist
|
|
269
|
+
|
|
270
|
+
# Gatsby files
|
|
271
|
+
.cache/
|
|
272
|
+
public
|
|
273
|
+
|
|
274
|
+
# Storybook build outputs
|
|
275
|
+
.out
|
|
276
|
+
.storybook-out
|
|
277
|
+
|
|
278
|
+
# Temporary folders
|
|
279
|
+
tmp/
|
|
280
|
+
temp/
|
|
281
|
+
|
|
282
|
+
# Editor directories and files
|
|
283
|
+
.vscode/
|
|
284
|
+
.idea
|
|
285
|
+
.DS_Store
|
|
286
|
+
*.suo
|
|
287
|
+
*.ntvs*
|
|
288
|
+
*.njsproj
|
|
289
|
+
*.sln
|
|
290
|
+
*.sw?
|
|
291
|
+
|
|
292
|
+
# OS generated files
|
|
293
|
+
Thumbs.db
|
|
294
|
+
|
|
295
|
+
# Cypress
|
|
296
|
+
/cypress/videos/
|
|
297
|
+
/cypress/screenshots/
|
|
298
|
+
|
|
299
|
+
# Local development
|
|
300
|
+
.env.development
|
|
301
|
+
.env.production
|
|
302
|
+
|
|
303
|
+
# Package manager lockfiles (uncomment if you want to ignore them)
|
|
304
|
+
# package-lock.json
|
|
305
|
+
# yarn.lock
|
|
306
|
+
# pnpm-lock.yaml
|
|
307
|
+
|
|
308
|
+
# Skuilder specific
|
|
309
|
+
/src/data/local-*.json
|
|
310
|
+
`;
|
|
311
|
+
|
|
312
|
+
await fs.writeFile(gitignorePath, gitignoreContent);
|
|
313
|
+
}
|
|
314
|
+
|
|
117
315
|
/**
|
|
118
316
|
* Generate project README.md
|
|
119
317
|
*/
|
|
120
|
-
export async function generateReadme(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
: `This project connects to CouchDB at: ${config.couchdbUrl || '[URL not specified]'}`;
|
|
127
|
-
|
|
318
|
+
export async function generateReadme(readmePath: string, config: ProjectConfig): Promise<void> {
|
|
319
|
+
const dataLayerInfo =
|
|
320
|
+
config.dataLayerType === 'static'
|
|
321
|
+
? 'This project uses a static data layer with JSON files.'
|
|
322
|
+
: `This project connects to CouchDB at: ${config.couchdbUrl || '[URL not specified]'}`;
|
|
323
|
+
|
|
128
324
|
const readme = `# ${config.title}
|
|
129
325
|
|
|
130
326
|
A Skuilder course application built with Vue 3, Vuetify, and Pinia.
|
|
@@ -160,10 +356,42 @@ Course configuration is managed in \`skuilder.config.json\`. You can modify:
|
|
|
160
356
|
|
|
161
357
|
## Theme
|
|
162
358
|
|
|
163
|
-
Current theme: **${config.theme.name}**
|
|
164
|
-
- Primary: ${config.theme.colors.primary}
|
|
165
|
-
- Secondary: ${config.theme.colors.secondary}
|
|
166
|
-
- Accent: ${config.theme.colors.accent}
|
|
359
|
+
Current theme: **${config.theme.name}** (${config.theme.defaultMode} mode)
|
|
360
|
+
- Primary: ${config.theme.light.colors.primary}
|
|
361
|
+
- Secondary: ${config.theme.light.colors.secondary}
|
|
362
|
+
- Accent: ${config.theme.light.colors.accent}
|
|
363
|
+
|
|
364
|
+
This theme includes both light and dark variants. The application will use the ${config.theme.defaultMode} theme by default, but users can toggle between light and dark modes in their settings.
|
|
365
|
+
|
|
366
|
+
### Theme Customization
|
|
367
|
+
|
|
368
|
+
To customize the theme colors, edit the \`theme\` section in \`skuilder.config.json\`:
|
|
369
|
+
|
|
370
|
+
\`\`\`json
|
|
371
|
+
{
|
|
372
|
+
"theme": {
|
|
373
|
+
"name": "custom",
|
|
374
|
+
"defaultMode": "light",
|
|
375
|
+
"light": {
|
|
376
|
+
"dark": false,
|
|
377
|
+
"colors": {
|
|
378
|
+
"primary": "#your-color",
|
|
379
|
+
"secondary": "#your-color",
|
|
380
|
+
"accent": "#your-color"
|
|
381
|
+
// ... other semantic colors
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
"dark": {
|
|
385
|
+
"dark": true,
|
|
386
|
+
"colors": {
|
|
387
|
+
// ... dark variant colors
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
\`\`\`
|
|
393
|
+
|
|
394
|
+
The theme system supports all Vuetify semantic colors including error, success, warning, info, background, surface, and text colors. Changes to the configuration file are applied automatically on restart.
|
|
167
395
|
|
|
168
396
|
## Testing
|
|
169
397
|
|
|
@@ -195,21 +423,31 @@ export async function processTemplate(
|
|
|
195
423
|
): Promise<void> {
|
|
196
424
|
console.log(chalk.blue('š¦ Locating standalone-ui template...'));
|
|
197
425
|
const templatePath = await findStandaloneUiPath();
|
|
198
|
-
|
|
426
|
+
|
|
199
427
|
console.log(chalk.blue('š Copying project files...'));
|
|
200
428
|
await copyDirectory(templatePath, projectPath);
|
|
201
|
-
|
|
429
|
+
|
|
202
430
|
console.log(chalk.blue('āļø Configuring package.json...'));
|
|
203
431
|
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
204
432
|
await transformPackageJson(packageJsonPath, config.projectName, cliVersion);
|
|
205
|
-
|
|
433
|
+
|
|
434
|
+
console.log(chalk.blue('š§ Creating vite.config.ts...'));
|
|
435
|
+
const viteConfigPath = path.join(projectPath, 'vite.config.ts');
|
|
436
|
+
if (existsSync(viteConfigPath)) {
|
|
437
|
+
await createViteConfig(viteConfigPath);
|
|
438
|
+
}
|
|
439
|
+
|
|
206
440
|
console.log(chalk.blue('š§ Generating configuration...'));
|
|
207
441
|
const configPath = path.join(projectPath, 'skuilder.config.json');
|
|
208
442
|
await generateSkuilderConfig(configPath, config);
|
|
209
|
-
|
|
443
|
+
|
|
210
444
|
console.log(chalk.blue('š Creating README...'));
|
|
211
445
|
const readmePath = path.join(projectPath, 'README.md');
|
|
212
446
|
await generateReadme(readmePath, config);
|
|
213
|
-
|
|
447
|
+
|
|
448
|
+
console.log(chalk.blue('š Generating .gitignore...'));
|
|
449
|
+
const gitignorePath = path.join(projectPath, '.gitignore');
|
|
450
|
+
await generateGitignore(gitignorePath);
|
|
451
|
+
|
|
214
452
|
console.log(chalk.green('ā
Template processing complete!'));
|
|
215
|
-
}
|
|
453
|
+
}
|