coursecode 0.1.47 → 0.1.48

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/lib/create.js CHANGED
@@ -10,6 +10,44 @@ import { spawn } from 'child_process';
10
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
11
  const PACKAGE_ROOT = path.join(__dirname, '..');
12
12
 
13
+ export function toProjectDirectoryName(name) {
14
+ return String(name || '')
15
+ .trim()
16
+ .normalize('NFKD')
17
+ .replace(/[\u0300-\u036f]/g, '')
18
+ .toLowerCase()
19
+ .replace(/['’]/g, '')
20
+ .replace(/[^a-z0-9]+/g, '_')
21
+ .replace(/^_+|_+$/g, '');
22
+ }
23
+
24
+ function escapeSingleQuotedValue(value) {
25
+ return String(value).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
26
+ }
27
+
28
+ export function stampCourseTitle(configContent, title) {
29
+ const titleValue = escapeSingleQuotedValue(title);
30
+ let nextContent = configContent.replace(
31
+ /^(\s*)title\s*:\s*['"`][^'"`]*['"`](\s*,?)/m,
32
+ `$1title: '${titleValue}'$2`
33
+ );
34
+
35
+ nextContent = nextContent.replace(
36
+ /^(\s*)courseTitle\s*:\s*['"`][^'"`]*['"`](\s*,?)/m,
37
+ `$1courseTitle: '${titleValue}'$2`
38
+ );
39
+
40
+ return nextContent;
41
+ }
42
+
43
+ function writeCourseTitle(projectDir, title) {
44
+ const configPath = path.join(projectDir, 'course', 'course-config.js');
45
+ if (!fs.existsSync(configPath)) return;
46
+
47
+ const current = fs.readFileSync(configPath, 'utf-8');
48
+ fs.writeFileSync(configPath, stampCourseTitle(current, title), 'utf-8');
49
+ }
50
+
13
51
  /**
14
52
  * Copy directory recursively
15
53
  */
@@ -85,15 +123,31 @@ function gitInit(cwd) {
85
123
  }
86
124
 
87
125
  export async function create(name, options = {}) {
88
- const targetDir = path.resolve(process.cwd(), name);
126
+ const displayName = String(name || '').trim();
127
+ const directoryName = toProjectDirectoryName(displayName);
128
+
129
+ if (!displayName) {
130
+ console.error('\n❌ Course name is required.\n');
131
+ process.exit(1);
132
+ }
133
+
134
+ if (!directoryName) {
135
+ console.error('\n❌ Course name must include letters or numbers.\n');
136
+ process.exit(1);
137
+ }
138
+
139
+ const targetDir = path.resolve(process.cwd(), directoryName);
89
140
 
90
141
  // Check if directory already exists
91
142
  if (fs.existsSync(targetDir)) {
92
- console.error(`\n❌ Directory "${name}" already exists.\n`);
143
+ console.error(`\n❌ Directory "${directoryName}" already exists.\n`);
93
144
  process.exit(1);
94
145
  }
95
146
 
96
- console.log(`\n🚀 Creating CourseCode project: ${name}\n`);
147
+ console.log(`\n🚀 Creating CourseCode project: ${displayName}\n`);
148
+ if (directoryName !== displayName) {
149
+ console.log(` Project folder: ${directoryName}`);
150
+ }
97
151
 
98
152
  // Create project directory
99
153
  fs.mkdirSync(targetDir, { recursive: true });
@@ -138,7 +192,7 @@ export async function create(name, options = {}) {
138
192
  // Read and customize package.json
139
193
  const pkgPath = path.join(targetDir, 'package.json');
140
194
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
141
- pkg.name = name;
195
+ pkg.name = directoryName;
142
196
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
143
197
 
144
198
  // Create .coursecoderc.json to track framework version
@@ -161,6 +215,8 @@ export async function create(name, options = {}) {
161
215
  clean({ basePath: targetDir });
162
216
  }
163
217
 
218
+ writeCourseTitle(targetDir, displayName);
219
+
164
220
  // Install dependencies
165
221
  if (options.install !== false) {
166
222
  console.log('\n Installing dependencies...\n');
@@ -184,7 +240,7 @@ export async function create(name, options = {}) {
184
240
  // Print success message
185
241
  if (options.blank) {
186
242
  console.log(`
187
- ✅ CourseCode project "${name}" created (blank starter)!
243
+ ✅ CourseCode project "${displayName}" created (blank starter)!
188
244
 
189
245
  Course files:
190
246
  - course/course-config.js - Course metadata & structure (minimal)
@@ -199,7 +255,7 @@ export async function create(name, options = {}) {
199
255
  `);
200
256
  } else {
201
257
  console.log(`
202
- ✅ CourseCode project "${name}" created successfully!
258
+ ✅ CourseCode project "${displayName}" created successfully!
203
259
 
204
260
  Course files:
205
261
  - course/course-config.js - Course metadata & structure
@@ -228,9 +284,15 @@ export async function create(name, options = {}) {
228
284
 
229
285
  child.on('error', () => {
230
286
  console.warn(' ⚠️ Failed to start dev server. Run manually:');
231
- console.log(` cd ${name} && coursecode dev\n`);
287
+ console.log(` cd ${directoryName} && coursecode dev\n`);
232
288
  });
233
289
  } else {
234
- console.log(`\n To start developing:\n\n cd ${name}\n coursecode dev\n`);
290
+ console.log(`\n To start developing:\n\n cd ${directoryName}\n coursecode dev\n`);
235
291
  }
292
+
293
+ return {
294
+ displayName,
295
+ directoryName,
296
+ targetDir
297
+ };
236
298
  }
package/lib/import.js CHANGED
@@ -179,9 +179,9 @@ export async function importPresentation(source, options = {}) {
179
179
 
180
180
  // Create blank project (no example slides — they'd just be deleted)
181
181
  console.log(' ⏳ Creating course project...\n');
182
- await create(name, { blank: true, install: options.install });
182
+ const createdProject = await create(name, { blank: true, install: options.install });
183
183
 
184
- const targetDir = path.resolve(process.cwd(), name);
184
+ const targetDir = createdProject?.targetDir || path.resolve(process.cwd(), name);
185
185
  const courseDir = path.join(targetDir, 'course');
186
186
 
187
187
  // Import presentation in-place
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coursecode",
3
- "version": "0.1.47",
3
+ "version": "0.1.48",
4
4
  "description": "Multi-format course authoring framework with CLI tools (SCORM 2004, SCORM 1.2, cmi5, LTI 1.3)",
5
5
  "type": "module",
6
6
  "bin": {