resuml 1.5.1 → 1.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resuml",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "description": "Generate JSON resumes from YAML with theme support",
5
5
  "type": "module",
6
6
  "main": "./dist/api.js",
@@ -15,10 +15,10 @@
15
15
  */
16
16
 
17
17
  import { build } from 'esbuild';
18
- import { resolve, dirname } from 'path';
18
+ import { resolve, dirname, join } from 'path';
19
19
  import { fileURLToPath } from 'url';
20
20
  import { execSync } from 'child_process';
21
- import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'fs';
21
+ import { mkdirSync, writeFileSync, existsSync, readFileSync, readdirSync } from 'fs';
22
22
 
23
23
  const __dirname = dirname(fileURLToPath(import.meta.url));
24
24
  const THEMES_DIR = resolve(__dirname, '../docs/themes');
@@ -81,7 +81,90 @@ async function discoverThemes() {
81
81
  return themes;
82
82
  }
83
83
 
84
- async function bundleTheme(shortName, packageName) {
84
+ /** Recursively collect text files from a theme directory for embedding in the fs shim. */
85
+ function collectThemeFiles(themeDir) {
86
+ const files = {};
87
+ const dirs = {};
88
+
89
+ function walk(dir, relPrefix = '') {
90
+ let entries;
91
+ try { entries = readdirSync(dir, { withFileTypes: true }); }
92
+ catch { return; }
93
+
94
+ const childNames = [];
95
+ for (const entry of entries) {
96
+ if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;
97
+ childNames.push(entry.name);
98
+ const full = join(dir, entry.name);
99
+ const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
100
+
101
+ if (entry.isDirectory()) {
102
+ walk(full, rel);
103
+ } else {
104
+ const ext = (entry.name.split('.').pop() || '').toLowerCase();
105
+ if (['css', 'hbs', 'html', 'json', 'txt', 'handlebars', 'mustache'].includes(ext)) {
106
+ try { files[rel] = readFileSync(full, 'utf-8'); }
107
+ catch { /* skip unreadable */ }
108
+ }
109
+ }
110
+ }
111
+ dirs[relPrefix || '.'] = childNames;
112
+ }
113
+
114
+ walk(themeDir);
115
+ return { files, dirs };
116
+ }
117
+
118
+ /** Generate an fs shim that embeds the theme's files so readFileSync/readdirSync work at runtime. */
119
+ function generateThemeFsShim(themeFiles) {
120
+ const { files, dirs } = themeFiles;
121
+ return `
122
+ const __files = ${JSON.stringify(files)};
123
+ const __dirs = ${JSON.stringify(dirs)};
124
+
125
+ function matchFile(path) {
126
+ const clean = path.replace(/\\/+/g, '/').replace(/^\\/+/, '');
127
+ if (__files[clean] !== undefined) return __files[clean];
128
+ for (const key of Object.keys(__files)) {
129
+ if (clean.endsWith('/' + key) || clean.endsWith(key)) return __files[key];
130
+ }
131
+ return undefined;
132
+ }
133
+
134
+ function matchDir(path) {
135
+ const clean = path.replace(/\\/+/g, '/').replace(/^\\/+/, '');
136
+ if (__dirs[clean] !== undefined) return __dirs[clean];
137
+ for (const key of Object.keys(__dirs)) {
138
+ if (clean.endsWith('/' + key) || clean.endsWith(key)) return __dirs[key];
139
+ }
140
+ return undefined;
141
+ }
142
+
143
+ export const readFileSync = (path, encoding) => {
144
+ const r = matchFile(path);
145
+ return r !== undefined ? r : '';
146
+ };
147
+
148
+ export const readdirSync = (path) => {
149
+ const r = matchDir(path);
150
+ return r !== undefined ? r : [];
151
+ };
152
+
153
+ export const existsSync = (path) => {
154
+ return matchFile(path) !== undefined || matchDir(path) !== undefined;
155
+ };
156
+
157
+ export default { readFileSync, readdirSync, existsSync };
158
+ `;
159
+ }
160
+
161
+ async function bundleTheme(shortName, packageName, shimsDir) {
162
+ // Generate a theme-specific fs shim with embedded file contents so that
163
+ // readFileSync / readdirSync return real CSS, templates, and partials at runtime.
164
+ const themeDir = resolve(__dirname, `../node_modules/${packageName}`);
165
+ const themeFiles = collectThemeFiles(themeDir);
166
+ writeFileSync(resolve(shimsDir, 'fs.js'), generateThemeFsShim(themeFiles));
167
+
85
168
  const entryContent = `
86
169
  import * as themeNs from '${packageName}';
87
170
  const _t = themeNs.default ?? themeNs;
@@ -157,11 +240,7 @@ async function main() {
157
240
  export const extname = (p) => { const m = p.match(/\\.[^.]+$/); return m ? m[0] : ''; };
158
241
  export default { join, resolve, dirname, basename, extname };
159
242
  `);
160
- writeFileSync(resolve(shimsDir, 'fs.js'), `
161
- export const readFileSync = () => '';
162
- export const existsSync = () => false;
163
- export default { readFileSync, existsSync };
164
- `);
243
+ // fs shim is generated per-theme in bundleTheme() with embedded file contents
165
244
  writeFileSync(resolve(shimsDir, 'url.js'), `
166
245
  export const URL = globalThis.URL;
167
246
  export const URLSearchParams = globalThis.URLSearchParams;
@@ -236,7 +315,7 @@ async function main() {
236
315
  } catch {}
237
316
 
238
317
  // Bundle it
239
- const success = await bundleTheme(theme.name, theme.packageName);
318
+ const success = await bundleTheme(theme.name, theme.packageName, shimsDir);
240
319
 
241
320
  if (success) {
242
321
  const outFile = resolve(THEMES_DIR, `${theme.name}.js`);