@vue-skuilder/cli 0.1.6 → 0.1.7
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 +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/studio.d.ts +3 -0
- package/dist/commands/studio.d.ts.map +1 -0
- package/dist/commands/studio.js +396 -0
- package/dist/commands/studio.js.map +1 -0
- package/dist/commands/unpack.d.ts +3 -0
- package/dist/commands/unpack.d.ts.map +1 -0
- package/dist/commands/unpack.js +228 -0
- package/dist/commands/unpack.js.map +1 -0
- package/dist/studio-ui-assets/assets/BrowseView-BJbixGOU.js +2 -0
- package/dist/studio-ui-assets/assets/BrowseView-BJbixGOU.js.map +1 -0
- package/dist/studio-ui-assets/assets/BrowseView-CM4HBO4j.css +1 -0
- package/dist/studio-ui-assets/assets/BulkImportView-DB6DYDJU.js +2 -0
- package/dist/studio-ui-assets/assets/BulkImportView-DB6DYDJU.js.map +1 -0
- package/dist/studio-ui-assets/assets/BulkImportView-g4wQUfPA.css +1 -0
- package/dist/studio-ui-assets/assets/CourseEditorView-BIlhlhw1.js +2 -0
- package/dist/studio-ui-assets/assets/CourseEditorView-BIlhlhw1.js.map +1 -0
- package/dist/studio-ui-assets/assets/CourseEditorView-WuPNLVKp.css +1 -0
- package/dist/studio-ui-assets/assets/CreateCardView-CyNOKCkm.css +1 -0
- package/dist/studio-ui-assets/assets/CreateCardView-DPjPvzzt.js +2 -0
- package/dist/studio-ui-assets/assets/CreateCardView-DPjPvzzt.js.map +1 -0
- package/dist/studio-ui-assets/assets/edit-ui.es-DiUxqbgF.js +330 -0
- package/dist/studio-ui-assets/assets/edit-ui.es-DiUxqbgF.js.map +1 -0
- package/dist/studio-ui-assets/assets/index--zY88pg6.css +14 -0
- package/dist/studio-ui-assets/assets/index-BnAv1C72.js +287 -0
- package/dist/studio-ui-assets/assets/index-BnAv1C72.js.map +1 -0
- package/dist/studio-ui-assets/assets/index-DHMXQY3-.js +192 -0
- package/dist/studio-ui-assets/assets/index-DHMXQY3-.js.map +1 -0
- package/dist/studio-ui-assets/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
- package/dist/studio-ui-assets/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
- package/dist/studio-ui-assets/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
- package/dist/studio-ui-assets/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
- package/dist/studio-ui-assets/assets/vue-DZcMATiC.js +28 -0
- package/dist/studio-ui-assets/assets/vue-DZcMATiC.js.map +1 -0
- package/dist/studio-ui-assets/assets/vuetify-qg7mRxy_.js +6 -0
- package/dist/studio-ui-assets/assets/vuetify-qg7mRxy_.js.map +1 -0
- package/dist/studio-ui-assets/index.html +16 -0
- package/dist/utils/NodeFileSystemAdapter.d.ts +14 -0
- package/dist/utils/NodeFileSystemAdapter.d.ts.map +1 -0
- package/dist/utils/NodeFileSystemAdapter.js +55 -0
- package/dist/utils/NodeFileSystemAdapter.js.map +1 -0
- package/dist/utils/template.d.ts +1 -1
- package/dist/utils/template.d.ts.map +1 -1
- package/dist/utils/template.js +19 -5
- package/dist/utils/template.js.map +1 -1
- package/package.json +7 -4
- package/src/cli.ts +8 -0
- package/src/commands/studio.ts +497 -0
- package/src/commands/unpack.ts +259 -0
- package/src/utils/NodeFileSystemAdapter.ts +72 -0
- package/src/utils/template.ts +26 -7
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import PouchDB from 'pouchdb';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { StaticToCouchDBMigrator, validateStaticCourse, CourseLookup, ENV } from '@vue-skuilder/db';
|
|
6
|
+
import { NodeFileSystemAdapter } from '../utils/NodeFileSystemAdapter.js';
|
|
7
|
+
|
|
8
|
+
export function createUnpackCommand(): Command {
|
|
9
|
+
return new Command('unpack')
|
|
10
|
+
.description('Unpack a static course directory into CouchDB')
|
|
11
|
+
.argument('<coursePath>', 'Path to static course directory')
|
|
12
|
+
.option('-s, --server <url>', 'CouchDB server URL', 'http://localhost:5984')
|
|
13
|
+
.option('-u, --username <username>', 'CouchDB username')
|
|
14
|
+
.option('-p, --password <password>', 'CouchDB password')
|
|
15
|
+
.option('-d, --database <name>', 'Target database name (auto-generated if not provided)')
|
|
16
|
+
.option('--as <name>', 'Set a custom name for the unpacked course')
|
|
17
|
+
.option('--chunk-size <size>', 'Documents per batch', '100')
|
|
18
|
+
.option('--validate', 'Run migration validation')
|
|
19
|
+
.option('--cleanup-on-error', 'Clean up database if migration fails')
|
|
20
|
+
.action(unpackCourse);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface UnpackOptions {
|
|
24
|
+
server: string;
|
|
25
|
+
username?: string;
|
|
26
|
+
password?: string;
|
|
27
|
+
database?: string;
|
|
28
|
+
as?: string;
|
|
29
|
+
chunkSize: string;
|
|
30
|
+
validate: boolean;
|
|
31
|
+
cleanupOnError: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function unpackCourse(coursePath: string, options: UnpackOptions) {
|
|
35
|
+
// Store original ENV values for cleanup
|
|
36
|
+
const originalEnv = {
|
|
37
|
+
COUCHDB_SERVER_PROTOCOL: ENV.COUCHDB_SERVER_PROTOCOL,
|
|
38
|
+
COUCHDB_SERVER_URL: ENV.COUCHDB_SERVER_URL,
|
|
39
|
+
COUCHDB_USERNAME: ENV.COUCHDB_USERNAME,
|
|
40
|
+
COUCHDB_PASSWORD: ENV.COUCHDB_PASSWORD,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
console.log(chalk.cyan(`🔧 Unpacking static course to CouchDB...`));
|
|
45
|
+
console.log(chalk.gray(`📁 Source: ${path.resolve(coursePath)}`));
|
|
46
|
+
|
|
47
|
+
// Create file system adapter
|
|
48
|
+
const fileSystemAdapter = new NodeFileSystemAdapter();
|
|
49
|
+
|
|
50
|
+
// Validate static course directory
|
|
51
|
+
console.log(chalk.cyan('🔍 Validating static course...'));
|
|
52
|
+
const validation = await validateStaticCourse(coursePath, fileSystemAdapter);
|
|
53
|
+
|
|
54
|
+
if (!validation.valid) {
|
|
55
|
+
console.log(chalk.red('❌ Static course validation failed:'));
|
|
56
|
+
validation.errors.forEach((error: string) => {
|
|
57
|
+
console.log(chalk.red(` • ${error}`));
|
|
58
|
+
});
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (validation.warnings.length > 0) {
|
|
63
|
+
console.log(chalk.yellow('⚠️ Validation warnings:'));
|
|
64
|
+
validation.warnings.forEach((warning: string) => {
|
|
65
|
+
console.log(chalk.yellow(` • ${warning}`));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(chalk.green('✅ Static course validation passed'));
|
|
70
|
+
console.log(chalk.gray(`📋 Course: ${validation.courseName || 'Unknown'} (${validation.courseId || 'Unknown ID'})`));
|
|
71
|
+
|
|
72
|
+
// Generate studio course ID and database name if not provided
|
|
73
|
+
let targetDbName = options.database;
|
|
74
|
+
let studioCourseId: string;
|
|
75
|
+
|
|
76
|
+
if (!targetDbName) {
|
|
77
|
+
const timestamp = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
78
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
79
|
+
studioCourseId = `unpacked_${validation.courseId || 'unknown'}_${timestamp}_${random}`;
|
|
80
|
+
targetDbName = `coursedb-${studioCourseId}`;
|
|
81
|
+
} else {
|
|
82
|
+
// If user provided custom database name, extract studio course ID from it
|
|
83
|
+
studioCourseId = targetDbName.startsWith('coursedb-')
|
|
84
|
+
? targetDbName.substring(9)
|
|
85
|
+
: targetDbName;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Construct database URL
|
|
89
|
+
const dbUrl = `${options.server}/${targetDbName}`;
|
|
90
|
+
console.log(chalk.gray(`📡 Target: ${dbUrl}`));
|
|
91
|
+
|
|
92
|
+
// Setup database connection options
|
|
93
|
+
const dbOptions: Record<string, unknown> = {};
|
|
94
|
+
if (options.username && options.password) {
|
|
95
|
+
dbOptions.auth = {
|
|
96
|
+
username: options.username,
|
|
97
|
+
password: options.password,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Create and connect to target database
|
|
102
|
+
console.log(chalk.cyan('🔄 Creating target database...'));
|
|
103
|
+
const targetDB = new PouchDB(dbUrl, dbOptions);
|
|
104
|
+
|
|
105
|
+
// Test connection by trying to get database info
|
|
106
|
+
try {
|
|
107
|
+
await targetDB.info();
|
|
108
|
+
console.log(chalk.green('✅ Connected to target database'));
|
|
109
|
+
} catch (error: unknown) {
|
|
110
|
+
let errorMessage = 'Unknown error';
|
|
111
|
+
if (error instanceof Error) {
|
|
112
|
+
errorMessage = error.message;
|
|
113
|
+
} else if (typeof error === 'string') {
|
|
114
|
+
errorMessage = error;
|
|
115
|
+
} else if (error && typeof error === 'object' && 'message' in error) {
|
|
116
|
+
errorMessage = String((error as { message: unknown }).message);
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`Failed to connect to target database: ${errorMessage}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Setup ENV variables for CourseLookup (temporarily override for this operation)
|
|
122
|
+
try {
|
|
123
|
+
// Parse server URL to extract protocol and host
|
|
124
|
+
const serverUrl = new URL(options.server);
|
|
125
|
+
ENV.COUCHDB_SERVER_PROTOCOL = serverUrl.protocol.slice(0, -1); // Remove trailing ':'
|
|
126
|
+
ENV.COUCHDB_SERVER_URL = serverUrl.host;
|
|
127
|
+
if (options.username) ENV.COUCHDB_USERNAME = options.username;
|
|
128
|
+
if (options.password) ENV.COUCHDB_PASSWORD = options.password;
|
|
129
|
+
} catch {
|
|
130
|
+
throw new Error(`Invalid server URL: ${options.server}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Configure migrator
|
|
134
|
+
const migratorOptions = {
|
|
135
|
+
chunkBatchSize: parseInt(options.chunkSize),
|
|
136
|
+
validateRoundTrip: options.validate,
|
|
137
|
+
cleanupOnFailure: options.cleanupOnError,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
console.log(chalk.gray(`📦 Batch size: ${migratorOptions.chunkBatchSize} documents`));
|
|
141
|
+
console.log(chalk.gray(`🔍 Validation enabled: ${migratorOptions.validateRoundTrip}`));
|
|
142
|
+
|
|
143
|
+
// Setup progress reporting
|
|
144
|
+
const migrator = new StaticToCouchDBMigrator(migratorOptions, fileSystemAdapter);
|
|
145
|
+
migrator.setProgressCallback((progress: { phase: string; message: string; current: number; total: number }) => {
|
|
146
|
+
const percentage = progress.total > 0 ? Math.round((progress.current / progress.total) * 100) : 0;
|
|
147
|
+
console.log(chalk.cyan(`🔄 ${progress.phase}: ${progress.message} (${progress.current}/${progress.total} - ${percentage}%)`));
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Perform migration
|
|
151
|
+
console.log(chalk.cyan('🚀 Starting migration...'));
|
|
152
|
+
|
|
153
|
+
const result = await migrator.migrateCourse(coursePath, targetDB);
|
|
154
|
+
|
|
155
|
+
if (!result.success) {
|
|
156
|
+
console.log(chalk.red('\n❌ Migration failed:'));
|
|
157
|
+
result.errors.forEach((error: string) => {
|
|
158
|
+
console.log(chalk.red(` • ${error}`));
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (result.warnings.length > 0) {
|
|
162
|
+
console.log(chalk.yellow('\n⚠️ Warnings:'));
|
|
163
|
+
result.warnings.forEach((warning: string) => {
|
|
164
|
+
console.log(chalk.yellow(` • ${warning}`));
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const courseName = options.as || validation.courseName || 'Unknown Course';
|
|
172
|
+
|
|
173
|
+
// Update CourseConfig with new name if provided
|
|
174
|
+
if (options.as) {
|
|
175
|
+
try {
|
|
176
|
+
console.log(chalk.cyan(`🔄 Updating course name to "${courseName}"...`));
|
|
177
|
+
const courseConfig = await targetDB.get('CourseConfig') as { _id: string; _rev: string; name: string };
|
|
178
|
+
courseConfig.name = courseName;
|
|
179
|
+
await targetDB.put(courseConfig);
|
|
180
|
+
console.log(chalk.green('✅ Course name updated.'));
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.log(chalk.yellow('⚠️ Warning: Failed to update course name in CourseConfig.'));
|
|
183
|
+
console.log(chalk.yellow(` ${error instanceof Error ? error.message : String(error)}`));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Success! Register course in lookup and display results
|
|
188
|
+
console.log(chalk.green('\n✅ Successfully unpacked course!'));
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
console.log(chalk.cyan('🔄 Registering course in course lookup...'));
|
|
192
|
+
await CourseLookup.addWithId(studioCourseId, courseName);
|
|
193
|
+
console.log(chalk.green('✅ Course registered in course lookup'));
|
|
194
|
+
} catch (lookupError) {
|
|
195
|
+
console.log(chalk.yellow('⚠️ Warning: Failed to register course in lookup database'));
|
|
196
|
+
console.log(chalk.yellow(` ${lookupError instanceof Error ? lookupError.message : String(lookupError)}`));
|
|
197
|
+
console.log(chalk.yellow(' The unpacked course data is still available, but may not appear in the course browser.'));
|
|
198
|
+
}
|
|
199
|
+
console.log('');
|
|
200
|
+
console.log(chalk.white(`📊 Course: ${courseName}`));
|
|
201
|
+
console.log(chalk.white(`📄 Documents: ${result.documentsRestored}`));
|
|
202
|
+
console.log(chalk.white(`🗃️ Design Docs: ${result.designDocsRestored}`));
|
|
203
|
+
console.log(chalk.white(`📎 Attachments: ${result.attachmentsRestored}`));
|
|
204
|
+
console.log(chalk.white(`⏱️ Migration Time: ${(result.migrationTime / 1000).toFixed(1)}s`));
|
|
205
|
+
console.log(chalk.white(`📡 Database: ${targetDbName}`));
|
|
206
|
+
|
|
207
|
+
if (result.warnings.length > 0) {
|
|
208
|
+
console.log(chalk.yellow('\n⚠️ Migration warnings:'));
|
|
209
|
+
result.warnings.forEach((warning: string) => {
|
|
210
|
+
console.log(chalk.yellow(` • ${warning}`));
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Display next steps
|
|
215
|
+
console.log('');
|
|
216
|
+
console.log(chalk.cyan('📝 Next steps:'));
|
|
217
|
+
console.log(chalk.gray(' • Test the migrated course data in your application'));
|
|
218
|
+
console.log(chalk.gray(' • Verify document counts and content manually if needed'));
|
|
219
|
+
console.log(chalk.gray(` • Use database: ${targetDbName}`));
|
|
220
|
+
|
|
221
|
+
if (!options.validate) {
|
|
222
|
+
console.log(chalk.gray(' • Consider running with --validate flag for comprehensive verification'));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
} catch (error: unknown) {
|
|
226
|
+
console.error(chalk.red('\n❌ Unpacking failed:'));
|
|
227
|
+
let errorMessage = 'Unknown error';
|
|
228
|
+
if (error instanceof Error) {
|
|
229
|
+
errorMessage = error.message;
|
|
230
|
+
|
|
231
|
+
// Show stack trace in development/debug mode
|
|
232
|
+
if (process.env.DEBUG || process.env.NODE_ENV === 'development') {
|
|
233
|
+
console.error(chalk.gray('\nStack trace:'));
|
|
234
|
+
console.error(chalk.gray(error.stack || 'No stack trace available'));
|
|
235
|
+
}
|
|
236
|
+
} else if (typeof error === 'string') {
|
|
237
|
+
errorMessage = error;
|
|
238
|
+
} else if (error && typeof error === 'object' && 'message' in error) {
|
|
239
|
+
errorMessage = String((error as { message: unknown }).message);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.error(chalk.red(errorMessage));
|
|
243
|
+
console.error('');
|
|
244
|
+
console.error(chalk.yellow('💡 Troubleshooting tips:'));
|
|
245
|
+
console.error(chalk.gray(' • Verify the static course directory path is correct'));
|
|
246
|
+
console.error(chalk.gray(' • Ensure CouchDB is running and accessible'));
|
|
247
|
+
console.error(chalk.gray(' • Check that manifest.json and chunks/ directory exist'));
|
|
248
|
+
console.error(chalk.gray(' • Verify database permissions if using authentication'));
|
|
249
|
+
console.error(chalk.gray(' • Use --validate flag for detailed error information'));
|
|
250
|
+
|
|
251
|
+
process.exit(1);
|
|
252
|
+
} finally {
|
|
253
|
+
// Restore original ENV values
|
|
254
|
+
ENV.COUCHDB_SERVER_PROTOCOL = originalEnv.COUCHDB_SERVER_PROTOCOL;
|
|
255
|
+
ENV.COUCHDB_SERVER_URL = originalEnv.COUCHDB_SERVER_URL;
|
|
256
|
+
ENV.COUCHDB_USERNAME = originalEnv.COUCHDB_USERNAME;
|
|
257
|
+
ENV.COUCHDB_PASSWORD = originalEnv.COUCHDB_PASSWORD;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// packages/cli/src/utils/NodeFileSystemAdapter.ts
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { FileSystemAdapter, FileStats, FileSystemError } from '@vue-skuilder/db';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Node.js implementation of FileSystemAdapter using native fs and path modules.
|
|
9
|
+
* This works cleanly in CLI environments without bundling issues.
|
|
10
|
+
*/
|
|
11
|
+
export class NodeFileSystemAdapter implements FileSystemAdapter {
|
|
12
|
+
async readFile(filePath: string): Promise<string> {
|
|
13
|
+
try {
|
|
14
|
+
return await fs.promises.readFile(filePath, 'utf8');
|
|
15
|
+
} catch (error) {
|
|
16
|
+
throw new FileSystemError(
|
|
17
|
+
`Failed to read file: ${error instanceof Error ? error.message : String(error)}`,
|
|
18
|
+
'readFile',
|
|
19
|
+
filePath,
|
|
20
|
+
error instanceof Error ? error : undefined
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async readBinary(filePath: string): Promise<Buffer> {
|
|
26
|
+
try {
|
|
27
|
+
return await fs.promises.readFile(filePath);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
throw new FileSystemError(
|
|
30
|
+
`Failed to read binary file: ${error instanceof Error ? error.message : String(error)}`,
|
|
31
|
+
'readBinary',
|
|
32
|
+
filePath,
|
|
33
|
+
error instanceof Error ? error : undefined
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async exists(filePath: string): Promise<boolean> {
|
|
39
|
+
try {
|
|
40
|
+
await fs.promises.access(filePath);
|
|
41
|
+
return true;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async stat(filePath: string): Promise<FileStats> {
|
|
48
|
+
try {
|
|
49
|
+
const stats = await fs.promises.stat(filePath);
|
|
50
|
+
return {
|
|
51
|
+
isDirectory: () => stats.isDirectory(),
|
|
52
|
+
isFile: () => stats.isFile(),
|
|
53
|
+
size: stats.size
|
|
54
|
+
};
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new FileSystemError(
|
|
57
|
+
`Failed to stat file: ${error instanceof Error ? error.message : String(error)}`,
|
|
58
|
+
'stat',
|
|
59
|
+
filePath,
|
|
60
|
+
error instanceof Error ? error : undefined
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
joinPath(...segments: string[]): string {
|
|
66
|
+
return path.join(...segments);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
isAbsolute(filePath: string): boolean {
|
|
70
|
+
return path.isAbsolute(filePath);
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/utils/template.ts
CHANGED
|
@@ -62,7 +62,8 @@ export async function copyDirectory(
|
|
|
62
62
|
export async function transformPackageJson(
|
|
63
63
|
packageJsonPath: string,
|
|
64
64
|
projectName: string,
|
|
65
|
-
cliVersion: string
|
|
65
|
+
cliVersion: string,
|
|
66
|
+
config: ProjectConfig
|
|
66
67
|
): Promise<void> {
|
|
67
68
|
const content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
68
69
|
const packageJson = JSON.parse(content);
|
|
@@ -87,6 +88,20 @@ export async function transformPackageJson(
|
|
|
87
88
|
packageJson.devDependencies['terser'] = '^5.39.0';
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
// Add CLI as devDependency for all projects
|
|
92
|
+
if (!packageJson.devDependencies) {
|
|
93
|
+
packageJson.devDependencies = {};
|
|
94
|
+
}
|
|
95
|
+
packageJson.devDependencies['@vue-skuilder/cli'] = `^${cliVersion}`;
|
|
96
|
+
|
|
97
|
+
// Add studio script for static data layer projects
|
|
98
|
+
if (config.dataLayerType === 'static') {
|
|
99
|
+
if (!packageJson.scripts) {
|
|
100
|
+
packageJson.scripts = {};
|
|
101
|
+
}
|
|
102
|
+
packageJson.scripts['studio'] = 'skuilder studio';
|
|
103
|
+
}
|
|
104
|
+
|
|
90
105
|
// Remove CLI-specific fields that don't belong in generated projects
|
|
91
106
|
delete packageJson.publishConfig;
|
|
92
107
|
|
|
@@ -180,7 +195,11 @@ export async function generateSkuilderConfig(
|
|
|
180
195
|
}
|
|
181
196
|
|
|
182
197
|
// For static data layer with imported courses, use the first course as primary
|
|
183
|
-
if (
|
|
198
|
+
if (
|
|
199
|
+
config.dataLayerType === 'static' &&
|
|
200
|
+
config.importCourseIds &&
|
|
201
|
+
config.importCourseIds.length > 0
|
|
202
|
+
) {
|
|
184
203
|
skuilderConfig.course = config.importCourseIds[0];
|
|
185
204
|
}
|
|
186
205
|
|
|
@@ -359,12 +378,12 @@ Thumbs.db
|
|
|
359
378
|
*/
|
|
360
379
|
export async function generateReadme(readmePath: string, config: ProjectConfig): Promise<void> {
|
|
361
380
|
let dataLayerInfo = '';
|
|
362
|
-
|
|
381
|
+
|
|
363
382
|
if (config.dataLayerType === 'static') {
|
|
364
383
|
dataLayerInfo = 'This project uses a static data layer with JSON files.';
|
|
365
|
-
|
|
384
|
+
|
|
366
385
|
if (config.importCourseIds && config.importCourseIds.length > 0) {
|
|
367
|
-
const courseList = config.importCourseIds.map(id => `- ${id}`).join('\n');
|
|
386
|
+
const courseList = config.importCourseIds.map((id) => `- ${id}`).join('\n');
|
|
368
387
|
dataLayerInfo += `\n\n**Imported Courses:**\n${courseList}\n\nCourse data is stored in \`public/static-courses/\` and loaded automatically.`;
|
|
369
388
|
}
|
|
370
389
|
} else {
|
|
@@ -408,7 +427,7 @@ Course configuration is managed in \`skuilder.config.json\`. You can modify:
|
|
|
408
427
|
|
|
409
428
|
Current theme: **${config.theme.name}** (${config.theme.defaultMode} mode)
|
|
410
429
|
- Primary: ${config.theme.light.colors.primary}
|
|
411
|
-
- Secondary: ${config.theme.light.colors.secondary}
|
|
430
|
+
- Secondary: ${config.theme.light.colors.secondary}
|
|
412
431
|
- Accent: ${config.theme.light.colors.accent}
|
|
413
432
|
|
|
414
433
|
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.
|
|
@@ -479,7 +498,7 @@ export async function processTemplate(
|
|
|
479
498
|
|
|
480
499
|
console.log(chalk.blue('⚙️ Configuring package.json...'));
|
|
481
500
|
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
482
|
-
await transformPackageJson(packageJsonPath, config.projectName, cliVersion);
|
|
501
|
+
await transformPackageJson(packageJsonPath, config.projectName, cliVersion, config);
|
|
483
502
|
|
|
484
503
|
console.log(chalk.blue('🔧 Creating vite.config.ts...'));
|
|
485
504
|
const viteConfigPath = path.join(projectPath, 'vite.config.ts');
|