create-marp-presentation 1.1.0 → 1.2.1
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 +45 -37
- package/cli/commands/add-themes-cli.js +85 -0
- package/cli/commands/create-project.js +199 -0
- package/cli/utils/file-utils.js +106 -0
- package/cli/utils/prompt-utils.js +89 -0
- package/docs/plans/2025-02-19-marp-template-design.md +207 -0
- package/docs/plans/2025-02-19-marp-template-implementation.md +848 -0
- package/docs/plans/2026-02-20-example-slides-design.md +179 -0
- package/docs/plans/2026-02-20-example-slides-implementation.md +811 -0
- package/docs/plans/2026-02-23-theme-management-design.md +836 -0
- package/docs/plans/2026-02-23-theme-management-implementation.md +3585 -0
- package/docs/plans/2026-02-26-theme-addition-refactoring-design.md +172 -0
- package/docs/plans/2026-02-26-theme-addition-refactoring.md +456 -0
- package/docs/plans/2026-02-27-theme-add-fix-design.md +136 -0
- package/docs/plans/2026-02-27-theme-add-fix.md +353 -0
- package/docs/plans/TODO.md +5 -0
- package/docs/reqs/themes-requirements.md +49 -0
- package/docs/theme-management.md +261 -0
- package/index.js +111 -164
- package/lib/add-themes-command.js +381 -0
- package/lib/errors.js +62 -0
- package/lib/frontmatter.js +71 -0
- package/lib/prompts.js +222 -0
- package/lib/theme-manager.js +238 -0
- package/lib/theme-resolver.js +227 -0
- package/lib/vscode-integration.js +198 -0
- package/package.json +15 -5
- package/template/README.md +28 -17
- package/template/package.json +13 -1
- package/template/scripts/theme-cli.js +248 -0
- package/themes/beam/beam.css +141 -0
- package/themes/default-clean/default-clean.css +57 -0
- package/themes/gaia-dark/gaia-dark.css +27 -0
- package/themes/marpx/marpx.css +1735 -0
- package/themes/marpx/socrates.css +105 -0
- package/themes/uncover-minimal/uncover-minimal.css +22 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Theme Management Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to use the theme management system in your Marp presentation project.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The theme management system provides a simple way to:
|
|
8
|
+
- Add pre-built themes to your project
|
|
9
|
+
- List available and installed themes
|
|
10
|
+
- Switch between themes
|
|
11
|
+
- Create custom themes
|
|
12
|
+
- Integrate with VSCode for live preview
|
|
13
|
+
|
|
14
|
+
## Available Themes
|
|
15
|
+
|
|
16
|
+
The following themes are available in the template:
|
|
17
|
+
|
|
18
|
+
- **beam** - Clean, modern theme with progress bar
|
|
19
|
+
- **default-clean** - Minimal variation of default theme
|
|
20
|
+
- **gaia-dark** - Dark version of the Gaia theme
|
|
21
|
+
- **marpx** - Extended version of Marp's default theme
|
|
22
|
+
- **socrates** - Educational theme with clear typography
|
|
23
|
+
- **uncover-minimal** - Minimal reveal-style theme
|
|
24
|
+
|
|
25
|
+
### System Themes
|
|
26
|
+
|
|
27
|
+
These built-in Marp themes are always available:
|
|
28
|
+
- **default** - Marp's default theme
|
|
29
|
+
- **gaia** - Clean, professional theme
|
|
30
|
+
- **uncover** - Slide-by-slide reveal theme
|
|
31
|
+
|
|
32
|
+
## Commands
|
|
33
|
+
|
|
34
|
+
### List Themes
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm run theme:list
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This shows:
|
|
41
|
+
- Available themes in the theme library
|
|
42
|
+
- Installed themes in your project's `themes/` directory
|
|
43
|
+
- Dependencies for each theme
|
|
44
|
+
|
|
45
|
+
### Add Themes
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm run theme:add
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
You'll be prompted to:
|
|
52
|
+
1. Select themes from the available list
|
|
53
|
+
2. Resolve any conflicts (if themes with the same name exist)
|
|
54
|
+
3. Confirm the selection
|
|
55
|
+
|
|
56
|
+
The command will:
|
|
57
|
+
- Copy theme files to your `themes/` directory
|
|
58
|
+
- Resolve dependencies automatically
|
|
59
|
+
- Update VSCode settings
|
|
60
|
+
- Skip themes that would conflict (unless using `--force`)
|
|
61
|
+
|
|
62
|
+
**Options:**
|
|
63
|
+
```bash
|
|
64
|
+
npm run theme add --force # Overwrite existing themes
|
|
65
|
+
npm run theme add --no-vscode # Skip VSCode settings update
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Set Active Theme
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm run theme:set <theme-name>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This will:
|
|
75
|
+
- Update the `theme` value in `presentation.md` frontmatter
|
|
76
|
+
- Add the theme to VSCode settings (for custom themes)
|
|
77
|
+
- Warn if the theme is not installed
|
|
78
|
+
|
|
79
|
+
**Examples:**
|
|
80
|
+
```bash
|
|
81
|
+
npm run theme:set beam
|
|
82
|
+
npm run theme:set default
|
|
83
|
+
npm run theme:set gaia
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Create Custom Theme
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm run theme:create <theme-name>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
You'll be prompted to:
|
|
93
|
+
1. Select a parent theme to base your theme on
|
|
94
|
+
2. Choose a directory location (root, subfolder, or new folder)
|
|
95
|
+
|
|
96
|
+
The command will:
|
|
97
|
+
- Create a new CSS file in the appropriate location
|
|
98
|
+
- Add the theme directive and parent import
|
|
99
|
+
- Update VSCode settings if needed
|
|
100
|
+
|
|
101
|
+
**Example:**
|
|
102
|
+
```bash
|
|
103
|
+
npm run theme:create my-brand
|
|
104
|
+
# Select parent: beam
|
|
105
|
+
# Select location: root
|
|
106
|
+
# Creates: themes/my-brand.css
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Theme File Structure
|
|
110
|
+
|
|
111
|
+
Themes are organized in the `themes/` directory:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
my-presentation/
|
|
115
|
+
├── themes/
|
|
116
|
+
│ ├── beam.css
|
|
117
|
+
│ ├── marpx.css
|
|
118
|
+
│ └── custom/
|
|
119
|
+
│ └── my-theme.css
|
|
120
|
+
└── presentation.md
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Theme File Format
|
|
124
|
+
|
|
125
|
+
Each theme CSS file must include:
|
|
126
|
+
1. A theme directive: `/* @theme theme-name */`
|
|
127
|
+
2. Optional parent imports: `@import "parent-theme";`
|
|
128
|
+
|
|
129
|
+
**Example:**
|
|
130
|
+
```css
|
|
131
|
+
/* @theme my-custom */
|
|
132
|
+
@import "beam";
|
|
133
|
+
|
|
134
|
+
:root {
|
|
135
|
+
--color-primary: #3498db;
|
|
136
|
+
--color-secondary: #2ecc71;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
section {
|
|
140
|
+
background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## VSCode Integration
|
|
145
|
+
|
|
146
|
+
The theme management system automatically updates VSCode settings for live preview support.
|
|
147
|
+
|
|
148
|
+
### Settings Location
|
|
149
|
+
|
|
150
|
+
`.vscode/settings.json`:
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"markdown.marp.themes": [
|
|
154
|
+
"themes/beam/beam.css",
|
|
155
|
+
"themes/marpx/marpx.css"
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### How It Works
|
|
161
|
+
|
|
162
|
+
- When you add themes, they're added to the settings
|
|
163
|
+
- When you set an active theme, it's added to the settings
|
|
164
|
+
- System themes (default, gaia, uncover) are not added (built-in)
|
|
165
|
+
- Use `--no-vscode` to skip this behavior
|
|
166
|
+
|
|
167
|
+
## Manual Theme Management
|
|
168
|
+
|
|
169
|
+
You can also manage themes manually:
|
|
170
|
+
|
|
171
|
+
### Add a Theme Manually
|
|
172
|
+
|
|
173
|
+
1. Copy your theme CSS file to the `themes/` directory (e.g., `themes/your-theme/your-theme.css`)
|
|
174
|
+
2. Add the theme directive: `/* @theme your-theme-name */`
|
|
175
|
+
3. Update `.vscode/settings.json` if needed:
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"markdown.marp.themes": ["themes/your-theme/your-theme.css"]
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Switch Theme Manually
|
|
183
|
+
|
|
184
|
+
Edit `presentation.md`:
|
|
185
|
+
```yaml
|
|
186
|
+
---
|
|
187
|
+
marp: true
|
|
188
|
+
theme: your-theme-name
|
|
189
|
+
---
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Troubleshooting
|
|
193
|
+
|
|
194
|
+
### Theme Not Found
|
|
195
|
+
|
|
196
|
+
If you get a "Theme not found" error:
|
|
197
|
+
1. Check if the theme is installed: `npm run theme:list`
|
|
198
|
+
2. Add the theme if missing: `npm run theme:add`
|
|
199
|
+
3. Verify the theme name matches exactly
|
|
200
|
+
|
|
201
|
+
### Live Preview Not Working
|
|
202
|
+
|
|
203
|
+
If VSCode live preview doesn't show your theme:
|
|
204
|
+
1. Check `.vscode/settings.json` exists
|
|
205
|
+
2. Verify the theme path is correct
|
|
206
|
+
3. Reload the VSCode window
|
|
207
|
+
|
|
208
|
+
### Dependencies Not Found
|
|
209
|
+
|
|
210
|
+
Warning about missing dependencies is expected for system themes (default, gaia, uncover). These are built into Marp CLI and don't need to be in your project.
|
|
211
|
+
|
|
212
|
+
## Best Practices
|
|
213
|
+
|
|
214
|
+
1. **Use Descriptive Names**: Name themes clearly (e.g., `brand-primary`, `presentation-dark`)
|
|
215
|
+
2. **Organize Themes**: Use subfolders for many themes
|
|
216
|
+
3. **Document Dependencies**: Include parent theme imports in comments
|
|
217
|
+
4. **Test Themes**: Always test with `npm run dev` after changing themes
|
|
218
|
+
5. **Version Control**: Commit your custom themes to git
|
|
219
|
+
|
|
220
|
+
## Advanced Usage
|
|
221
|
+
|
|
222
|
+
### Creating Theme Variants
|
|
223
|
+
|
|
224
|
+
Create multiple variants based on the same parent:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npm run theme:create brand-light # Parent: beam
|
|
228
|
+
npm run theme:create brand-dark # Parent: beam
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Edit the themes to differentiate them:
|
|
232
|
+
|
|
233
|
+
```css
|
|
234
|
+
/* themes/brand-light.css */
|
|
235
|
+
/* @theme brand-light */
|
|
236
|
+
@import "beam";
|
|
237
|
+
:root { --bg-color: #ffffff; }
|
|
238
|
+
|
|
239
|
+
/* themes/brand-dark.css */
|
|
240
|
+
/* @theme brand-dark */
|
|
241
|
+
@import "beam";
|
|
242
|
+
:root { --bg-color: #1a1a1a; }
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Conditional Themes
|
|
246
|
+
|
|
247
|
+
Use different themes for different presentations:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# For external presentations
|
|
251
|
+
npm run theme:set brand-professional
|
|
252
|
+
|
|
253
|
+
# For internal workshops
|
|
254
|
+
npm run theme:set brand-casual
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## References
|
|
258
|
+
|
|
259
|
+
- [Marp Theme Documentation](https://marp.app/docs/theming)
|
|
260
|
+
- [Marp CLI GitHub](https://github.com/marp-team/marp-cli)
|
|
261
|
+
- [CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)
|
package/index.js
CHANGED
|
@@ -1,184 +1,131 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (!projectName) {
|
|
11
|
-
console.error('Please provide a project name:');
|
|
12
|
-
console.error(' npx create-marp-presentation <project-name>');
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Валидация имени проекта
|
|
17
|
-
const validName = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
|
|
18
|
-
if (!validName.test(projectName)) {
|
|
19
|
-
console.error(`Invalid project name: "${projectName}"`);
|
|
20
|
-
console.error('Project name must be lowercase, contain only letters, numbers, and hyphens.');
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const projectPath = path.join(process.cwd(), projectName);
|
|
25
|
-
|
|
26
|
-
// Запрос на создание примеров слайдов
|
|
27
|
-
async function askCreateExamples() {
|
|
28
|
-
return new Promise((resolve) => {
|
|
29
|
-
// Интерактивный режим — спрашиваем пользователя
|
|
30
|
-
if (process.stdin.isTTY) {
|
|
31
|
-
const rl = readline.createInterface({
|
|
32
|
-
input: process.stdin,
|
|
33
|
-
output: process.stdout
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
rl.question('Create example slides file? (Y/n) ', (answer) => {
|
|
37
|
-
rl.close();
|
|
38
|
-
const normalized = answer.toLowerCase().trim();
|
|
39
|
-
resolve(normalized !== 'n' && normalized !== 'no');
|
|
40
|
-
});
|
|
41
|
-
} else {
|
|
42
|
-
// Неинтерактивный режим — читаем из stdin если есть данные
|
|
43
|
-
let input = '';
|
|
44
|
-
process.stdin.setEncoding('utf8');
|
|
45
|
-
|
|
46
|
-
process.stdin.on('readable', () => {
|
|
47
|
-
let chunk;
|
|
48
|
-
while ((chunk = process.stdin.read()) !== null) {
|
|
49
|
-
input += chunk;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
process.stdin.on('end', () => {
|
|
54
|
-
const normalized = input.toLowerCase().trim();
|
|
55
|
-
// Если ввод пустой, создаём примеры по умолчанию
|
|
56
|
-
if (normalized === '') {
|
|
57
|
-
resolve(true);
|
|
58
|
-
} else {
|
|
59
|
-
resolve(normalized !== 'n' && normalized !== 'no');
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// Если stdin не имеет данных сразу, завершаем
|
|
64
|
-
if (process.stdin.readableLength === 0) {
|
|
65
|
-
// Даем небольшое время на появление данных
|
|
66
|
-
setTimeout(() => {
|
|
67
|
-
if (input === '') {
|
|
68
|
-
process.stdin.destroy();
|
|
69
|
-
resolve(true);
|
|
70
|
-
}
|
|
71
|
-
}, 10);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
3
|
+
/**
|
|
4
|
+
* create-marp-presentation - CLI entry point
|
|
5
|
+
* Supports dual entry points:
|
|
6
|
+
* 1. npx create-marp-presentation <name> [--path <dir>] - Create new project
|
|
7
|
+
* 2. npx create-marp-presentation theme:add <path> [themes...] - Add themes to existing project
|
|
8
|
+
*/
|
|
76
9
|
|
|
77
|
-
|
|
78
|
-
const resolvedPath = path.resolve(projectPath);
|
|
79
|
-
if (!resolvedPath.startsWith(path.resolve(process.cwd()))) {
|
|
80
|
-
console.error('Invalid project path: path traversal detected.');
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
10
|
+
const path = require('path');
|
|
83
11
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
process.exit(1);
|
|
88
|
-
}
|
|
12
|
+
const { createProject, validateProjectName, parsePathArg } = require('./cli/commands/create-project');
|
|
13
|
+
const { addThemesToExistingProject } = require('./cli/commands/add-themes-cli');
|
|
14
|
+
const { validateOutputPath } = require('./cli/utils/file-utils');
|
|
89
15
|
|
|
90
|
-
//
|
|
16
|
+
// Paths
|
|
91
17
|
const templatePath = path.join(__dirname, 'template');
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const examplesDest = path.join(destPath, 'examples.md');
|
|
117
|
-
if (fs.existsSync(examplesSrc)) {
|
|
118
|
-
fs.copyFileSync(examplesSrc, examplesDest);
|
|
18
|
+
const themesLibraryPath = path.join(__dirname, 'themes');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Show usage information
|
|
22
|
+
* @param {boolean} isError - If true, write to stderr and exit with error code
|
|
23
|
+
*/
|
|
24
|
+
function showUsage(isError = false) {
|
|
25
|
+
const output = isError ? console.error : console.log;
|
|
26
|
+
output('Please provide a project name:');
|
|
27
|
+
output(' npx create-marp-presentation <project-name> [--path <output-dir>]');
|
|
28
|
+
output('');
|
|
29
|
+
output('Or use the theme:add command:');
|
|
30
|
+
output(' npx create-marp-presentation theme:add <project-path> [theme-names...]');
|
|
31
|
+
output('');
|
|
32
|
+
output('Examples:');
|
|
33
|
+
output(' npx create-marp-presentation my-project');
|
|
34
|
+
output(' npx create-marp-presentation my-project --path /tmp');
|
|
35
|
+
output(' npx create-marp-presentation my-project --path ~/projects');
|
|
36
|
+
output(' npx create-marp-presentation theme:add ./my-project');
|
|
37
|
+
output(' npx create-marp-presentation theme:add ./my-project beam marpx');
|
|
38
|
+
output('');
|
|
39
|
+
|
|
40
|
+
if (isError) {
|
|
41
|
+
process.exit(1);
|
|
119
42
|
}
|
|
43
|
+
}
|
|
120
44
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Handle theme:add command
|
|
47
|
+
* @param {string[]} args - Command arguments
|
|
48
|
+
*/
|
|
49
|
+
async function handleThemeAdd(args) {
|
|
50
|
+
const targetPath = args[0];
|
|
51
|
+
if (!targetPath) {
|
|
52
|
+
console.error('Usage: npx create-marp-presentation theme:add <project-path> [theme-names...]');
|
|
53
|
+
process.exit(1);
|
|
126
54
|
}
|
|
127
|
-
};
|
|
128
55
|
|
|
129
|
-
|
|
130
|
-
console.log();
|
|
56
|
+
const themeNames = args.slice(1); // Additional arguments are theme names
|
|
131
57
|
|
|
132
|
-
// Основной async flow
|
|
133
|
-
(async () => {
|
|
134
58
|
try {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
// Создаём папку проекта
|
|
139
|
-
fs.mkdirSync(projectPath, { recursive: true });
|
|
140
|
-
|
|
141
|
-
// Рекурсивно копируем template
|
|
142
|
-
copyDir(templatePath, projectPath);
|
|
143
|
-
|
|
144
|
-
console.log('✓ Project created');
|
|
145
|
-
|
|
146
|
-
// Копируем опциональные файлы
|
|
147
|
-
if (createExamples) {
|
|
148
|
-
copyOptionalFiles(projectPath);
|
|
149
|
-
console.log('✓ Example slides added');
|
|
150
|
-
console.log('✓ Demo image added to static/');
|
|
151
|
-
}
|
|
152
|
-
console.log();
|
|
153
|
-
|
|
154
|
-
// Запускаем npm install
|
|
155
|
-
console.log('Installing dependencies...');
|
|
156
|
-
const installResult = spawnSync('npm', ['install'], {
|
|
157
|
-
cwd: projectPath,
|
|
158
|
-
stdio: 'inherit',
|
|
59
|
+
await addThemesToExistingProject(targetPath, {
|
|
60
|
+
themesLibraryPath,
|
|
61
|
+
themeNames: themeNames.length > 0 ? themeNames : null
|
|
159
62
|
});
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(`Error: ${error.message}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
160
68
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Handle project creation command
|
|
71
|
+
* @param {string} projectName - Name of the project
|
|
72
|
+
* @param {string[]} args - Remaining arguments (e.g., --path)
|
|
73
|
+
*/
|
|
74
|
+
async function handleProjectCreation(projectName, args = []) {
|
|
75
|
+
// Parse --path argument if present
|
|
76
|
+
const { pathIndex } = parsePathArg(args);
|
|
77
|
+
let outputPath = process.cwd();
|
|
78
|
+
|
|
79
|
+
if (pathIndex !== null) {
|
|
80
|
+
const pathArg = args[pathIndex + 1];
|
|
81
|
+
const validation = validateOutputPath(pathArg);
|
|
82
|
+
if (!validation.valid) {
|
|
83
|
+
console.error(`Invalid --path: "${pathArg}"`);
|
|
84
|
+
console.error(validation.error);
|
|
165
85
|
process.exit(1);
|
|
166
86
|
}
|
|
87
|
+
outputPath = validation.resolvedPath;
|
|
88
|
+
}
|
|
167
89
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
90
|
+
try {
|
|
91
|
+
await createProject(projectName, {
|
|
92
|
+
outputPath,
|
|
93
|
+
templatePath,
|
|
94
|
+
themesLibraryPath
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (error.message === 'Invalid project name' || error.message === 'Project directory already exists') {
|
|
98
|
+
process.exit(1);
|
|
176
99
|
}
|
|
177
|
-
console.
|
|
178
|
-
console.log();
|
|
179
|
-
|
|
180
|
-
} catch (err) {
|
|
181
|
-
console.error('Error creating project:', err.message);
|
|
100
|
+
console.error('Error creating project:', error.message);
|
|
182
101
|
process.exit(1);
|
|
183
102
|
}
|
|
184
|
-
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Main entry point
|
|
107
|
+
*/
|
|
108
|
+
async function main() {
|
|
109
|
+
const [command, ...args] = process.argv.slice(2);
|
|
110
|
+
|
|
111
|
+
switch (command) {
|
|
112
|
+
case 'theme:add':
|
|
113
|
+
await handleThemeAdd(args);
|
|
114
|
+
break;
|
|
115
|
+
|
|
116
|
+
case undefined:
|
|
117
|
+
showUsage(true); // Exit with error code 1
|
|
118
|
+
break;
|
|
119
|
+
|
|
120
|
+
default:
|
|
121
|
+
// Treat as project name (backward compatible)
|
|
122
|
+
await handleProjectCreation(command, args);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Run
|
|
128
|
+
main().catch(error => {
|
|
129
|
+
console.error('Unexpected error:', error.message);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
});
|