resuml 1.3.1 → 1.4.2
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 +93 -1
- package/bin/resuml +21 -0
- package/dist/{api.js → api.cjs} +50 -45
- package/dist/api.cjs.map +1 -0
- package/dist/api.d.cts +9 -0
- package/dist/{index.js → index.cjs} +1180 -118
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +88 -0
- package/dist/{loadResume-BFCirLAW.d.ts → themeLoader-C7CBqNiC.d.cts} +14 -1
- package/package.json +53 -47
- package/scripts/build-builder.js +25 -0
- package/scripts/bundle-themes.js +233 -0
- package/scripts/dev-server.js +392 -0
- package/scripts/quick-bundle.cjs +129 -0
- package/dist/api.d.ts +0 -8
- package/dist/api.js.map +0 -1
- package/dist/index.d.ts +0 -55
- package/dist/index.js.map +0 -1
- /package/scripts/{generate-types.js → generate-types.cjs} +0 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { R as ResumeSchema } from './themeLoader-C7CBqNiC.cjs';
|
|
4
|
+
export { l as loadResumeFiles, a as loadTheme, p as processResumeData } from './themeLoader-C7CBqNiC.cjs';
|
|
5
|
+
|
|
6
|
+
interface ThemeConfig {
|
|
7
|
+
sections?: {
|
|
8
|
+
order?: string[];
|
|
9
|
+
exclude?: string[];
|
|
10
|
+
};
|
|
11
|
+
layout?: {
|
|
12
|
+
style?: string;
|
|
13
|
+
};
|
|
14
|
+
styling?: Record<string, string | number>;
|
|
15
|
+
labels?: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Render a resume using the specified theme
|
|
19
|
+
* @param themeName Name of the theme to use
|
|
20
|
+
* @param resumeData Resume data to render
|
|
21
|
+
* @param themeConfig Theme configuration
|
|
22
|
+
* @param inlineCss Optional CSS to include in the HTML
|
|
23
|
+
* @param language Language code for localization
|
|
24
|
+
* @returns Object containing the rendered HTML
|
|
25
|
+
*/
|
|
26
|
+
declare function renderTheme(themeName: string, resumeData: ResumeSchema, themeConfig?: ThemeConfig, inlineCss?: string, language?: string): Promise<{
|
|
27
|
+
htmlOutput: string;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Injects CSS into HTML content
|
|
31
|
+
* @param html HTML content
|
|
32
|
+
* @param css CSS to inject
|
|
33
|
+
* @returns HTML with injected CSS
|
|
34
|
+
*/
|
|
35
|
+
declare function injectCss(html: string, css?: string): string;
|
|
36
|
+
|
|
37
|
+
declare const themeRender_injectCss: typeof injectCss;
|
|
38
|
+
declare const themeRender_renderTheme: typeof renderTheme;
|
|
39
|
+
declare namespace themeRender {
|
|
40
|
+
export { themeRender_injectCss as injectCss, themeRender_renderTheme as renderTheme };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type AtsCheckCategory = 'contact' | 'content' | 'structure' | 'keywords';
|
|
44
|
+
type AtsCheckWeight = 'high' | 'medium' | 'low';
|
|
45
|
+
type AtsRating = 'excellent' | 'good' | 'needs-work' | 'poor';
|
|
46
|
+
interface AtsCheck {
|
|
47
|
+
id: string;
|
|
48
|
+
category: AtsCheckCategory;
|
|
49
|
+
weight: AtsCheckWeight;
|
|
50
|
+
passed: boolean;
|
|
51
|
+
score: number;
|
|
52
|
+
message: string;
|
|
53
|
+
suggestion?: string;
|
|
54
|
+
}
|
|
55
|
+
interface AtsKeywordMatch {
|
|
56
|
+
matched: string[];
|
|
57
|
+
missing: string[];
|
|
58
|
+
matchPercentage: number;
|
|
59
|
+
}
|
|
60
|
+
interface AtsResult {
|
|
61
|
+
score: number;
|
|
62
|
+
rating: AtsRating;
|
|
63
|
+
checks: AtsCheck[];
|
|
64
|
+
keywords?: AtsKeywordMatch;
|
|
65
|
+
summary: string;
|
|
66
|
+
}
|
|
67
|
+
interface AtsOptions {
|
|
68
|
+
language?: string;
|
|
69
|
+
jobDescription?: string;
|
|
70
|
+
threshold?: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Run ATS analysis on a resume.
|
|
75
|
+
*
|
|
76
|
+
* Performs deterministic, offline checks:
|
|
77
|
+
* 1. Generic best-practice checks (contact, content, structure)
|
|
78
|
+
* 2. Optional job-description keyword matching
|
|
79
|
+
*
|
|
80
|
+
* @param resume - Validated resume data
|
|
81
|
+
* @param options - ATS analysis options
|
|
82
|
+
* @returns Full ATS analysis result with score, checks, and suggestions
|
|
83
|
+
*/
|
|
84
|
+
declare function analyzeAts(resume: ResumeSchema, options?: AtsOptions): AtsResult;
|
|
85
|
+
|
|
86
|
+
declare const program: Command;
|
|
87
|
+
|
|
88
|
+
export { type AtsOptions, type AtsResult, analyzeAts, program, themeRender };
|
|
@@ -356,4 +356,17 @@ declare function loadResumeFiles(inputPath?: string): Promise<{
|
|
|
356
356
|
yamlContents: string[];
|
|
357
357
|
}>;
|
|
358
358
|
|
|
359
|
-
|
|
359
|
+
interface ThemeModule {
|
|
360
|
+
render: (resume: Record<string, unknown>, options?: Record<string, unknown>) => string | Promise<string>;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Load a theme module by name
|
|
364
|
+
* @param themeName The name of the theme to load
|
|
365
|
+
* @param options Optional settings (autoInstall: boolean)
|
|
366
|
+
* @returns The loaded theme module
|
|
367
|
+
*/
|
|
368
|
+
declare function loadTheme(themeName: string, options?: {
|
|
369
|
+
autoInstall?: boolean;
|
|
370
|
+
}): ThemeModule;
|
|
371
|
+
|
|
372
|
+
export { type ResumeSchema as R, type ThemeModule as T, loadTheme as a, loadResumeFiles as l, processResumeData as p };
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "resuml",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.4.2",
|
|
4
|
+
"description": "Generate JSON resumes from YAML with theme support",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "./dist/api.js",
|
|
6
7
|
"exports": {
|
|
7
8
|
".": {
|
|
@@ -11,7 +12,7 @@
|
|
|
11
12
|
"./cli": "./dist/index.js"
|
|
12
13
|
},
|
|
13
14
|
"bin": {
|
|
14
|
-
"resuml": "./bin/resuml
|
|
15
|
+
"resuml": "./bin/resuml"
|
|
15
16
|
},
|
|
16
17
|
"files": [
|
|
17
18
|
"bin",
|
|
@@ -21,80 +22,85 @@
|
|
|
21
22
|
"src/types"
|
|
22
23
|
],
|
|
23
24
|
"scripts": {
|
|
24
|
-
"build": "tsup",
|
|
25
|
+
"build": "tsup && npm run build:builder",
|
|
26
|
+
"build:builder": "node scripts/build-builder.js",
|
|
27
|
+
"dev:builder": "node scripts/dev-server.js",
|
|
25
28
|
"prepublishOnly": "npm run generate:types && npm run build",
|
|
26
|
-
"generate:types": "node scripts/generate-types.
|
|
29
|
+
"generate:types": "node scripts/generate-types.cjs",
|
|
27
30
|
"test": "vitest run",
|
|
28
31
|
"test:watch": "vitest",
|
|
29
|
-
"lint": "eslint src --ext .ts",
|
|
32
|
+
"lint": "eslint src api --ext .ts,.tsx",
|
|
33
|
+
"lint:fix": "eslint src api --ext .ts,.tsx --fix",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
30
35
|
"format": "prettier --write .",
|
|
36
|
+
"format:check": "prettier --check .",
|
|
31
37
|
"release": "semantic-release",
|
|
32
38
|
"prepare": "husky"
|
|
33
39
|
},
|
|
34
40
|
"dependencies": {
|
|
35
|
-
"@jsonresume/schema": "^1.
|
|
36
|
-
"ajv": "^8.
|
|
37
|
-
"chalk": "^5.
|
|
41
|
+
"@jsonresume/schema": "^1.0.0",
|
|
42
|
+
"ajv": "^8.12.0",
|
|
43
|
+
"chalk": "^5.3.0",
|
|
38
44
|
"commander": "^11.1.0",
|
|
39
|
-
"js-yaml": "^4.1.
|
|
45
|
+
"js-yaml": "^4.1.0",
|
|
46
|
+
"jsonresume-theme-stackoverflow": "^2.1.0",
|
|
40
47
|
"lodash.merge": "^4.6.2",
|
|
41
|
-
"
|
|
48
|
+
"lucide-react": "^1.7.0",
|
|
49
|
+
"react": "^19.1.0",
|
|
50
|
+
"react-dom": "^19.1.0",
|
|
51
|
+
"tar": "^7.5.13",
|
|
52
|
+
"yaml": "^2.3.4",
|
|
53
|
+
"zod": "^4.3.6"
|
|
42
54
|
},
|
|
43
55
|
"devDependencies": {
|
|
56
|
+
"@codemirror/commands": "^6.10.3",
|
|
57
|
+
"@codemirror/lang-yaml": "^6.1.3",
|
|
58
|
+
"@codemirror/language": "^6.12.3",
|
|
59
|
+
"@codemirror/search": "^6.6.0",
|
|
60
|
+
"@codemirror/state": "^6.6.0",
|
|
61
|
+
"@codemirror/view": "^6.41.0",
|
|
44
62
|
"@commitlint/cli": "^19.8.1",
|
|
45
63
|
"@commitlint/config-conventional": "^19.8.1",
|
|
46
64
|
"@semantic-release/changelog": "^6.0.3",
|
|
47
65
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
48
66
|
"@semantic-release/git": "^10.0.1",
|
|
49
|
-
"@semantic-release/github": "^11.0.
|
|
50
|
-
"@semantic-release/npm": "^13.1.
|
|
51
|
-
"@semantic-release/release-notes-generator": "^14.
|
|
67
|
+
"@semantic-release/github": "^11.0.3",
|
|
68
|
+
"@semantic-release/npm": "^13.1.5",
|
|
69
|
+
"@semantic-release/release-notes-generator": "^14.0.3",
|
|
52
70
|
"@types/js-yaml": "^4.0.9",
|
|
53
71
|
"@types/lodash.merge": "^4.6.9",
|
|
54
|
-
"@types/node": "^20.19
|
|
55
|
-
"@
|
|
56
|
-
"@
|
|
57
|
-
"
|
|
58
|
-
"eslint-
|
|
72
|
+
"@types/node": "^20.11.19",
|
|
73
|
+
"@types/pako": "^2.0.4",
|
|
74
|
+
"@types/react": "^19.2.14",
|
|
75
|
+
"@types/react-dom": "^19.2.3",
|
|
76
|
+
"@typescript-eslint/eslint-plugin": "^8.33.0",
|
|
77
|
+
"@typescript-eslint/parser": "^8.33.0",
|
|
78
|
+
"@vercel/node": "^5.7.2",
|
|
79
|
+
"codemirror": "^6.0.2",
|
|
80
|
+
"esbuild": "^0.28.0",
|
|
81
|
+
"eslint": "^8.0.0",
|
|
82
|
+
"eslint-config-prettier": "^9.0.0",
|
|
59
83
|
"husky": "^9.1.7",
|
|
60
84
|
"json-schema-to-typescript": "^15.0.4",
|
|
61
|
-
"
|
|
85
|
+
"pako": "^2.1.0",
|
|
86
|
+
"playwright": "^1.52.0",
|
|
87
|
+
"prettier": "^3.0.0",
|
|
62
88
|
"semantic-release": "^25.0.3",
|
|
63
|
-
"tsup": "^8.
|
|
64
|
-
"typescript": "^5.
|
|
65
|
-
"vitest": "^3.2.
|
|
66
|
-
},
|
|
67
|
-
"peerDependencies": {
|
|
68
|
-
"puppeteer": ">=20.0.0"
|
|
69
|
-
},
|
|
70
|
-
"peerDependenciesMeta": {
|
|
71
|
-
"puppeteer": {
|
|
72
|
-
"optional": true
|
|
73
|
-
}
|
|
89
|
+
"tsup": "^8.0.2",
|
|
90
|
+
"typescript": "^5.0.0",
|
|
91
|
+
"vitest": "^3.2.2"
|
|
74
92
|
},
|
|
75
93
|
"engines": {
|
|
76
94
|
"node": ">=20.0.0"
|
|
77
95
|
},
|
|
78
|
-
"
|
|
96
|
+
"license": "ISC",
|
|
97
|
+
"author": "phoinixi",
|
|
79
98
|
"repository": {
|
|
80
99
|
"type": "git",
|
|
81
|
-
"url": "https://github.com/phoinixi/resuml
|
|
100
|
+
"url": "https://github.com/phoinixi/resuml"
|
|
82
101
|
},
|
|
83
|
-
"license": "ISC",
|
|
84
|
-
"author": "phoinixi",
|
|
85
102
|
"publishConfig": {
|
|
86
103
|
"access": "public",
|
|
87
104
|
"registry": "https://registry.npmjs.org/"
|
|
88
|
-
}
|
|
89
|
-
"keywords": [
|
|
90
|
-
"resume",
|
|
91
|
-
"yaml",
|
|
92
|
-
"cli",
|
|
93
|
-
"json-resume",
|
|
94
|
-
"pdf",
|
|
95
|
-
"resume-builder",
|
|
96
|
-
"resume-generator",
|
|
97
|
-
"cv",
|
|
98
|
-
"career"
|
|
99
|
-
]
|
|
105
|
+
}
|
|
100
106
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { build } from 'esbuild';
|
|
2
|
+
import { resolve, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
await build({
|
|
8
|
+
entryPoints: [resolve(__dirname, '../src/builder/index.tsx')],
|
|
9
|
+
bundle: true,
|
|
10
|
+
minify: true,
|
|
11
|
+
treeShaking: true,
|
|
12
|
+
format: 'esm',
|
|
13
|
+
target: 'es2022',
|
|
14
|
+
outfile: resolve(__dirname, '../docs/app/main.js'),
|
|
15
|
+
jsx: 'automatic',
|
|
16
|
+
define: {
|
|
17
|
+
'process.env.NODE_ENV': '"production"',
|
|
18
|
+
},
|
|
19
|
+
loader: {
|
|
20
|
+
'.ts': 'ts',
|
|
21
|
+
'.tsx': 'tsx',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
console.log('✅ Builder built to docs/app/main.js');
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Bundling Script
|
|
3
|
+
*
|
|
4
|
+
* Discovers jsonresume-theme packages from npm, installs them,
|
|
5
|
+
* and bundles each into a browser-compatible ES module.
|
|
6
|
+
*
|
|
7
|
+
* Output:
|
|
8
|
+
* docs/themes/{name}.js — bundled theme module
|
|
9
|
+
* docs/themes/manifest.json — theme metadata registry
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* node scripts/bundle-themes.js # Bundle popular themes
|
|
13
|
+
* node scripts/bundle-themes.js --all # Discover + bundle all from npm
|
|
14
|
+
* node scripts/bundle-themes.js --themes elegant,even,kendall # Specific themes
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { build } from 'esbuild';
|
|
18
|
+
import { resolve, dirname } from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
import { execSync } from 'child_process';
|
|
21
|
+
import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'fs';
|
|
22
|
+
|
|
23
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
const THEMES_DIR = resolve(__dirname, '../docs/themes');
|
|
25
|
+
|
|
26
|
+
// Popular themes that are known to work in the browser
|
|
27
|
+
const POPULAR_THEMES = [
|
|
28
|
+
'stackoverflow',
|
|
29
|
+
'elegant',
|
|
30
|
+
'even',
|
|
31
|
+
'kendall',
|
|
32
|
+
'flat',
|
|
33
|
+
'macchiato',
|
|
34
|
+
'class',
|
|
35
|
+
'paper',
|
|
36
|
+
'spartan',
|
|
37
|
+
'short',
|
|
38
|
+
'caffeine',
|
|
39
|
+
'onepage',
|
|
40
|
+
'kwan',
|
|
41
|
+
'autumn',
|
|
42
|
+
'relaxed',
|
|
43
|
+
'compact',
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
async function discoverThemes() {
|
|
47
|
+
const themes = [];
|
|
48
|
+
let from = 0;
|
|
49
|
+
const size = 250;
|
|
50
|
+
|
|
51
|
+
console.log('🔍 Discovering themes from npm...');
|
|
52
|
+
|
|
53
|
+
// npm search API has pagination, fetch up to 1000
|
|
54
|
+
for (let page = 0; page < 4; page++) {
|
|
55
|
+
const url = `https://registry.npmjs.org/-/v1/search?text=jsonresume-theme&size=${size}&from=${from}`;
|
|
56
|
+
const res = await fetch(url);
|
|
57
|
+
if (!res.ok) break;
|
|
58
|
+
const data = await res.json();
|
|
59
|
+
|
|
60
|
+
for (const pkg of data.objects) {
|
|
61
|
+
const name = pkg.package.name;
|
|
62
|
+
if (!name.startsWith('jsonresume-theme-')) continue;
|
|
63
|
+
const shortName = name.replace('jsonresume-theme-', '');
|
|
64
|
+
|
|
65
|
+
// Skip themes with 0 downloads or very old
|
|
66
|
+
if (pkg.score?.detail?.popularity === 0) continue;
|
|
67
|
+
|
|
68
|
+
themes.push({
|
|
69
|
+
name: shortName,
|
|
70
|
+
packageName: name,
|
|
71
|
+
description: pkg.package.description || '',
|
|
72
|
+
version: pkg.package.version,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (data.objects.length < size) break;
|
|
77
|
+
from += size;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(` Found ${themes.length} themes`);
|
|
81
|
+
return themes;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function bundleTheme(shortName, packageName) {
|
|
85
|
+
const entryContent = `
|
|
86
|
+
import theme from '${packageName}';
|
|
87
|
+
export const render = theme.render || theme.default?.render;
|
|
88
|
+
export const pdfRenderOptions = theme.pdfRenderOptions || theme.default?.pdfRenderOptions;
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
const entryFile = resolve(THEMES_DIR, `_entry_${shortName}.js`);
|
|
92
|
+
writeFileSync(entryFile, entryContent);
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
await build({
|
|
96
|
+
entryPoints: [entryFile],
|
|
97
|
+
bundle: true,
|
|
98
|
+
minify: true,
|
|
99
|
+
format: 'esm',
|
|
100
|
+
target: 'es2022',
|
|
101
|
+
platform: 'browser',
|
|
102
|
+
outfile: resolve(THEMES_DIR, `${shortName}.js`),
|
|
103
|
+
define: {
|
|
104
|
+
'process.env.NODE_ENV': '"production"',
|
|
105
|
+
'global': 'globalThis',
|
|
106
|
+
},
|
|
107
|
+
// Polyfill Node.js built-ins as no-ops for browser
|
|
108
|
+
alias: {
|
|
109
|
+
'path': resolve(__dirname, 'shims/path.js'),
|
|
110
|
+
'fs': resolve(__dirname, 'shims/fs.js'),
|
|
111
|
+
},
|
|
112
|
+
logLevel: 'silent',
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Clean up entry file
|
|
116
|
+
execSync(`rm -f "${entryFile}"`);
|
|
117
|
+
return true;
|
|
118
|
+
} catch (e) {
|
|
119
|
+
execSync(`rm -f "${entryFile}"`);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function main() {
|
|
125
|
+
const args = process.argv.slice(2);
|
|
126
|
+
const doAll = args.includes('--all');
|
|
127
|
+
const themesArg = args.find(a => a.startsWith('--themes='));
|
|
128
|
+
const specificThemes = themesArg ? themesArg.split('=')[1].split(',') : null;
|
|
129
|
+
|
|
130
|
+
mkdirSync(THEMES_DIR, { recursive: true });
|
|
131
|
+
|
|
132
|
+
// Create shims directory
|
|
133
|
+
const shimsDir = resolve(__dirname, 'shims');
|
|
134
|
+
mkdirSync(shimsDir, { recursive: true });
|
|
135
|
+
writeFileSync(resolve(shimsDir, 'path.js'), `
|
|
136
|
+
export const join = (...parts) => parts.join('/');
|
|
137
|
+
export const resolve = (...parts) => parts.join('/');
|
|
138
|
+
export const dirname = (p) => p.split('/').slice(0, -1).join('/');
|
|
139
|
+
export const basename = (p) => p.split('/').pop();
|
|
140
|
+
export const extname = (p) => { const m = p.match(/\\.[^.]+$/); return m ? m[0] : ''; };
|
|
141
|
+
export default { join, resolve, dirname, basename, extname };
|
|
142
|
+
`);
|
|
143
|
+
writeFileSync(resolve(shimsDir, 'fs.js'), `
|
|
144
|
+
export const readFileSync = () => '';
|
|
145
|
+
export const existsSync = () => false;
|
|
146
|
+
export default { readFileSync, existsSync };
|
|
147
|
+
`);
|
|
148
|
+
|
|
149
|
+
let themes;
|
|
150
|
+
if (specificThemes) {
|
|
151
|
+
themes = specificThemes.map(name => ({
|
|
152
|
+
name,
|
|
153
|
+
packageName: `jsonresume-theme-${name}`,
|
|
154
|
+
description: '',
|
|
155
|
+
version: '',
|
|
156
|
+
}));
|
|
157
|
+
} else if (doAll) {
|
|
158
|
+
themes = await discoverThemes();
|
|
159
|
+
} else {
|
|
160
|
+
themes = POPULAR_THEMES.map(name => ({
|
|
161
|
+
name,
|
|
162
|
+
packageName: `jsonresume-theme-${name}`,
|
|
163
|
+
description: '',
|
|
164
|
+
version: '',
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
console.log(`📦 Bundling ${themes.length} themes...\n`);
|
|
169
|
+
|
|
170
|
+
const manifest = [];
|
|
171
|
+
|
|
172
|
+
for (const theme of themes) {
|
|
173
|
+
process.stdout.write(` ${theme.name}... `);
|
|
174
|
+
|
|
175
|
+
// Install the theme
|
|
176
|
+
try {
|
|
177
|
+
execSync(`npm install --no-save ${theme.packageName} 2>/dev/null`, {
|
|
178
|
+
cwd: resolve(__dirname, '..'),
|
|
179
|
+
timeout: 30000,
|
|
180
|
+
});
|
|
181
|
+
} catch {
|
|
182
|
+
console.log('❌ install failed');
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Try to get description from installed package
|
|
187
|
+
try {
|
|
188
|
+
const pkgPath = resolve(__dirname, `../node_modules/${theme.packageName}/package.json`);
|
|
189
|
+
if (existsSync(pkgPath)) {
|
|
190
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
191
|
+
theme.description = theme.description || pkg.description || '';
|
|
192
|
+
theme.version = theme.version || pkg.version || '';
|
|
193
|
+
}
|
|
194
|
+
} catch {}
|
|
195
|
+
|
|
196
|
+
// Bundle it
|
|
197
|
+
const success = await bundleTheme(theme.name, theme.packageName);
|
|
198
|
+
|
|
199
|
+
if (success) {
|
|
200
|
+
const outFile = resolve(THEMES_DIR, `${theme.name}.js`);
|
|
201
|
+
const stat = existsSync(outFile) ? readFileSync(outFile).length : 0;
|
|
202
|
+
manifest.push({
|
|
203
|
+
name: theme.name,
|
|
204
|
+
displayName: theme.name.charAt(0).toUpperCase() + theme.name.slice(1).replace(/-/g, ' '),
|
|
205
|
+
description: theme.description,
|
|
206
|
+
browserCompatible: true,
|
|
207
|
+
fileSize: stat,
|
|
208
|
+
});
|
|
209
|
+
console.log(`✅ (${(stat / 1024).toFixed(0)}KB)`);
|
|
210
|
+
} else {
|
|
211
|
+
manifest.push({
|
|
212
|
+
name: theme.name,
|
|
213
|
+
displayName: theme.name.charAt(0).toUpperCase() + theme.name.slice(1).replace(/-/g, ' '),
|
|
214
|
+
description: theme.description,
|
|
215
|
+
browserCompatible: false,
|
|
216
|
+
fileSize: 0,
|
|
217
|
+
});
|
|
218
|
+
console.log('⚠️ browser incompatible');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Write manifest
|
|
223
|
+
writeFileSync(resolve(THEMES_DIR, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
224
|
+
|
|
225
|
+
const successful = manifest.filter(t => t.browserCompatible).length;
|
|
226
|
+
console.log(`\n✅ Done! ${successful}/${manifest.length} themes bundled for the browser`);
|
|
227
|
+
console.log(`📁 Output: docs/themes/`);
|
|
228
|
+
|
|
229
|
+
// Clean up shims
|
|
230
|
+
execSync(`rm -rf "${shimsDir}"`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
main().catch(console.error);
|