resuml 1.5.0 → 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.0",
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;
@@ -100,6 +183,13 @@ async function bundleTheme(shortName, packageName) {
100
183
  format: 'esm',
101
184
  target: 'es2022',
102
185
  platform: 'browser',
186
+ // Use 'require' condition so packages like underscore/lodash resolve to their
187
+ // CJS/UMD builds (which export a callable function) instead of their ESM builds
188
+ // (which export a namespace object that breaks _(collection) call syntax).
189
+ conditions: ['browser', 'require', 'default'],
190
+ // Prefer the CJS 'main' field over the ESM 'module' field for packages
191
+ // that don't use the exports map (older packages).
192
+ mainFields: ['browser', 'main'],
103
193
  outfile: resolve(THEMES_DIR, `${shortName}.js`),
104
194
  define: {
105
195
  'process.env.NODE_ENV': '"production"',
@@ -150,11 +240,7 @@ async function main() {
150
240
  export const extname = (p) => { const m = p.match(/\\.[^.]+$/); return m ? m[0] : ''; };
151
241
  export default { join, resolve, dirname, basename, extname };
152
242
  `);
153
- writeFileSync(resolve(shimsDir, 'fs.js'), `
154
- export const readFileSync = () => '';
155
- export const existsSync = () => false;
156
- export default { readFileSync, existsSync };
157
- `);
243
+ // fs shim is generated per-theme in bundleTheme() with embedded file contents
158
244
  writeFileSync(resolve(shimsDir, 'url.js'), `
159
245
  export const URL = globalThis.URL;
160
246
  export const URLSearchParams = globalThis.URLSearchParams;
@@ -229,7 +315,7 @@ async function main() {
229
315
  } catch {}
230
316
 
231
317
  // Bundle it
232
- const success = await bundleTheme(theme.name, theme.packageName);
318
+ const success = await bundleTheme(theme.name, theme.packageName, shimsDir);
233
319
 
234
320
  if (success) {
235
321
  const outFile = resolve(THEMES_DIR, `${theme.name}.js`);