@rbleattler/omp-ts-typegen 0.2025.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/.github/copilot-instructions.md +74 -0
- package/.github/workflows/nightly-types.yml +42 -0
- package/.vscode/settings.json +5 -0
- package/CHANGELOG.md +9 -0
- package/cache/schema.json +5330 -0
- package/default.omp.json +60 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/types/omp.d.ts +368 -0
- package/dist/types/omp.d.ts.map +1 -0
- package/dist/types/omp.js +398 -0
- package/dist/types/omp.js.map +1 -0
- package/package.json +43 -0
- package/readme.md +130 -0
- package/schema-explained.md +192 -0
- package/scripts/generate-types.ts +184 -0
- package/scripts/test-types.ts +416 -0
- package/scripts/validator.ts +88 -0
- package/src/index.ts +1 -0
- package/theme-validation-badge.svg +20 -0
- package/theme-validation.md +134 -0
- package/tsconfig.json +44 -0
- package/tsconfig.scripts.json +9 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { validate } from './validator';
|
|
4
|
+
import { exec } from 'child_process';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
|
|
9
|
+
const TYPES_DIR = path.join(process.cwd(), 'src', 'types');
|
|
10
|
+
const REPO_CACHE_DIR = path.join(process.cwd(), 'cache', 'oh-my-posh');
|
|
11
|
+
const THEMES_DIR = path.join(REPO_CACHE_DIR, 'themes');
|
|
12
|
+
const REPO_URL = 'https://github.com/JanDeDobbeleer/oh-my-posh.git';
|
|
13
|
+
const VALIDATION_REPORT = path.join(process.cwd(), 'theme-validation.md');
|
|
14
|
+
const DETAILED_REPORT = path.join(process.cwd(), 'theme-validation-details.md');
|
|
15
|
+
const BADGE_PATH = path.join(process.cwd(), 'theme-validation-badge.svg');
|
|
16
|
+
const TEMP_DIR = path.join(process.cwd(), 'temp');
|
|
17
|
+
|
|
18
|
+
async function ensureRepoCloned(): Promise<void> {
|
|
19
|
+
try {
|
|
20
|
+
// Check if the repo directory exists
|
|
21
|
+
await fs.access(REPO_CACHE_DIR);
|
|
22
|
+
|
|
23
|
+
// If it exists, pull the latest changes
|
|
24
|
+
console.log('Oh My Posh repo exists, pulling latest changes...');
|
|
25
|
+
await execAsync('git pull', { cwd: REPO_CACHE_DIR });
|
|
26
|
+
} catch (error) {
|
|
27
|
+
// If the directory doesn't exist, clone the repo
|
|
28
|
+
console.log('Cloning Oh My Posh repository...');
|
|
29
|
+
await fs.mkdir(path.dirname(REPO_CACHE_DIR), { recursive: true });
|
|
30
|
+
await execAsync(`git clone ${REPO_URL} ${REPO_CACHE_DIR} --depth 1`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function getThemeFiles(): Promise<string[]> {
|
|
35
|
+
try {
|
|
36
|
+
// Get all files in the themes directory
|
|
37
|
+
const files = await fs.readdir(THEMES_DIR);
|
|
38
|
+
|
|
39
|
+
// Filter for JSON files only, excluding schema.json
|
|
40
|
+
const themeFiles = files
|
|
41
|
+
.filter(file => file.endsWith('.json') && file !== 'schema.json')
|
|
42
|
+
.map(file => path.join(THEMES_DIR, file));
|
|
43
|
+
|
|
44
|
+
return themeFiles;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
throw new Error(`Failed to get theme files: ${error}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function validateTheme(themePath: string): Promise<{
|
|
51
|
+
path: string,
|
|
52
|
+
name: string,
|
|
53
|
+
valid: boolean,
|
|
54
|
+
errors?: string[]
|
|
55
|
+
}> {
|
|
56
|
+
try {
|
|
57
|
+
const themeName = path.basename(themePath);
|
|
58
|
+
const content = await fs.readFile(themePath, 'utf8');
|
|
59
|
+
const config = JSON.parse(content);
|
|
60
|
+
|
|
61
|
+
// Pass the theme name to the validator
|
|
62
|
+
const result = await validate(config, themeName);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
path: themePath,
|
|
66
|
+
name: themeName,
|
|
67
|
+
valid: result.valid,
|
|
68
|
+
errors: result.errors
|
|
69
|
+
};
|
|
70
|
+
} catch (error: any) {
|
|
71
|
+
return {
|
|
72
|
+
path: themePath,
|
|
73
|
+
name: path.basename(themePath),
|
|
74
|
+
valid: false,
|
|
75
|
+
errors: [`Error processing theme: ${error.message}`]
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function generateMarkdownReport(results: {
|
|
81
|
+
total: number,
|
|
82
|
+
valid: number,
|
|
83
|
+
invalid: number,
|
|
84
|
+
validThemes: Array<{ name: string, path: string }>,
|
|
85
|
+
invalidThemes: Array<{ name: string, path: string, errors: string[] }>
|
|
86
|
+
}): Promise<void> {
|
|
87
|
+
const { default: chalk } = await import('chalk');
|
|
88
|
+
console.log(chalk.blue('Generating validation report...'));
|
|
89
|
+
|
|
90
|
+
const repoOwner = 'JanDeDobbeleer';
|
|
91
|
+
const repoName = 'oh-my-posh';
|
|
92
|
+
const branch = 'main'; // Assuming 'main' is the default branch
|
|
93
|
+
|
|
94
|
+
const reportHeader = `# Oh My Posh Theme Validation Report
|
|
95
|
+
|
|
96
|
+
This report shows the validation status of all themes in the [Oh My Posh](https://github.com/${repoOwner}/${repoName}) repository against the TypeScript types generated from the schema.
|
|
97
|
+
|
|
98
|
+
- **Total themes tested:** ${results.total}
|
|
99
|
+
- **Valid themes:** ${results.valid} (${Math.round((results.valid / results.total) * 100)}%)
|
|
100
|
+
- **Invalid themes:** ${results.invalid} (${Math.round((results.invalid / results.total) * 100)}%)
|
|
101
|
+
|
|
102
|
+
Last updated: ${new Date().toISOString().split('T')[0]}
|
|
103
|
+
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const tableHeader = `| Theme | Status |
|
|
107
|
+
|-------|--------|
|
|
108
|
+
`;
|
|
109
|
+
|
|
110
|
+
let tableRows = '';
|
|
111
|
+
|
|
112
|
+
// Add valid themes first
|
|
113
|
+
for (const theme of results.validThemes) {
|
|
114
|
+
const themeName = path.basename(theme.name, '.json');
|
|
115
|
+
const themeUrl = `https://github.com/${repoOwner}/${repoName}/blob/${branch}/themes/${theme.name}`;
|
|
116
|
+
tableRows += `| [${themeName}](${themeUrl}) | ✅ Valid |\n`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Then add invalid themes
|
|
120
|
+
for (const theme of results.invalidThemes) {
|
|
121
|
+
const themeName = path.basename(theme.name, '.json');
|
|
122
|
+
const themeUrl = `https://github.com/${repoOwner}/${repoName}/blob/${branch}/themes/${theme.name}`;
|
|
123
|
+
tableRows += `| [${themeName}](${themeUrl}) | ❌ Invalid |\n`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const reportContent = reportHeader + tableHeader + tableRows;
|
|
127
|
+
await fs.writeFile(VALIDATION_REPORT, reportContent);
|
|
128
|
+
console.log(chalk.green(`Validation report generated at ${VALIDATION_REPORT}`));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Generates a detailed markdown report with all validation errors
|
|
133
|
+
*/
|
|
134
|
+
async function generateDetailedReport(results: {
|
|
135
|
+
total: number,
|
|
136
|
+
valid: number,
|
|
137
|
+
invalid: number,
|
|
138
|
+
validThemes: Array<{ name: string, path: string }>,
|
|
139
|
+
invalidThemes: Array<{ name: string, path: string, errors: string[] }>
|
|
140
|
+
}): Promise<void> {
|
|
141
|
+
const { default: chalk } = await import('chalk');
|
|
142
|
+
console.log(chalk.blue('Generating detailed validation report...'));
|
|
143
|
+
|
|
144
|
+
const repoOwner = 'JanDeDobbeleer';
|
|
145
|
+
const repoName = 'oh-my-posh';
|
|
146
|
+
const branch = 'main';
|
|
147
|
+
|
|
148
|
+
const reportHeader = `# Oh My Posh Theme Validation - Detailed Error Report
|
|
149
|
+
|
|
150
|
+
This report contains detailed validation errors for each theme that failed validation.
|
|
151
|
+
These errors can help identify issues in either the theme files or in the TypeScript type definitions.
|
|
152
|
+
|
|
153
|
+
- **Total themes tested:** ${results.total}
|
|
154
|
+
- **Valid themes:** ${results.valid} (${Math.round((results.valid / results.total) * 100)}%)
|
|
155
|
+
- **Invalid themes:** ${results.invalid} (${Math.round((results.invalid / results.total) * 100)}%)
|
|
156
|
+
|
|
157
|
+
Last updated: ${new Date().toISOString().split('T')[0]}
|
|
158
|
+
|
|
159
|
+
## Summary
|
|
160
|
+
|
|
161
|
+
${results.invalid === 0 ?
|
|
162
|
+
'✅ All themes passed validation!' :
|
|
163
|
+
`⚠️ ${results.invalid} themes failed validation with type errors.`}
|
|
164
|
+
|
|
165
|
+
${results.invalid > 0 ? '## Error Details\n\nThe following sections contain detailed error information for each invalid theme.\n' : ''}
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
let detailContent = '';
|
|
169
|
+
|
|
170
|
+
// Add details for invalid themes
|
|
171
|
+
for (const theme of results.invalidThemes) {
|
|
172
|
+
const themeName = path.basename(theme.name, '.json');
|
|
173
|
+
const themeUrl = `https://github.com/${repoOwner}/${repoName}/blob/${branch}/themes/${theme.name}`;
|
|
174
|
+
|
|
175
|
+
detailContent += `### [${themeName}](${themeUrl})\n\n`;
|
|
176
|
+
detailContent += `**Validation Errors:**\n\n`;
|
|
177
|
+
detailContent += theme.errors?.map(error => `- ${error}`).join('\n') + '\n\n';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const reportContent = reportHeader + detailContent;
|
|
181
|
+
await fs.writeFile(DETAILED_REPORT, reportContent);
|
|
182
|
+
console.log(chalk.green(`Detailed validation report generated at ${DETAILED_REPORT}`));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Generates a custom SVG badge showing theme validation status
|
|
187
|
+
*/
|
|
188
|
+
async function generateValidationBadge(results: {
|
|
189
|
+
total: number,
|
|
190
|
+
valid: number,
|
|
191
|
+
invalid: number
|
|
192
|
+
}): Promise<void> {
|
|
193
|
+
const { default: chalk } = await import('chalk');
|
|
194
|
+
console.log(chalk.blue('Generating validation badge SVG...'));
|
|
195
|
+
|
|
196
|
+
const percentage = Math.round((results.valid / results.total) * 100);
|
|
197
|
+
const width = 240;
|
|
198
|
+
const height = 20;
|
|
199
|
+
const labelWidth = 70;
|
|
200
|
+
const valueWidth = width - labelWidth;
|
|
201
|
+
|
|
202
|
+
// Determine color based on percentage
|
|
203
|
+
let color = '#e05d44'; // red
|
|
204
|
+
if (percentage > 80) color = '#4c1'; // bright green
|
|
205
|
+
else if (percentage > 60) color = '#97CA00'; // green
|
|
206
|
+
else if (percentage > 40) color = '#dfb317'; // yellow
|
|
207
|
+
|
|
208
|
+
const text = `${results.valid}/${results.total} themes valid (${percentage}%)`;
|
|
209
|
+
|
|
210
|
+
// Create SVG badge
|
|
211
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
212
|
+
<linearGradient id="b" x2="0" y2="100%">
|
|
213
|
+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
|
214
|
+
<stop offset="1" stop-opacity=".1"/>
|
|
215
|
+
</linearGradient>
|
|
216
|
+
<mask id="a">
|
|
217
|
+
<rect width="${width}" height="${height}" rx="3" fill="#fff"/>
|
|
218
|
+
</mask>
|
|
219
|
+
<g mask="url(#a)">
|
|
220
|
+
<path fill="#555" d="M0 0h${labelWidth}v${height}H0z"/>
|
|
221
|
+
<path fill="${color}" d="M${labelWidth} 0h${valueWidth}v${height}H${labelWidth}z"/>
|
|
222
|
+
<path fill="url(#b)" d="M0 0h${width}v${height}H0z"/>
|
|
223
|
+
</g>
|
|
224
|
+
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
|
225
|
+
<text x="${labelWidth / 2}" y="15" fill="#010101" fill-opacity=".3">Themes</text>
|
|
226
|
+
<text x="${labelWidth / 2}" y="14">Themes</text>
|
|
227
|
+
<text x="${labelWidth + valueWidth / 2}" y="15" fill="#010101" fill-opacity=".3">${text}</text>
|
|
228
|
+
<text x="${labelWidth + valueWidth / 2}" y="14">${text}</text>
|
|
229
|
+
</g>
|
|
230
|
+
</svg>`;
|
|
231
|
+
|
|
232
|
+
await fs.writeFile(BADGE_PATH, svg);
|
|
233
|
+
console.log(chalk.green(`Validation badge generated at ${BADGE_PATH}`));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Updates the README to include the custom validation badge
|
|
238
|
+
*/
|
|
239
|
+
async function updateReadmeBadge(): Promise<void> {
|
|
240
|
+
const { default: chalk } = await import('chalk');
|
|
241
|
+
const readmePath = path.join(process.cwd(), 'readme.md');
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
// Read the README content
|
|
245
|
+
let readmeContent = await fs.readFile(readmePath, 'utf-8');
|
|
246
|
+
|
|
247
|
+
// Create badge markdown
|
|
248
|
+
const badgeMarkdown = ``;
|
|
249
|
+
|
|
250
|
+
// Replace existing badge or add new one after the title
|
|
251
|
+
if (readmeContent.includes('![Theme Validation]')) {
|
|
252
|
+
readmeContent = readmeContent.replace(
|
|
253
|
+
/!\[Theme Validation\]\(.+?\)/,
|
|
254
|
+
badgeMarkdown
|
|
255
|
+
);
|
|
256
|
+
} else {
|
|
257
|
+
// Add after the first heading
|
|
258
|
+
readmeContent = readmeContent.replace(
|
|
259
|
+
/(# .+?\n)/,
|
|
260
|
+
`$1\n${badgeMarkdown}\n`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Write updated README
|
|
265
|
+
await fs.writeFile(readmePath, readmeContent);
|
|
266
|
+
console.log(chalk.green('Updated README with custom validation badge'));
|
|
267
|
+
} catch (error) {
|
|
268
|
+
console.error(chalk.red('Failed to update README badge:'), error);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async function main() {
|
|
273
|
+
// Dynamically import chalk
|
|
274
|
+
const { default: chalk } = await import('chalk');
|
|
275
|
+
|
|
276
|
+
console.log(chalk.blue('Testing generated types against all Oh My Posh themes...'));
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
// Check if the types file exists
|
|
280
|
+
const typesFile = path.join(TYPES_DIR, 'omp.ts');
|
|
281
|
+
await fs.access(typesFile);
|
|
282
|
+
console.log(chalk.green('Types file exists at:'), typesFile);
|
|
283
|
+
|
|
284
|
+
// Check if the OhMyPosh type is exported
|
|
285
|
+
const typesContent = await fs.readFile(typesFile, 'utf-8');
|
|
286
|
+
if (typesContent.includes('export interface OhMyPosh') ||
|
|
287
|
+
typesContent.includes('export type OhMyPosh')) {
|
|
288
|
+
console.log(chalk.green('OhMyPosh type is properly exported!'));
|
|
289
|
+
} else {
|
|
290
|
+
console.log(chalk.red('Warning: OhMyPosh type may not be properly exported!'));
|
|
291
|
+
console.log('Searching for OhMyPosh definition...');
|
|
292
|
+
|
|
293
|
+
// Find the OhMyPosh definition
|
|
294
|
+
const match = typesContent.match(/(?:interface|type)\s+OhMyPosh/);
|
|
295
|
+
if (match) {
|
|
296
|
+
console.log(chalk.yellow(`Found "${match[0]}" but it might not be exported properly`));
|
|
297
|
+
} else {
|
|
298
|
+
console.log(chalk.red('Could not find OhMyPosh type definition!'));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// First test with our bundled default config
|
|
303
|
+
// console.log(chalk.green('Testing with local default.omp.json...'));
|
|
304
|
+
// const defaultResult = await validate(defaultConfig);
|
|
305
|
+
// if (defaultResult.valid) {
|
|
306
|
+
// console.log(chalk.green('✅ Local default config is valid!'));
|
|
307
|
+
// } else {
|
|
308
|
+
// console.error(chalk.red('❌ Local default config validation failed:'));
|
|
309
|
+
// defaultResult.errors?.forEach(err => console.error(chalk.red(err)));
|
|
310
|
+
// // Don't exit early, continue with other themes
|
|
311
|
+
// }
|
|
312
|
+
|
|
313
|
+
// Clone or update the Oh My Posh repository
|
|
314
|
+
console.log(chalk.yellow('Ensuring Oh My Posh repository is up to date...'));
|
|
315
|
+
await ensureRepoCloned();
|
|
316
|
+
|
|
317
|
+
// Get all theme files
|
|
318
|
+
console.log(chalk.yellow('Finding themes in Oh My Posh repository...'));
|
|
319
|
+
const themeFiles = await getThemeFiles();
|
|
320
|
+
console.log(chalk.green(`Found ${themeFiles.length} themes to validate`));
|
|
321
|
+
|
|
322
|
+
// Track validation results
|
|
323
|
+
const results = {
|
|
324
|
+
total: themeFiles.length,
|
|
325
|
+
valid: 0,
|
|
326
|
+
invalid: 0,
|
|
327
|
+
validThemes: [] as Array<{ name: string, path: string }>,
|
|
328
|
+
invalidThemes: [] as Array<{ name: string, path: string, errors: string[] }>
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
// Process each theme
|
|
332
|
+
console.log(chalk.yellow('Validating Oh My Posh themes against generated types...'));
|
|
333
|
+
|
|
334
|
+
// Create a progress indicator
|
|
335
|
+
let completed = 0;
|
|
336
|
+
|
|
337
|
+
// Validate themes in batches to avoid overwhelming the system
|
|
338
|
+
const BATCH_SIZE = 5;
|
|
339
|
+
for (let i = 0; i < themeFiles.length; i += BATCH_SIZE) {
|
|
340
|
+
const batch = themeFiles.slice(i, i + BATCH_SIZE);
|
|
341
|
+
const batchResults = await Promise.all(batch.map(themePath => validateTheme(themePath)));
|
|
342
|
+
|
|
343
|
+
for (const result of batchResults) {
|
|
344
|
+
completed++;
|
|
345
|
+
|
|
346
|
+
if (result.valid) {
|
|
347
|
+
results.valid++;
|
|
348
|
+
results.validThemes.push({
|
|
349
|
+
name: path.basename(result.path),
|
|
350
|
+
path: result.path
|
|
351
|
+
});
|
|
352
|
+
process.stdout.write(chalk.green('.'));
|
|
353
|
+
} else {
|
|
354
|
+
results.invalid++;
|
|
355
|
+
results.invalidThemes.push({
|
|
356
|
+
name: path.basename(result.path),
|
|
357
|
+
path: result.path,
|
|
358
|
+
errors: result.errors || []
|
|
359
|
+
});
|
|
360
|
+
process.stdout.write(chalk.red('✗'));
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Print progress every 10 themes
|
|
364
|
+
if (completed % 10 === 0) {
|
|
365
|
+
process.stdout.write(` ${completed}/${themeFiles.length}\n`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Print final newline
|
|
371
|
+
process.stdout.write('\n');
|
|
372
|
+
|
|
373
|
+
// Generate the markdown report
|
|
374
|
+
await generateMarkdownReport(results);
|
|
375
|
+
|
|
376
|
+
// Generate detailed markdown report
|
|
377
|
+
await generateDetailedReport(results);
|
|
378
|
+
|
|
379
|
+
// Generate custom SVG badge and update README
|
|
380
|
+
await generateValidationBadge(results);
|
|
381
|
+
await updateReadmeBadge();
|
|
382
|
+
|
|
383
|
+
// Report results to console
|
|
384
|
+
console.log(chalk.blue('Theme validation complete:'));
|
|
385
|
+
console.log(chalk.green(`✅ Valid themes: ${results.valid}/${results.total}`));
|
|
386
|
+
|
|
387
|
+
if (results.invalid > 0) {
|
|
388
|
+
console.log(chalk.red(`❌ Invalid themes: ${results.invalid}/${results.total}`));
|
|
389
|
+
console.log(chalk.yellow('See theme-validation.md for detailed report'));
|
|
390
|
+
process.exit(1);
|
|
391
|
+
} else {
|
|
392
|
+
console.log(chalk.green('✅ All themes passed validation!'));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
} catch (error) {
|
|
396
|
+
// Get chalk again in case error happens before first import
|
|
397
|
+
const { default: chalk } = await import('chalk');
|
|
398
|
+
console.error(chalk.red('Error testing types:'), error);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
await fs.rm(TEMP_DIR, { recursive: true }).catch(() => { });
|
|
402
|
+
// Clean up all temporary files at the end
|
|
403
|
+
try {
|
|
404
|
+
console.log(chalk.yellow('Cleaning up temporary files...'));
|
|
405
|
+
await fs.rm(TEMP_DIR, { recursive: true }).catch(() => {});
|
|
406
|
+
console.log(chalk.green('Temporary files cleaned up!'));
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.log(chalk.yellow('Failed to clean up some temporary files.'));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
main().catch(async (error) => {
|
|
413
|
+
const { default: chalk } = await import('chalk');
|
|
414
|
+
console.error(chalk.red('Unhandled error:'), error);
|
|
415
|
+
process.exit(1);
|
|
416
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Note: This is a simplified validator that checks if the JSON is
|
|
3
|
+
* compatible with the TypeScript interfaces. For a full check,
|
|
4
|
+
* we would need to compile the TypeScript and use the runtime types.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { exec } from 'child_process';
|
|
10
|
+
import { promisify } from 'util';
|
|
11
|
+
|
|
12
|
+
const execAsync = promisify(exec);
|
|
13
|
+
|
|
14
|
+
const TEMP_DIR = path.join(process.cwd(), 'temp');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validates a JSON config against the TypeScript interface
|
|
18
|
+
* @param config The configuration object to validate
|
|
19
|
+
* @param themeName Optional theme name to use as part of the test file name
|
|
20
|
+
* @returns Validation result with valid status and any errors
|
|
21
|
+
*/
|
|
22
|
+
export async function validate(config: any, themeName?: string): Promise<{ valid: boolean; errors?: string[] }> {
|
|
23
|
+
const { default: chalk } = await import('chalk');
|
|
24
|
+
|
|
25
|
+
// Create a unique filename based on the theme name or a timestamp
|
|
26
|
+
const safeThemeName = themeName ?
|
|
27
|
+
themeName.replace(/[^\w.-]/g, '_') :
|
|
28
|
+
`config_${Date.now()}`;
|
|
29
|
+
|
|
30
|
+
const testFilePath = path.join(TEMP_DIR, `${safeThemeName}.test.ts`);
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Create temp directory if it doesn't exist
|
|
34
|
+
await fs.mkdir(TEMP_DIR, { recursive: true });
|
|
35
|
+
|
|
36
|
+
// Create a temporary TypeScript file that imports the types and validates the config
|
|
37
|
+
const testContent = `
|
|
38
|
+
// This is a generated test file for ${themeName || 'unknown theme'}
|
|
39
|
+
import { OhMyPosh } from '../src/types/omp';
|
|
40
|
+
|
|
41
|
+
// The config to validate
|
|
42
|
+
const config: OhMyPosh = ${JSON.stringify(config, null, 2)};
|
|
43
|
+
|
|
44
|
+
// If TypeScript compiles this, it means the config is valid
|
|
45
|
+
console.log('Config is valid!');
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
// Save the test file
|
|
49
|
+
await fs.writeFile(testFilePath, testContent);
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// Run tsc with verbose error output and report all errors
|
|
53
|
+
const { stdout, stderr } = await execAsync(`npx tsc --noEmit --pretty ${testFilePath}`);
|
|
54
|
+
|
|
55
|
+
// If we get here and stderr is empty, compilation succeeded
|
|
56
|
+
if (!stderr.trim()) {
|
|
57
|
+
return { valid: true };
|
|
58
|
+
} else {
|
|
59
|
+
return {
|
|
60
|
+
valid: false,
|
|
61
|
+
errors: [
|
|
62
|
+
`TypeScript validation failed for ${themeName || 'config'}:`,
|
|
63
|
+
stderr
|
|
64
|
+
]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
} catch (execError: any) {
|
|
68
|
+
// TypeScript compilation failed - get the full error details
|
|
69
|
+
return {
|
|
70
|
+
valid: false,
|
|
71
|
+
errors: [
|
|
72
|
+
`TypeScript validation failed for ${themeName || 'config'}:`,
|
|
73
|
+
execError.stderr || execError.message || 'Unknown error'
|
|
74
|
+
]
|
|
75
|
+
};
|
|
76
|
+
} finally {
|
|
77
|
+
// Clean up the test file
|
|
78
|
+
try {
|
|
79
|
+
await fs.unlink(testFilePath).catch(() => {});
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// Ignore errors during cleanup
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch (error: any) {
|
|
85
|
+
// Other errors (file system, etc.)
|
|
86
|
+
return { valid: false, errors: [`Error during validation: ${error.message}`] };
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types/omp';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="20" viewBox="0 0 240 20">
|
|
2
|
+
<linearGradient id="b" x2="0" y2="100%">
|
|
3
|
+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
|
4
|
+
<stop offset="1" stop-opacity=".1"/>
|
|
5
|
+
</linearGradient>
|
|
6
|
+
<mask id="a">
|
|
7
|
+
<rect width="240" height="20" rx="3" fill="#fff"/>
|
|
8
|
+
</mask>
|
|
9
|
+
<g mask="url(#a)">
|
|
10
|
+
<path fill="#555" d="M0 0h70v20H0z"/>
|
|
11
|
+
<path fill="#4c1" d="M70 0h170v20H70z"/>
|
|
12
|
+
<path fill="url(#b)" d="M0 0h240v20H0z"/>
|
|
13
|
+
</g>
|
|
14
|
+
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
|
15
|
+
<text x="35" y="15" fill="#010101" fill-opacity=".3">Themes</text>
|
|
16
|
+
<text x="35" y="14">Themes</text>
|
|
17
|
+
<text x="155" y="15" fill="#010101" fill-opacity=".3">122/122 themes valid (100%)</text>
|
|
18
|
+
<text x="155" y="14">122/122 themes valid (100%)</text>
|
|
19
|
+
</g>
|
|
20
|
+
</svg>
|