@litodocs/cli 0.6.0 → 1.0.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 +325 -124
- package/package.json +7 -2
- package/src/cli.js +50 -2
- package/src/commands/build.js +26 -17
- package/src/commands/dev.js +17 -12
- package/src/commands/doctor.js +311 -0
- package/src/commands/info.js +192 -0
- package/src/commands/init.js +284 -0
- package/src/commands/preview.js +98 -0
- package/src/commands/validate.js +124 -0
- package/src/core/config.js +62 -18
- package/src/core/framework-runner.js +208 -0
- package/src/core/landing-sync.js +708 -0
- package/src/core/output.js +10 -4
- package/src/core/sync.js +141 -15
- package/src/core/template-registry.js +8 -5
- package/src/core/update-check.js +11 -1
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { resolve, join } from 'path';
|
|
3
|
+
import { intro, outro, text, select, confirm, spinner, log, isCancel, cancel } from '@clack/prompts';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { TEMPLATE_REGISTRY } from '../core/template-registry.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Default docs-config.json template
|
|
9
|
+
*/
|
|
10
|
+
function createDefaultConfig(projectName, framework) {
|
|
11
|
+
return {
|
|
12
|
+
metadata: {
|
|
13
|
+
name: projectName,
|
|
14
|
+
description: `Documentation for ${projectName}`,
|
|
15
|
+
},
|
|
16
|
+
branding: {
|
|
17
|
+
colors: {
|
|
18
|
+
primary: '#10b981',
|
|
19
|
+
accent: '#3b82f6',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
navigation: {
|
|
23
|
+
navbar: {
|
|
24
|
+
links: [
|
|
25
|
+
{ label: 'Docs', href: '/' },
|
|
26
|
+
{ label: 'GitHub', href: 'https://github.com' },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
sidebar: [
|
|
30
|
+
{
|
|
31
|
+
label: 'Getting Started',
|
|
32
|
+
items: [
|
|
33
|
+
{ label: 'Introduction', href: '/introduction' },
|
|
34
|
+
{ label: 'Quick Start', href: '/quickstart' },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
search: {
|
|
40
|
+
enabled: true,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Sample introduction page content
|
|
47
|
+
*/
|
|
48
|
+
function createIntroductionPage(projectName) {
|
|
49
|
+
return `---
|
|
50
|
+
title: Introduction
|
|
51
|
+
description: Welcome to ${projectName}
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
# Welcome to ${projectName}
|
|
55
|
+
|
|
56
|
+
This is your documentation home. Edit this file at \`introduction.mdx\` to get started.
|
|
57
|
+
|
|
58
|
+
## Features
|
|
59
|
+
|
|
60
|
+
<CardGroup cols={2}>
|
|
61
|
+
<Card title="Fast" icon="bolt">
|
|
62
|
+
Built for speed with static site generation
|
|
63
|
+
</Card>
|
|
64
|
+
<Card title="Flexible" icon="puzzle-piece">
|
|
65
|
+
Customize with MDX components
|
|
66
|
+
</Card>
|
|
67
|
+
</CardGroup>
|
|
68
|
+
|
|
69
|
+
## Next Steps
|
|
70
|
+
|
|
71
|
+
- Read the [Quick Start](/quickstart) guide
|
|
72
|
+
- Explore the MDX components available
|
|
73
|
+
- Customize your \`docs-config.json\`
|
|
74
|
+
`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Sample quickstart page content
|
|
79
|
+
*/
|
|
80
|
+
function createQuickstartPage(projectName) {
|
|
81
|
+
return `---
|
|
82
|
+
title: Quick Start
|
|
83
|
+
description: Get up and running with ${projectName}
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
# Quick Start
|
|
87
|
+
|
|
88
|
+
Get your documentation site running in minutes.
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
<Steps>
|
|
93
|
+
<Step title="Install Lito CLI">
|
|
94
|
+
\`\`\`bash
|
|
95
|
+
npm install -g @aspect-ui/lito
|
|
96
|
+
\`\`\`
|
|
97
|
+
</Step>
|
|
98
|
+
|
|
99
|
+
<Step title="Start Development Server">
|
|
100
|
+
\`\`\`bash
|
|
101
|
+
lito dev -i ./docs
|
|
102
|
+
\`\`\`
|
|
103
|
+
</Step>
|
|
104
|
+
|
|
105
|
+
<Step title="Build for Production">
|
|
106
|
+
\`\`\`bash
|
|
107
|
+
lito build -i ./docs -o ./dist
|
|
108
|
+
\`\`\`
|
|
109
|
+
</Step>
|
|
110
|
+
</Steps>
|
|
111
|
+
|
|
112
|
+
## Configuration
|
|
113
|
+
|
|
114
|
+
Edit \`docs-config.json\` to customize your site:
|
|
115
|
+
|
|
116
|
+
\`\`\`json
|
|
117
|
+
{
|
|
118
|
+
"metadata": {
|
|
119
|
+
"name": "${projectName}",
|
|
120
|
+
"description": "Your project description"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
\`\`\`
|
|
124
|
+
|
|
125
|
+
> [!TIP]
|
|
126
|
+
> Run \`lito validate\` to check your configuration for errors.
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Init command - Initialize a new documentation project
|
|
132
|
+
*/
|
|
133
|
+
export async function initCommand(options) {
|
|
134
|
+
try {
|
|
135
|
+
console.clear();
|
|
136
|
+
intro(pc.inverse(pc.cyan(' Lito - Initialize New Project ')));
|
|
137
|
+
|
|
138
|
+
// Determine output directory
|
|
139
|
+
let outputDir = options.output ? resolve(options.output) : null;
|
|
140
|
+
|
|
141
|
+
if (!outputDir) {
|
|
142
|
+
const dirAnswer = await text({
|
|
143
|
+
message: 'Where should we create your docs?',
|
|
144
|
+
placeholder: './docs',
|
|
145
|
+
defaultValue: './docs',
|
|
146
|
+
validate: (value) => {
|
|
147
|
+
if (!value) return 'Please enter a directory path';
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (isCancel(dirAnswer)) {
|
|
152
|
+
cancel('Operation cancelled.');
|
|
153
|
+
process.exit(0);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
outputDir = resolve(dirAnswer);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check if directory exists and has content
|
|
160
|
+
if (existsSync(outputDir)) {
|
|
161
|
+
const files = await import('fs').then(fs => fs.readdirSync(outputDir));
|
|
162
|
+
if (files.length > 0) {
|
|
163
|
+
const overwrite = await confirm({
|
|
164
|
+
message: `Directory ${pc.cyan(outputDir)} is not empty. Continue anyway?`,
|
|
165
|
+
initialValue: false,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (isCancel(overwrite) || !overwrite) {
|
|
169
|
+
cancel('Operation cancelled.');
|
|
170
|
+
process.exit(0);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Project name
|
|
176
|
+
let projectName = options.name;
|
|
177
|
+
if (!projectName) {
|
|
178
|
+
const nameAnswer = await text({
|
|
179
|
+
message: 'What is your project name?',
|
|
180
|
+
placeholder: 'My Docs',
|
|
181
|
+
defaultValue: 'My Docs',
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
if (isCancel(nameAnswer)) {
|
|
185
|
+
cancel('Operation cancelled.');
|
|
186
|
+
process.exit(0);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
projectName = nameAnswer;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Framework/template selection
|
|
193
|
+
const templates = Object.keys(TEMPLATE_REGISTRY);
|
|
194
|
+
let selectedTemplate = options.template;
|
|
195
|
+
|
|
196
|
+
if (!selectedTemplate) {
|
|
197
|
+
const templateAnswer = await select({
|
|
198
|
+
message: 'Which template would you like to use?',
|
|
199
|
+
options: templates.map((t) => ({
|
|
200
|
+
value: t,
|
|
201
|
+
label: t === 'default' ? `${t} (Astro - Recommended)` : t,
|
|
202
|
+
hint: TEMPLATE_REGISTRY[t],
|
|
203
|
+
})),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (isCancel(templateAnswer)) {
|
|
207
|
+
cancel('Operation cancelled.');
|
|
208
|
+
process.exit(0);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
selectedTemplate = templateAnswer;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Create sample content?
|
|
215
|
+
let createSample = options.sample !== false;
|
|
216
|
+
if (!options.sample) {
|
|
217
|
+
const sampleAnswer = await confirm({
|
|
218
|
+
message: 'Create sample documentation pages?',
|
|
219
|
+
initialValue: true,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (isCancel(sampleAnswer)) {
|
|
223
|
+
cancel('Operation cancelled.');
|
|
224
|
+
process.exit(0);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
createSample = sampleAnswer;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Create project
|
|
231
|
+
const s = spinner();
|
|
232
|
+
s.start('Creating project structure...');
|
|
233
|
+
|
|
234
|
+
// Create directories
|
|
235
|
+
mkdirSync(outputDir, { recursive: true });
|
|
236
|
+
mkdirSync(join(outputDir, '_assets'), { recursive: true });
|
|
237
|
+
mkdirSync(join(outputDir, '_images'), { recursive: true });
|
|
238
|
+
|
|
239
|
+
// Create docs-config.json
|
|
240
|
+
const config = createDefaultConfig(projectName, selectedTemplate);
|
|
241
|
+
writeFileSync(
|
|
242
|
+
join(outputDir, 'docs-config.json'),
|
|
243
|
+
JSON.stringify(config, null, 2)
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
// Create sample pages if requested
|
|
247
|
+
if (createSample) {
|
|
248
|
+
writeFileSync(
|
|
249
|
+
join(outputDir, 'introduction.mdx'),
|
|
250
|
+
createIntroductionPage(projectName)
|
|
251
|
+
);
|
|
252
|
+
writeFileSync(
|
|
253
|
+
join(outputDir, 'quickstart.mdx'),
|
|
254
|
+
createQuickstartPage(projectName)
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
s.stop('Project created');
|
|
259
|
+
|
|
260
|
+
// Success message
|
|
261
|
+
log.success(pc.green('Project initialized successfully!'));
|
|
262
|
+
log.message('');
|
|
263
|
+
log.message(pc.bold('Next steps:'));
|
|
264
|
+
log.message('');
|
|
265
|
+
log.message(` ${pc.cyan('cd')} ${outputDir}`);
|
|
266
|
+
log.message(` ${pc.cyan('lito dev')} -i .`);
|
|
267
|
+
log.message('');
|
|
268
|
+
log.message(pc.dim(`Template: ${selectedTemplate}`));
|
|
269
|
+
log.message(pc.dim(`Config: ${join(outputDir, 'docs-config.json')}`));
|
|
270
|
+
|
|
271
|
+
outro(pc.green('Happy documenting! 📚'));
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (isCancel(error)) {
|
|
274
|
+
cancel('Operation cancelled.');
|
|
275
|
+
process.exit(0);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
log.error(pc.red(error.message));
|
|
279
|
+
if (error.stack) {
|
|
280
|
+
log.error(pc.gray(error.stack));
|
|
281
|
+
}
|
|
282
|
+
process.exit(1);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { resolve, join } from 'path';
|
|
3
|
+
import { intro, log, spinner, isCancel, cancel } from '@clack/prompts';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { execa } from 'execa';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Preview command - Build and preview production site locally
|
|
9
|
+
*/
|
|
10
|
+
export async function previewCommand(options) {
|
|
11
|
+
try {
|
|
12
|
+
const inputPath = options.input ? resolve(options.input) : null;
|
|
13
|
+
const outputPath = resolve(options.output || './dist');
|
|
14
|
+
const port = options.port || '4321';
|
|
15
|
+
|
|
16
|
+
console.clear();
|
|
17
|
+
intro(pc.inverse(pc.cyan(' Lito - Preview Production Build ')));
|
|
18
|
+
|
|
19
|
+
const s = spinner();
|
|
20
|
+
|
|
21
|
+
// Check if dist exists, if not run build first
|
|
22
|
+
if (!existsSync(outputPath)) {
|
|
23
|
+
if (!inputPath) {
|
|
24
|
+
log.error(`Output directory ${pc.cyan(outputPath)} does not exist.`);
|
|
25
|
+
log.message('');
|
|
26
|
+
log.message('Either:');
|
|
27
|
+
log.message(` 1. Run ${pc.cyan('lito build -i <docs>')} first`);
|
|
28
|
+
log.message(` 2. Use ${pc.cyan('lito preview -i <docs>')} to build and preview`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
log.warn(`Output directory not found. Building first...`);
|
|
33
|
+
log.message('');
|
|
34
|
+
|
|
35
|
+
// Run build command
|
|
36
|
+
const { buildCommand } = await import('./build.js');
|
|
37
|
+
await buildCommand({
|
|
38
|
+
input: inputPath,
|
|
39
|
+
output: outputPath,
|
|
40
|
+
template: options.template || 'default',
|
|
41
|
+
baseUrl: options.baseUrl || '/',
|
|
42
|
+
provider: 'static',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
console.clear();
|
|
46
|
+
intro(pc.inverse(pc.cyan(' Lito - Preview Production Build ')));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check for index.html in output
|
|
50
|
+
const indexPath = join(outputPath, 'index.html');
|
|
51
|
+
if (!existsSync(indexPath)) {
|
|
52
|
+
log.error(`No index.html found in ${pc.cyan(outputPath)}`);
|
|
53
|
+
log.message('This directory may not contain a valid build output.');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
log.success(`Serving ${pc.cyan(outputPath)}`);
|
|
58
|
+
log.message('');
|
|
59
|
+
log.message(` ${pc.bold('Local:')} ${pc.cyan(`http://localhost:${port}`)}`);
|
|
60
|
+
log.message('');
|
|
61
|
+
log.message(pc.dim('Press Ctrl+C to stop'));
|
|
62
|
+
log.message('');
|
|
63
|
+
|
|
64
|
+
// Use npx serve or a simple HTTP server
|
|
65
|
+
try {
|
|
66
|
+
// Try using 'serve' package
|
|
67
|
+
await execa('npx', ['serve', outputPath, '-l', port], {
|
|
68
|
+
stdio: 'inherit',
|
|
69
|
+
cwd: process.cwd(),
|
|
70
|
+
});
|
|
71
|
+
} catch (serveError) {
|
|
72
|
+
// Fallback to Python's http.server if serve isn't available
|
|
73
|
+
try {
|
|
74
|
+
await execa('python3', ['-m', 'http.server', port, '-d', outputPath], {
|
|
75
|
+
stdio: 'inherit',
|
|
76
|
+
cwd: process.cwd(),
|
|
77
|
+
});
|
|
78
|
+
} catch (pythonError) {
|
|
79
|
+
log.error('Could not start preview server.');
|
|
80
|
+
log.message('');
|
|
81
|
+
log.message('Please install a static server:');
|
|
82
|
+
log.message(` ${pc.cyan('npm install -g serve')}`);
|
|
83
|
+
log.message('');
|
|
84
|
+
log.message('Or manually serve the output:');
|
|
85
|
+
log.message(` ${pc.cyan(`npx serve ${outputPath}`)}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (isCancel(error)) {
|
|
91
|
+
cancel('Preview stopped.');
|
|
92
|
+
process.exit(0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
log.error(pc.red(error.message));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { resolve, join } from 'path';
|
|
3
|
+
import { intro, outro, log, spinner } from '@clack/prompts';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { validateConfig, isPortableConfig, getCoreConfigKeys, getExtensionKeys } from '../core/config-validator.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validate command - Validate docs-config.json
|
|
9
|
+
*/
|
|
10
|
+
export async function validateCommand(options) {
|
|
11
|
+
try {
|
|
12
|
+
const inputPath = options.input ? resolve(options.input) : process.cwd();
|
|
13
|
+
const configPath = join(inputPath, 'docs-config.json');
|
|
14
|
+
|
|
15
|
+
// Quick mode for CI - just exit with code
|
|
16
|
+
if (options.quiet) {
|
|
17
|
+
if (!existsSync(configPath)) {
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
22
|
+
const result = validateConfig(config, inputPath, { silent: true });
|
|
23
|
+
process.exit(result.valid ? 0 : 1);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.clear();
|
|
30
|
+
intro(pc.inverse(pc.cyan(' Lito - Validate Configuration ')));
|
|
31
|
+
|
|
32
|
+
const s = spinner();
|
|
33
|
+
|
|
34
|
+
// Check if config file exists
|
|
35
|
+
s.start('Looking for docs-config.json...');
|
|
36
|
+
|
|
37
|
+
if (!existsSync(configPath)) {
|
|
38
|
+
s.stop(pc.red('Configuration file not found'));
|
|
39
|
+
log.error(`No docs-config.json found at ${pc.cyan(inputPath)}`);
|
|
40
|
+
log.message('');
|
|
41
|
+
log.message(`Run ${pc.cyan('lito init')} to create a new project with a config file.`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
s.stop(`Found: ${pc.cyan(configPath)}`);
|
|
46
|
+
|
|
47
|
+
// Parse JSON
|
|
48
|
+
s.start('Parsing configuration...');
|
|
49
|
+
let config;
|
|
50
|
+
try {
|
|
51
|
+
config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
52
|
+
s.stop('Configuration parsed');
|
|
53
|
+
} catch (parseError) {
|
|
54
|
+
s.stop(pc.red('Invalid JSON'));
|
|
55
|
+
log.error('Failed to parse docs-config.json:');
|
|
56
|
+
log.error(pc.red(parseError.message));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Validate
|
|
61
|
+
s.start('Validating configuration...');
|
|
62
|
+
const result = validateConfig(config, inputPath, { silent: true });
|
|
63
|
+
|
|
64
|
+
if (!result.valid) {
|
|
65
|
+
s.stop(pc.red('Validation failed'));
|
|
66
|
+
log.message('');
|
|
67
|
+
log.error(pc.bold('Configuration errors:'));
|
|
68
|
+
for (const error of result.errors) {
|
|
69
|
+
log.error(` ${pc.red('•')} ${pc.yellow(error.path)}: ${error.message}`);
|
|
70
|
+
}
|
|
71
|
+
log.message('');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
s.stop(pc.green('Configuration is valid'));
|
|
76
|
+
|
|
77
|
+
// Show summary
|
|
78
|
+
log.message('');
|
|
79
|
+
log.success(pc.bold('Configuration Summary:'));
|
|
80
|
+
log.message('');
|
|
81
|
+
|
|
82
|
+
// Metadata
|
|
83
|
+
if (config.metadata) {
|
|
84
|
+
log.message(` ${pc.cyan('Name:')} ${config.metadata.name || pc.dim('(not set)')}`);
|
|
85
|
+
if (config.metadata.description) {
|
|
86
|
+
log.message(` ${pc.cyan('Description:')} ${config.metadata.description}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Navigation
|
|
91
|
+
if (config.navigation) {
|
|
92
|
+
const sidebarGroups = config.navigation.sidebar?.length || 0;
|
|
93
|
+
const navbarLinks = config.navigation.navbar?.links?.length || 0;
|
|
94
|
+
log.message(` ${pc.cyan('Sidebar groups:')} ${sidebarGroups}`);
|
|
95
|
+
log.message(` ${pc.cyan('Navbar links:')} ${navbarLinks}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Features
|
|
99
|
+
log.message('');
|
|
100
|
+
log.message(pc.bold(' Features:'));
|
|
101
|
+
log.message(` ${config.search?.enabled ? pc.green('✓') : pc.dim('○')} Search`);
|
|
102
|
+
log.message(` ${config.i18n?.enabled ? pc.green('✓') : pc.dim('○')} i18n`);
|
|
103
|
+
log.message(` ${config.versioning?.enabled ? pc.green('✓') : pc.dim('○')} Versioning`);
|
|
104
|
+
|
|
105
|
+
// Portability check
|
|
106
|
+
log.message('');
|
|
107
|
+
const portable = isPortableConfig(config);
|
|
108
|
+
if (portable) {
|
|
109
|
+
log.message(` ${pc.green('✓')} ${pc.dim('Portable config (works with any template)')}`);
|
|
110
|
+
} else {
|
|
111
|
+
const usedExtensions = getExtensionKeys().filter(key => key in config);
|
|
112
|
+
log.message(` ${pc.yellow('!')} ${pc.dim(`Uses template extensions: ${usedExtensions.join(', ')}`)}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
log.message('');
|
|
116
|
+
outro(pc.green('Validation complete!'));
|
|
117
|
+
} catch (error) {
|
|
118
|
+
log.error(pc.red(error.message));
|
|
119
|
+
if (error.stack && !options.quiet) {
|
|
120
|
+
log.error(pc.gray(error.stack));
|
|
121
|
+
}
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
}
|
package/src/core/config.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import pkg from "fs-extra";
|
|
2
|
-
const { readFile, writeFile, ensureDir } = pkg;
|
|
2
|
+
const { readFile, writeFile, ensureDir, pathExists } = pkg;
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { generateThemeStyles } from "./colors.js";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Generate configuration for the project
|
|
8
|
+
* @param {string} projectDir - The scaffolded project directory
|
|
9
|
+
* @param {object} options - CLI options
|
|
10
|
+
* @param {object|null} frameworkConfig - Framework configuration (optional)
|
|
11
|
+
*/
|
|
12
|
+
export async function generateConfig(projectDir, options, frameworkConfig = null) {
|
|
7
13
|
const {
|
|
8
14
|
baseUrl,
|
|
9
15
|
name,
|
|
@@ -68,27 +74,65 @@ export async function generateConfig(projectDir, options) {
|
|
|
68
74
|
await writeFile(join(stylesDir, "generated-theme.css"), "/* No generated theme styles */", "utf-8");
|
|
69
75
|
}
|
|
70
76
|
|
|
71
|
-
// Update astro.config.mjs with base URL and site URL
|
|
72
|
-
|
|
77
|
+
// Update astro.config.mjs with base URL and site URL (only for Astro)
|
|
78
|
+
const frameworkName = frameworkConfig?.name || 'astro';
|
|
79
|
+
|
|
80
|
+
if (frameworkName === 'astro' && ((baseUrl && baseUrl !== "/") || config.metadata?.url)) {
|
|
73
81
|
const astroConfigPath = join(projectDir, "astro.config.mjs");
|
|
74
|
-
|
|
82
|
+
if (await pathExists(astroConfigPath)) {
|
|
83
|
+
let content = await readFile(astroConfigPath, "utf-8");
|
|
84
|
+
|
|
85
|
+
let injection = "export default defineConfig({\n";
|
|
86
|
+
|
|
87
|
+
if (baseUrl && baseUrl !== "/") {
|
|
88
|
+
injection += ` base: '${baseUrl}',\n`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (config.metadata?.url) {
|
|
92
|
+
injection += ` site: '${config.metadata.url}',\n`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Add base/site option to defineConfig
|
|
96
|
+
content = content.replace(
|
|
97
|
+
"export default defineConfig({",
|
|
98
|
+
injection
|
|
99
|
+
);
|
|
75
100
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (baseUrl && baseUrl !== "/") {
|
|
79
|
-
injection += ` base: '${baseUrl}',\n`;
|
|
101
|
+
await writeFile(astroConfigPath, content, "utf-8");
|
|
80
102
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Update vite.config.js for React/Vue frameworks
|
|
106
|
+
if (['react', 'vue'].includes(frameworkName) && baseUrl && baseUrl !== "/") {
|
|
107
|
+
const viteConfigPath = join(projectDir, "vite.config.js");
|
|
108
|
+
if (await pathExists(viteConfigPath)) {
|
|
109
|
+
let content = await readFile(viteConfigPath, "utf-8");
|
|
110
|
+
|
|
111
|
+
// Add base option to defineConfig
|
|
112
|
+
if (content.includes("defineConfig({")) {
|
|
113
|
+
content = content.replace(
|
|
114
|
+
"defineConfig({",
|
|
115
|
+
`defineConfig({\n base: '${baseUrl}',`
|
|
116
|
+
);
|
|
117
|
+
await writeFile(viteConfigPath, content, "utf-8");
|
|
118
|
+
}
|
|
84
119
|
}
|
|
120
|
+
}
|
|
85
121
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
122
|
+
// Update next.config.js for Next.js
|
|
123
|
+
if (frameworkName === 'next' && baseUrl && baseUrl !== "/") {
|
|
124
|
+
const nextConfigPath = join(projectDir, "next.config.js");
|
|
125
|
+
if (await pathExists(nextConfigPath)) {
|
|
126
|
+
let content = await readFile(nextConfigPath, "utf-8");
|
|
91
127
|
|
|
92
|
-
|
|
128
|
+
// Add basePath to Next.js config
|
|
129
|
+
if (!content.includes("basePath")) {
|
|
130
|
+
content = content.replace(
|
|
131
|
+
"module.exports = {",
|
|
132
|
+
`module.exports = {\n basePath: '${baseUrl}',`
|
|
133
|
+
);
|
|
134
|
+
await writeFile(nextConfigPath, content, "utf-8");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
93
137
|
}
|
|
94
138
|
}
|