skuilder 0.1.2 → 0.1.4
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/README.md +162 -2
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +35 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +70 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/types.d.ts +35 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +35 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/prompts.d.ts +5 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +185 -0
- package/dist/utils/prompts.js.map +1 -0
- package/dist/utils/template.d.ts +36 -0
- package/dist/utils/template.d.ts.map +1 -0
- package/dist/utils/template.js +369 -0
- package/dist/utils/template.js.map +1 -0
- package/eslint.config.mjs +21 -0
- package/package.json +41 -36
- package/src/cli.ts +42 -0
- package/src/commands/init.ts +83 -0
- package/src/types.ts +72 -0
- package/src/utils/prompts.ts +204 -0
- package/src/utils/template.ts +421 -0
- package/tsconfig.json +12 -21
- package/.npmignore +0 -56
- package/android/app/BUCK +0 -65
- package/android/app/build.gradle +0 -139
- package/android/app/proguard-rules.pro +0 -66
- package/android/app/src/main/AndroidManifest.xml +0 -32
- package/android/app/src/main/java/com/rxphelloworld/MainActivity.java +0 -15
- package/android/app/src/main/java/com/rxphelloworld/MainApplication.java +0 -40
- package/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/android/app/src/main/res/values/strings.xml +0 -3
- package/android/app/src/main/res/values/styles.xml +0 -8
- package/android/build.gradle +0 -24
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
- package/android/gradle.properties +0 -20
- package/android/gradlew +0 -164
- package/android/gradlew.bat +0 -90
- package/android/keystores/BUCK +0 -8
- package/android/keystores/debug.keystore.properties +0 -4
- package/android/settings.gradle +0 -3
- package/img/fingerCounter/1.PNG +0 -0
- package/img/fingerCounter/10.PNG +0 -0
- package/img/fingerCounter/2.PNG +0 -0
- package/img/fingerCounter/3.PNG +0 -0
- package/img/fingerCounter/4.PNG +0 -0
- package/img/fingerCounter/5.PNG +0 -0
- package/img/fingerCounter/6.PNG +0 -0
- package/img/fingerCounter/7.PNG +0 -0
- package/img/fingerCounter/8.PNG +0 -0
- package/img/fingerCounter/9.PNG +0 -0
- package/index.android.js +0 -1
- package/index.html +0 -30
- package/index.ios.js +0 -1
- package/ios/RXPHelloWorld/AppDelegate.h +0 -16
- package/ios/RXPHelloWorld/AppDelegate.m +0 -37
- package/ios/RXPHelloWorld/Base.lproj/LaunchScreen.xib +0 -42
- package/ios/RXPHelloWorld/Images.xcassets/AppIcon.appiconset/Contents.json +0 -38
- package/ios/RXPHelloWorld/Info.plist +0 -56
- package/ios/RXPHelloWorld/main.m +0 -18
- package/ios/RXPHelloWorld.xcodeproj/project.pbxproj +0 -1251
- package/ios/RXPHelloWorld.xcodeproj/xcshareddata/xcschemes/RXPHelloWorld-tvOS.xcscheme +0 -129
- package/ios/RXPHelloWorld.xcodeproj/xcshareddata/xcschemes/RXPHelloWorld.xcscheme +0 -129
- package/ios/RXPHelloWorldTests/Info.plist +0 -24
- package/ios/RXPHelloWorldTests/RXPHelloWorldTests.m +0 -70
- package/src/App.tsx +0 -206
- package/src/appUtilities/Grader.ts +0 -72
- package/src/appUtilities/Keybinder.ts +0 -28
- package/src/appUtilities/Recorder.ts +0 -73
- package/src/cloudantFiles/_users._design._auth.validate_doc_update.js +0 -136
- package/src/components/ProgressChart.tsx +0 -155
- package/src/components/fingerCounter/fingerCounter.tsx +0 -38
- package/src/components/fingerCounter/resources/hands.svg +0 -512
- package/src/components/numpad.tsx +0 -146
- package/src/components/sessionReport.tsx +0 -87
- package/src/index.tsx +0 -8
- package/src/styles/answerStyles.css +0 -22
- package/src/typings/react.d.ts +0 -964
- package/webpack.config.ts +0 -26
- package/yarn.lock +0 -6208
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { CliOptions, ProjectConfig, PREDEFINED_THEMES, ThemeConfig } from '../types.js';
|
|
4
|
+
|
|
5
|
+
export async function gatherProjectConfig(
|
|
6
|
+
projectName: string,
|
|
7
|
+
options: CliOptions
|
|
8
|
+
): Promise<ProjectConfig> {
|
|
9
|
+
console.log(chalk.cyan('\n🚀 Creating a new Skuilder course application\n'));
|
|
10
|
+
|
|
11
|
+
let config: Partial<ProjectConfig> = {
|
|
12
|
+
projectName
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
if (options.interactive) {
|
|
16
|
+
const answers = await inquirer.prompt([
|
|
17
|
+
{
|
|
18
|
+
type: 'input',
|
|
19
|
+
name: 'title',
|
|
20
|
+
message: 'Course title:',
|
|
21
|
+
default: formatProjectName(projectName),
|
|
22
|
+
validate: (input: string) => input.trim().length > 0 || 'Course title is required'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
type: 'list',
|
|
26
|
+
name: 'dataLayerType',
|
|
27
|
+
message: 'Data layer type:',
|
|
28
|
+
choices: [
|
|
29
|
+
{
|
|
30
|
+
name: 'Dynamic (Connect to CouchDB server)',
|
|
31
|
+
value: 'couch'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'Static (Self-contained JSON files)',
|
|
35
|
+
value: 'static'
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
default: options.dataLayer === 'dynamic' ? 'couch' : 'static'
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'input',
|
|
42
|
+
name: 'couchdbUrl',
|
|
43
|
+
message: 'CouchDB server URL:',
|
|
44
|
+
default: 'http://localhost:5984',
|
|
45
|
+
when: (answers) => answers.dataLayerType === 'couch',
|
|
46
|
+
validate: (input: string) => {
|
|
47
|
+
if (!input.trim()) return 'CouchDB URL is required for dynamic data layer';
|
|
48
|
+
try {
|
|
49
|
+
new URL(input);
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
52
|
+
return 'Please enter a valid URL';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'input',
|
|
58
|
+
name: 'courseId',
|
|
59
|
+
message: 'Course ID to import (optional):',
|
|
60
|
+
when: (answers) => answers.dataLayerType === 'couch'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'list',
|
|
64
|
+
name: 'themeName',
|
|
65
|
+
message: 'Select theme:',
|
|
66
|
+
choices: [
|
|
67
|
+
{
|
|
68
|
+
name: 'Default (Material Blue)',
|
|
69
|
+
value: 'default'
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'Medical (Healthcare Green)',
|
|
73
|
+
value: 'medical'
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'Educational (Academic Orange)',
|
|
77
|
+
value: 'educational'
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'Corporate (Professional Gray)',
|
|
81
|
+
value: 'corporate'
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
default: options.theme
|
|
85
|
+
}
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
config = {
|
|
89
|
+
...config,
|
|
90
|
+
title: answers.title,
|
|
91
|
+
dataLayerType: answers.dataLayerType,
|
|
92
|
+
couchdbUrl: answers.couchdbUrl,
|
|
93
|
+
course: answers.courseId,
|
|
94
|
+
theme: PREDEFINED_THEMES[answers.themeName]
|
|
95
|
+
};
|
|
96
|
+
} else {
|
|
97
|
+
// Non-interactive mode: use provided options
|
|
98
|
+
config = {
|
|
99
|
+
projectName,
|
|
100
|
+
title: formatProjectName(projectName),
|
|
101
|
+
dataLayerType: options.dataLayer === 'dynamic' ? 'couch' : 'static',
|
|
102
|
+
couchdbUrl: options.couchdbUrl,
|
|
103
|
+
course: options.courseId,
|
|
104
|
+
theme: PREDEFINED_THEMES[options.theme]
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Validate required fields for non-interactive mode
|
|
108
|
+
if (config.dataLayerType === 'couch' && !config.couchdbUrl) {
|
|
109
|
+
throw new Error('CouchDB URL is required when using dynamic data layer. Use --couchdb-url option.');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return config as ProjectConfig;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function confirmProjectCreation(
|
|
117
|
+
config: ProjectConfig,
|
|
118
|
+
projectPath: string
|
|
119
|
+
): Promise<boolean> {
|
|
120
|
+
console.log(chalk.yellow('\n📋 Project Configuration Summary:'));
|
|
121
|
+
console.log(` Project Name: ${chalk.white(config.projectName)}`);
|
|
122
|
+
console.log(` Course Title: ${chalk.white(config.title)}`);
|
|
123
|
+
console.log(` Data Layer: ${chalk.white(config.dataLayerType)}`);
|
|
124
|
+
|
|
125
|
+
if (config.couchdbUrl) {
|
|
126
|
+
console.log(` CouchDB URL: ${chalk.white(config.couchdbUrl)}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (config.course) {
|
|
130
|
+
console.log(` Course ID: ${chalk.white(config.course)}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
console.log(` Theme: ${chalk.white(config.theme.name)}`);
|
|
134
|
+
console.log(` Directory: ${chalk.white(projectPath)}`);
|
|
135
|
+
|
|
136
|
+
const { confirmed } = await inquirer.prompt([
|
|
137
|
+
{
|
|
138
|
+
type: 'confirm',
|
|
139
|
+
name: 'confirmed',
|
|
140
|
+
message: 'Create project with these settings?',
|
|
141
|
+
default: true
|
|
142
|
+
}
|
|
143
|
+
]);
|
|
144
|
+
|
|
145
|
+
return confirmed;
|
|
146
|
+
}
|
|
147
|
+
|
|
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
|
+
|
|
181
|
+
return {
|
|
182
|
+
name: answers.name,
|
|
183
|
+
colors: {
|
|
184
|
+
primary: answers.primary,
|
|
185
|
+
secondary: answers.secondary,
|
|
186
|
+
accent: answers.accent
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function formatProjectName(projectName: string): string {
|
|
192
|
+
return projectName
|
|
193
|
+
.split(/[-_\s]+/)
|
|
194
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
195
|
+
.join(' ');
|
|
196
|
+
}
|
|
197
|
+
|
|
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
|
+
}
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { promises as fs, existsSync } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { ProjectConfig, SkuilderConfig } from '../types.js';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Find the standalone-ui package in node_modules
|
|
12
|
+
*/
|
|
13
|
+
export async function findStandaloneUiPath(): Promise<string> {
|
|
14
|
+
// Start from CLI package root and work upward
|
|
15
|
+
let currentDir = path.join(__dirname, '..', '..');
|
|
16
|
+
|
|
17
|
+
while (currentDir !== path.dirname(currentDir)) {
|
|
18
|
+
const nodeModulesPath = path.join(currentDir, 'node_modules', '@vue-skuilder', 'standalone-ui');
|
|
19
|
+
if (existsSync(nodeModulesPath)) {
|
|
20
|
+
return nodeModulesPath;
|
|
21
|
+
}
|
|
22
|
+
currentDir = path.dirname(currentDir);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
throw new Error('Could not find @vue-skuilder/standalone-ui package. Please ensure it is installed.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Copy directory recursively, excluding certain files/directories
|
|
30
|
+
*/
|
|
31
|
+
export async function copyDirectory(
|
|
32
|
+
source: string,
|
|
33
|
+
destination: string,
|
|
34
|
+
excludePatterns: string[] = ['node_modules', 'dist', '.git', 'cypress']
|
|
35
|
+
): Promise<void> {
|
|
36
|
+
const entries = await fs.readdir(source, { withFileTypes: true });
|
|
37
|
+
|
|
38
|
+
await fs.mkdir(destination, { recursive: true });
|
|
39
|
+
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
const sourcePath = path.join(source, entry.name);
|
|
42
|
+
const destPath = path.join(destination, entry.name);
|
|
43
|
+
|
|
44
|
+
// Skip excluded patterns
|
|
45
|
+
if (excludePatterns.some(pattern => entry.name.includes(pattern))) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (entry.isDirectory()) {
|
|
50
|
+
await copyDirectory(sourcePath, destPath, excludePatterns);
|
|
51
|
+
} else {
|
|
52
|
+
await fs.copyFile(sourcePath, destPath);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Transform package.json to use published dependencies instead of workspace references
|
|
59
|
+
*/
|
|
60
|
+
export async function transformPackageJson(
|
|
61
|
+
packageJsonPath: string,
|
|
62
|
+
projectName: string,
|
|
63
|
+
cliVersion: string
|
|
64
|
+
): Promise<void> {
|
|
65
|
+
const content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
66
|
+
const packageJson = JSON.parse(content);
|
|
67
|
+
|
|
68
|
+
// Update basic project info
|
|
69
|
+
packageJson.name = projectName;
|
|
70
|
+
packageJson.description = `Skuilder course application: ${projectName}`;
|
|
71
|
+
packageJson.version = '1.0.0';
|
|
72
|
+
|
|
73
|
+
// Transform workspace dependencies to published versions
|
|
74
|
+
if (packageJson.dependencies) {
|
|
75
|
+
for (const [depName, version] of Object.entries(packageJson.dependencies)) {
|
|
76
|
+
if (typeof version === 'string' && version.startsWith('workspace:')) {
|
|
77
|
+
// Replace workspace references with CLI's version
|
|
78
|
+
packageJson.dependencies[depName] = `^${cliVersion}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Add missing terser devDependency for build minification
|
|
84
|
+
if (packageJson.devDependencies && !packageJson.devDependencies['terser']) {
|
|
85
|
+
packageJson.devDependencies['terser'] = '^5.39.0';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Remove CLI-specific fields that don't belong in generated projects
|
|
89
|
+
delete packageJson.publishConfig;
|
|
90
|
+
|
|
91
|
+
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Create a vite.config.ts to work with published packages instead of workspace sources
|
|
96
|
+
*
|
|
97
|
+
* // [ ] 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.
|
|
98
|
+
*/
|
|
99
|
+
export async function createViteConfig(viteConfigPath: string): Promise<void> {
|
|
100
|
+
// Create a clean vite config for standalone projects
|
|
101
|
+
const transformedContent = `// packages/standalone-ui/vite.config.ts
|
|
102
|
+
import { defineConfig } from 'vite';
|
|
103
|
+
import vue from '@vitejs/plugin-vue';
|
|
104
|
+
import { fileURLToPath, URL } from 'node:url';
|
|
105
|
+
|
|
106
|
+
export default defineConfig({
|
|
107
|
+
plugins: [vue()],
|
|
108
|
+
resolve: {
|
|
109
|
+
alias: {
|
|
110
|
+
// Alias for internal src paths
|
|
111
|
+
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
|
112
|
+
|
|
113
|
+
// Add events alias if needed (often required by dependencies)
|
|
114
|
+
events: 'events',
|
|
115
|
+
},
|
|
116
|
+
extensions: ['.js', '.ts', '.json', '.vue'],
|
|
117
|
+
dedupe: [
|
|
118
|
+
// Ensure single instances of core libs and published packages
|
|
119
|
+
'vue',
|
|
120
|
+
'vuetify',
|
|
121
|
+
'pinia',
|
|
122
|
+
'vue-router',
|
|
123
|
+
'@vue-skuilder/db',
|
|
124
|
+
'@vue-skuilder/common',
|
|
125
|
+
'@vue-skuilder/common-ui',
|
|
126
|
+
'@vue-skuilder/courses',
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
// --- Dependencies optimization ---
|
|
130
|
+
optimizeDeps: {
|
|
131
|
+
// Help Vite pre-bundle dependencies from published packages
|
|
132
|
+
include: [
|
|
133
|
+
'@vue-skuilder/common-ui',
|
|
134
|
+
'@vue-skuilder/db',
|
|
135
|
+
'@vue-skuilder/common',
|
|
136
|
+
'@vue-skuilder/courses',
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
server: {
|
|
140
|
+
port: 5173, // Use standard Vite port for standalone projects
|
|
141
|
+
},
|
|
142
|
+
build: {
|
|
143
|
+
sourcemap: true,
|
|
144
|
+
target: 'es2020',
|
|
145
|
+
minify: 'terser',
|
|
146
|
+
terserOptions: {
|
|
147
|
+
keep_classnames: true,
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
// Add define block for process polyfills
|
|
151
|
+
define: {
|
|
152
|
+
global: 'window',
|
|
153
|
+
'process.env': process.env,
|
|
154
|
+
'process.browser': true,
|
|
155
|
+
'process.version': JSON.stringify(process.version),
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
`;
|
|
159
|
+
|
|
160
|
+
await fs.writeFile(viteConfigPath, transformedContent);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Generate skuilder.config.json based on project configuration
|
|
165
|
+
*/
|
|
166
|
+
export async function generateSkuilderConfig(
|
|
167
|
+
configPath: string,
|
|
168
|
+
config: ProjectConfig
|
|
169
|
+
): Promise<void> {
|
|
170
|
+
const skuilderConfig: SkuilderConfig = {
|
|
171
|
+
title: config.title,
|
|
172
|
+
dataLayerType: config.dataLayerType
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
if (config.course) {
|
|
176
|
+
skuilderConfig.course = config.course;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (config.couchdbUrl) {
|
|
180
|
+
skuilderConfig.couchdbUrl = config.couchdbUrl;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (config.theme) {
|
|
184
|
+
skuilderConfig.theme = config.theme;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
await fs.writeFile(configPath, JSON.stringify(skuilderConfig, null, 2));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Generate .gitignore file for the project
|
|
192
|
+
*/
|
|
193
|
+
export async function generateGitignore(gitignorePath: string): Promise<void> {
|
|
194
|
+
const gitignoreContent = `# Dependencies
|
|
195
|
+
node_modules/
|
|
196
|
+
/.pnp
|
|
197
|
+
.pnp.js
|
|
198
|
+
|
|
199
|
+
# Production builds
|
|
200
|
+
/dist
|
|
201
|
+
/build
|
|
202
|
+
|
|
203
|
+
# Local env files
|
|
204
|
+
.env
|
|
205
|
+
.env.local
|
|
206
|
+
.env.development.local
|
|
207
|
+
.env.test.local
|
|
208
|
+
.env.production.local
|
|
209
|
+
|
|
210
|
+
# Log files
|
|
211
|
+
npm-debug.log*
|
|
212
|
+
yarn-debug.log*
|
|
213
|
+
yarn-error.log*
|
|
214
|
+
pnpm-debug.log*
|
|
215
|
+
lerna-debug.log*
|
|
216
|
+
|
|
217
|
+
# Runtime data
|
|
218
|
+
pids
|
|
219
|
+
*.pid
|
|
220
|
+
*.seed
|
|
221
|
+
*.pid.lock
|
|
222
|
+
|
|
223
|
+
# Coverage directory used by tools like istanbul
|
|
224
|
+
coverage/
|
|
225
|
+
*.lcov
|
|
226
|
+
|
|
227
|
+
# nyc test coverage
|
|
228
|
+
.nyc_output
|
|
229
|
+
|
|
230
|
+
# Dependency directories
|
|
231
|
+
jspm_packages/
|
|
232
|
+
|
|
233
|
+
# TypeScript cache
|
|
234
|
+
*.tsbuildinfo
|
|
235
|
+
|
|
236
|
+
# Optional npm cache directory
|
|
237
|
+
.npm
|
|
238
|
+
|
|
239
|
+
# Optional eslint cache
|
|
240
|
+
.eslintcache
|
|
241
|
+
|
|
242
|
+
# Microbundle cache
|
|
243
|
+
.rpt2_cache/
|
|
244
|
+
.rts2_cache_cjs/
|
|
245
|
+
.rts2_cache_es/
|
|
246
|
+
.rts2_cache_umd/
|
|
247
|
+
|
|
248
|
+
# Optional REPL history
|
|
249
|
+
.node_repl_history
|
|
250
|
+
|
|
251
|
+
# Output of 'npm pack'
|
|
252
|
+
*.tgz
|
|
253
|
+
|
|
254
|
+
# Yarn Integrity file
|
|
255
|
+
.yarn-integrity
|
|
256
|
+
|
|
257
|
+
# parcel-bundler cache (https://parceljs.org/)
|
|
258
|
+
.cache
|
|
259
|
+
.parcel-cache
|
|
260
|
+
|
|
261
|
+
# Next.js build output
|
|
262
|
+
.next
|
|
263
|
+
|
|
264
|
+
# Nuxt.js build / generate output
|
|
265
|
+
.nuxt
|
|
266
|
+
dist
|
|
267
|
+
|
|
268
|
+
# Gatsby files
|
|
269
|
+
.cache/
|
|
270
|
+
public
|
|
271
|
+
|
|
272
|
+
# Storybook build outputs
|
|
273
|
+
.out
|
|
274
|
+
.storybook-out
|
|
275
|
+
|
|
276
|
+
# Temporary folders
|
|
277
|
+
tmp/
|
|
278
|
+
temp/
|
|
279
|
+
|
|
280
|
+
# Editor directories and files
|
|
281
|
+
.vscode/
|
|
282
|
+
.idea
|
|
283
|
+
.DS_Store
|
|
284
|
+
*.suo
|
|
285
|
+
*.ntvs*
|
|
286
|
+
*.njsproj
|
|
287
|
+
*.sln
|
|
288
|
+
*.sw?
|
|
289
|
+
|
|
290
|
+
# OS generated files
|
|
291
|
+
Thumbs.db
|
|
292
|
+
|
|
293
|
+
# Cypress
|
|
294
|
+
/cypress/videos/
|
|
295
|
+
/cypress/screenshots/
|
|
296
|
+
|
|
297
|
+
# Local development
|
|
298
|
+
.env.development
|
|
299
|
+
.env.production
|
|
300
|
+
|
|
301
|
+
# Package manager lockfiles (uncomment if you want to ignore them)
|
|
302
|
+
# package-lock.json
|
|
303
|
+
# yarn.lock
|
|
304
|
+
# pnpm-lock.yaml
|
|
305
|
+
|
|
306
|
+
# Skuilder specific
|
|
307
|
+
/src/data/local-*.json
|
|
308
|
+
`;
|
|
309
|
+
|
|
310
|
+
await fs.writeFile(gitignorePath, gitignoreContent);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Generate project README.md
|
|
315
|
+
*/
|
|
316
|
+
export async function generateReadme(
|
|
317
|
+
readmePath: string,
|
|
318
|
+
config: ProjectConfig
|
|
319
|
+
): Promise<void> {
|
|
320
|
+
const dataLayerInfo = 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
|
+
|
|
324
|
+
const readme = `# ${config.title}
|
|
325
|
+
|
|
326
|
+
A Skuilder course application built with Vue 3, Vuetify, and Pinia.
|
|
327
|
+
|
|
328
|
+
## Data Layer
|
|
329
|
+
|
|
330
|
+
${dataLayerInfo}
|
|
331
|
+
|
|
332
|
+
## Development
|
|
333
|
+
|
|
334
|
+
Install dependencies:
|
|
335
|
+
\`\`\`bash
|
|
336
|
+
npm install
|
|
337
|
+
\`\`\`
|
|
338
|
+
|
|
339
|
+
Start the development server:
|
|
340
|
+
\`\`\`bash
|
|
341
|
+
npm run dev
|
|
342
|
+
\`\`\`
|
|
343
|
+
|
|
344
|
+
Build for production:
|
|
345
|
+
\`\`\`bash
|
|
346
|
+
npm run build
|
|
347
|
+
\`\`\`
|
|
348
|
+
|
|
349
|
+
## Configuration
|
|
350
|
+
|
|
351
|
+
Course configuration is managed in \`skuilder.config.json\`. You can modify:
|
|
352
|
+
- Course title
|
|
353
|
+
- Data layer settings
|
|
354
|
+
- Theme customization
|
|
355
|
+
- Database connection details (for dynamic data layer)
|
|
356
|
+
|
|
357
|
+
## Theme
|
|
358
|
+
|
|
359
|
+
Current theme: **${config.theme.name}**
|
|
360
|
+
- Primary: ${config.theme.colors.primary}
|
|
361
|
+
- Secondary: ${config.theme.colors.secondary}
|
|
362
|
+
- Accent: ${config.theme.colors.accent}
|
|
363
|
+
|
|
364
|
+
## Testing
|
|
365
|
+
|
|
366
|
+
Run end-to-end tests:
|
|
367
|
+
\`\`\`bash
|
|
368
|
+
npm run test:e2e
|
|
369
|
+
\`\`\`
|
|
370
|
+
|
|
371
|
+
Run tests in headless mode:
|
|
372
|
+
\`\`\`bash
|
|
373
|
+
npm run test:e2e:headless
|
|
374
|
+
\`\`\`
|
|
375
|
+
|
|
376
|
+
## Learn More
|
|
377
|
+
|
|
378
|
+
Visit the [Skuilder documentation](https://github.com/NiloCK/vue-skuilder) for more information about building course applications.
|
|
379
|
+
`;
|
|
380
|
+
|
|
381
|
+
await fs.writeFile(readmePath, readme);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Copy and transform the standalone-ui template to create a new project
|
|
386
|
+
*/
|
|
387
|
+
export async function processTemplate(
|
|
388
|
+
projectPath: string,
|
|
389
|
+
config: ProjectConfig,
|
|
390
|
+
cliVersion: string
|
|
391
|
+
): Promise<void> {
|
|
392
|
+
console.log(chalk.blue('📦 Locating standalone-ui template...'));
|
|
393
|
+
const templatePath = await findStandaloneUiPath();
|
|
394
|
+
|
|
395
|
+
console.log(chalk.blue('📂 Copying project files...'));
|
|
396
|
+
await copyDirectory(templatePath, projectPath);
|
|
397
|
+
|
|
398
|
+
console.log(chalk.blue('⚙️ Configuring package.json...'));
|
|
399
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
400
|
+
await transformPackageJson(packageJsonPath, config.projectName, cliVersion);
|
|
401
|
+
|
|
402
|
+
console.log(chalk.blue('🔧 Creating vite.config.ts...'));
|
|
403
|
+
const viteConfigPath = path.join(projectPath, 'vite.config.ts');
|
|
404
|
+
if (existsSync(viteConfigPath)) {
|
|
405
|
+
await createViteConfig(viteConfigPath);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
console.log(chalk.blue('🔧 Generating configuration...'));
|
|
409
|
+
const configPath = path.join(projectPath, 'skuilder.config.json');
|
|
410
|
+
await generateSkuilderConfig(configPath, config);
|
|
411
|
+
|
|
412
|
+
console.log(chalk.blue('📝 Creating README...'));
|
|
413
|
+
const readmePath = path.join(projectPath, 'README.md');
|
|
414
|
+
await generateReadme(readmePath, config);
|
|
415
|
+
|
|
416
|
+
console.log(chalk.blue('📄 Generating .gitignore...'));
|
|
417
|
+
const gitignorePath = path.join(projectPath, '.gitignore');
|
|
418
|
+
await generateGitignore(gitignorePath);
|
|
419
|
+
|
|
420
|
+
console.log(chalk.green('✅ Template processing complete!'));
|
|
421
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"node_modules"
|
|
4
|
-
],
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
5
3
|
"compilerOptions": {
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"experimentalDecorators": true,
|
|
13
|
-
"sourceMap": true,
|
|
14
|
-
"noImplicitAny": true,
|
|
15
|
-
"noImplicitReturns": true,
|
|
16
|
-
"outDir": "./dist/",
|
|
17
|
-
"types": [
|
|
18
|
-
"node"
|
|
19
|
-
]
|
|
20
|
-
},
|
|
21
|
-
"typeAcquisition": {
|
|
22
|
-
"enable": true
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"allowImportingTsExtensions": false
|
|
23
10
|
},
|
|
24
11
|
"include": [
|
|
25
|
-
"
|
|
12
|
+
"src/**/*"
|
|
13
|
+
],
|
|
14
|
+
"exclude": [
|
|
15
|
+
"node_modules",
|
|
16
|
+
"dist"
|
|
26
17
|
]
|
|
27
18
|
}
|
package/.npmignore
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# OSX
|
|
2
|
-
#
|
|
3
|
-
.DS_Store
|
|
4
|
-
|
|
5
|
-
# Xcode
|
|
6
|
-
#
|
|
7
|
-
build/
|
|
8
|
-
*.pbxuser
|
|
9
|
-
!default.pbxuser
|
|
10
|
-
*.mode1v3
|
|
11
|
-
!default.mode1v3
|
|
12
|
-
*.mode2v3
|
|
13
|
-
!default.mode2v3
|
|
14
|
-
*.perspectivev3
|
|
15
|
-
!default.perspectivev3
|
|
16
|
-
xcuserdata
|
|
17
|
-
*.xccheckout
|
|
18
|
-
*.moved-aside
|
|
19
|
-
DerivedData
|
|
20
|
-
*.hmap
|
|
21
|
-
*.ipa
|
|
22
|
-
*.xcuserstate
|
|
23
|
-
project.xcworkspace
|
|
24
|
-
|
|
25
|
-
# Android/IntelliJ
|
|
26
|
-
#
|
|
27
|
-
build/
|
|
28
|
-
.idea
|
|
29
|
-
.gradle
|
|
30
|
-
local.properties
|
|
31
|
-
*.iml
|
|
32
|
-
|
|
33
|
-
# node.js
|
|
34
|
-
#
|
|
35
|
-
node_modules/
|
|
36
|
-
npm-debug.log
|
|
37
|
-
yarn-error.log
|
|
38
|
-
|
|
39
|
-
# BUCK
|
|
40
|
-
buck-out/
|
|
41
|
-
\.buckd/
|
|
42
|
-
*.keystore
|
|
43
|
-
|
|
44
|
-
# fastlane
|
|
45
|
-
#
|
|
46
|
-
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
|
47
|
-
# screenshots whenever they are needed.
|
|
48
|
-
# For more information about the recommended setup visit:
|
|
49
|
-
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
|
|
50
|
-
|
|
51
|
-
fastlane/report.xml
|
|
52
|
-
fastlane/Preview.html
|
|
53
|
-
fastlane/screenshots
|
|
54
|
-
|
|
55
|
-
.vscode
|
|
56
|
-
dist/
|