create-marp-presentation 1.1.0 → 1.2.0
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/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 +13 -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
package/README.md
CHANGED
|
@@ -1,64 +1,72 @@
|
|
|
1
1
|
# Create Marp Presentation
|
|
2
2
|
|
|
3
|
-
Create
|
|
3
|
+
Create beautiful presentations in Markdown. Zero setup.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Quick Start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npx create-marp-presentation my-presentation
|
|
9
|
+
cd my-presentation
|
|
10
|
+
npm run dev
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
That's it! Edit `presentation.md` and see changes live at `http://localhost:8080`.
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
cd my-presentation
|
|
15
|
-
npm run dev # Live preview
|
|
16
|
-
npm run build:all # Build all formats
|
|
17
|
-
```
|
|
15
|
+
## VSCode Setup
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
For the best editing experience, install [Marp for VSCode](https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode):
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- 📁 Static files support
|
|
26
|
-
- 🔥 Live preview
|
|
19
|
+
1. Open VSCode
|
|
20
|
+
2. Press `Ctrl+Shift+X` (or `Cmd+Shift+X` on Mac)
|
|
21
|
+
3. Search "Marp for VSCode"
|
|
22
|
+
4. Click Install
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
After installing, open any `.md` file and click the **Marp: Preview** button in the top-right corner.
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
## What You Get
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
- **Markdown slides** - Write presentations in plain text
|
|
29
|
+
- **Live preview** - See changes instantly in browser
|
|
30
|
+
- **Export anywhere** - HTML, PDF, PPTX with one command
|
|
31
|
+
- **Themes** - Beautiful built-in themes, add more anytime
|
|
32
|
+
- **Static files** - Images and assets copied automatically
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
npm install
|
|
34
|
+
## Commands
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
| Command | What it does |
|
|
37
|
+
|---------|--------------|
|
|
38
|
+
| `npm run dev` | Live preview at http://localhost:8080 |
|
|
39
|
+
| `npm run build:all` | Export to HTML, PDF, and PPTX |
|
|
40
|
+
| `npm run clean` | Remove output folder |
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
node index.js test-project
|
|
42
|
+
## Theme Management
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
cd test-project
|
|
48
|
-
ls -la
|
|
49
|
-
npm run dev
|
|
50
|
-
```
|
|
44
|
+
Add and switch themes anytime.
|
|
51
45
|
|
|
52
|
-
|
|
46
|
+
### Quick Theme Commands
|
|
53
47
|
|
|
54
48
|
```bash
|
|
55
|
-
#
|
|
56
|
-
|
|
49
|
+
npm run theme:add # Interactive: select themes to add
|
|
50
|
+
npm run theme:add beam # Add specific themes
|
|
51
|
+
npm run theme:list # See what's available
|
|
52
|
+
npm run theme:set marpx # Change active theme
|
|
57
53
|
```
|
|
58
54
|
|
|
59
|
-
|
|
55
|
+
When you create a project, you'll be prompted to add themes. Add more later with `npm run theme:add`.
|
|
56
|
+
|
|
57
|
+
[See full theme documentation →](docs/theme-management.md)
|
|
58
|
+
|
|
59
|
+
## Local Development
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
To test without publishing to npm:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git clone https://github.com/echernyshev/marp-presentation-template.git
|
|
65
|
+
cd marp-presentation-template
|
|
66
|
+
npm install
|
|
67
|
+
npm test
|
|
68
|
+
node index.js test-project
|
|
69
|
+
```
|
|
62
70
|
|
|
63
71
|
## License
|
|
64
72
|
|
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
|
+
});
|