@vue-skuilder/cli 0.1.3

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,215 @@
1
+ import { promises as fs } from 'fs';
2
+ import { existsSync } from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import chalk from 'chalk';
6
+ import { ProjectConfig, SkuilderConfig } from '../types.js';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ /**
12
+ * Find the standalone-ui package in node_modules
13
+ */
14
+ export async function findStandaloneUiPath(): Promise<string> {
15
+ // Start from CLI package root and work upward
16
+ let currentDir = path.join(__dirname, '..', '..');
17
+
18
+ while (currentDir !== path.dirname(currentDir)) {
19
+ const nodeModulesPath = path.join(currentDir, 'node_modules', '@vue-skuilder', 'standalone-ui');
20
+ if (existsSync(nodeModulesPath)) {
21
+ return nodeModulesPath;
22
+ }
23
+ currentDir = path.dirname(currentDir);
24
+ }
25
+
26
+ throw new Error('Could not find @vue-skuilder/standalone-ui package. Please ensure it is installed.');
27
+ }
28
+
29
+ /**
30
+ * Copy directory recursively, excluding certain files/directories
31
+ */
32
+ export async function copyDirectory(
33
+ source: string,
34
+ destination: string,
35
+ excludePatterns: string[] = ['node_modules', 'dist', '.git', 'cypress']
36
+ ): Promise<void> {
37
+ const entries = await fs.readdir(source, { withFileTypes: true });
38
+
39
+ await fs.mkdir(destination, { recursive: true });
40
+
41
+ for (const entry of entries) {
42
+ const sourcePath = path.join(source, entry.name);
43
+ const destPath = path.join(destination, entry.name);
44
+
45
+ // Skip excluded patterns
46
+ if (excludePatterns.some(pattern => entry.name.includes(pattern))) {
47
+ continue;
48
+ }
49
+
50
+ if (entry.isDirectory()) {
51
+ await copyDirectory(sourcePath, destPath, excludePatterns);
52
+ } else {
53
+ await fs.copyFile(sourcePath, destPath);
54
+ }
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Transform package.json to use published dependencies instead of workspace references
60
+ */
61
+ export async function transformPackageJson(
62
+ packageJsonPath: string,
63
+ projectName: string,
64
+ cliVersion: string
65
+ ): Promise<void> {
66
+ const content = await fs.readFile(packageJsonPath, 'utf-8');
67
+ const packageJson = JSON.parse(content);
68
+
69
+ // Update basic project info
70
+ packageJson.name = projectName;
71
+ packageJson.description = `Skuilder course application: ${projectName}`;
72
+ packageJson.version = '1.0.0';
73
+
74
+ // Transform workspace dependencies to published versions
75
+ if (packageJson.dependencies) {
76
+ for (const [depName, version] of Object.entries(packageJson.dependencies)) {
77
+ if (typeof version === 'string' && version.startsWith('workspace:')) {
78
+ // Replace workspace references with CLI's version
79
+ packageJson.dependencies[depName] = `^${cliVersion}`;
80
+ }
81
+ }
82
+ }
83
+
84
+ // Remove CLI-specific fields that don't belong in generated projects
85
+ delete packageJson.publishConfig;
86
+
87
+ await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
88
+ }
89
+
90
+ /**
91
+ * Generate skuilder.config.json based on project configuration
92
+ */
93
+ export async function generateSkuilderConfig(
94
+ configPath: string,
95
+ config: ProjectConfig
96
+ ): Promise<void> {
97
+ const skuilderConfig: SkuilderConfig = {
98
+ title: config.title,
99
+ dataLayerType: config.dataLayerType
100
+ };
101
+
102
+ if (config.course) {
103
+ skuilderConfig.course = config.course;
104
+ }
105
+
106
+ if (config.couchdbUrl) {
107
+ skuilderConfig.couchdbUrl = config.couchdbUrl;
108
+ }
109
+
110
+ if (config.theme) {
111
+ skuilderConfig.theme = config.theme;
112
+ }
113
+
114
+ await fs.writeFile(configPath, JSON.stringify(skuilderConfig, null, 2));
115
+ }
116
+
117
+ /**
118
+ * Generate project README.md
119
+ */
120
+ export async function generateReadme(
121
+ readmePath: string,
122
+ config: ProjectConfig
123
+ ): Promise<void> {
124
+ const dataLayerInfo = config.dataLayerType === 'static'
125
+ ? 'This project uses a static data layer with JSON files.'
126
+ : `This project connects to CouchDB at: ${config.couchdbUrl || '[URL not specified]'}`;
127
+
128
+ const readme = `# ${config.title}
129
+
130
+ A Skuilder course application built with Vue 3, Vuetify, and Pinia.
131
+
132
+ ## Data Layer
133
+
134
+ ${dataLayerInfo}
135
+
136
+ ## Development
137
+
138
+ Install dependencies:
139
+ \`\`\`bash
140
+ npm install
141
+ \`\`\`
142
+
143
+ Start the development server:
144
+ \`\`\`bash
145
+ npm run dev
146
+ \`\`\`
147
+
148
+ Build for production:
149
+ \`\`\`bash
150
+ npm run build
151
+ \`\`\`
152
+
153
+ ## Configuration
154
+
155
+ Course configuration is managed in \`skuilder.config.json\`. You can modify:
156
+ - Course title
157
+ - Data layer settings
158
+ - Theme customization
159
+ - Database connection details (for dynamic data layer)
160
+
161
+ ## Theme
162
+
163
+ Current theme: **${config.theme.name}**
164
+ - Primary: ${config.theme.colors.primary}
165
+ - Secondary: ${config.theme.colors.secondary}
166
+ - Accent: ${config.theme.colors.accent}
167
+
168
+ ## Testing
169
+
170
+ Run end-to-end tests:
171
+ \`\`\`bash
172
+ npm run test:e2e
173
+ \`\`\`
174
+
175
+ Run tests in headless mode:
176
+ \`\`\`bash
177
+ npm run test:e2e:headless
178
+ \`\`\`
179
+
180
+ ## Learn More
181
+
182
+ Visit the [Skuilder documentation](https://github.com/NiloCK/vue-skuilder) for more information about building course applications.
183
+ `;
184
+
185
+ await fs.writeFile(readmePath, readme);
186
+ }
187
+
188
+ /**
189
+ * Copy and transform the standalone-ui template to create a new project
190
+ */
191
+ export async function processTemplate(
192
+ projectPath: string,
193
+ config: ProjectConfig,
194
+ cliVersion: string
195
+ ): Promise<void> {
196
+ console.log(chalk.blue('📦 Locating standalone-ui template...'));
197
+ const templatePath = await findStandaloneUiPath();
198
+
199
+ console.log(chalk.blue('📂 Copying project files...'));
200
+ await copyDirectory(templatePath, projectPath);
201
+
202
+ console.log(chalk.blue('⚙️ Configuring package.json...'));
203
+ const packageJsonPath = path.join(projectPath, 'package.json');
204
+ await transformPackageJson(packageJsonPath, config.projectName, cliVersion);
205
+
206
+ console.log(chalk.blue('🔧 Generating configuration...'));
207
+ const configPath = path.join(projectPath, 'skuilder.config.json');
208
+ await generateSkuilderConfig(configPath, config);
209
+
210
+ console.log(chalk.blue('📝 Creating README...'));
211
+ const readmePath = path.join(projectPath, 'README.md');
212
+ await generateReadme(readmePath, config);
213
+
214
+ console.log(chalk.green('✅ Template processing complete!'));
215
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "resolveJsonModule": true,
9
+ "allowImportingTsExtensions": false
10
+ },
11
+ "include": [
12
+ "src/**/*"
13
+ ],
14
+ "exclude": [
15
+ "node_modules",
16
+ "dist"
17
+ ]
18
+ }