resuml 1.6.0 → 1.7.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/package.json +3 -1
- package/scripts/bundle-themes.js +298 -57
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "resuml",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Generate JSON resumes from YAML with theme support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/api.js",
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
"dev:builder": "node scripts/dev-server.js",
|
|
29
29
|
"prepublishOnly": "npm run generate:types && npm run build:lib",
|
|
30
30
|
"generate:types": "node scripts/generate-types.cjs",
|
|
31
|
+
"bundle:themes": "node scripts/bundle-themes.js",
|
|
32
|
+
"bundle:themes:all": "node scripts/bundle-themes.js --all",
|
|
31
33
|
"test": "vitest run",
|
|
32
34
|
"test:watch": "vitest",
|
|
33
35
|
"lint": "eslint src api --ext .ts,.tsx",
|
package/scripts/bundle-themes.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Theme Bundling Script
|
|
3
3
|
*
|
|
4
4
|
* Bundles jsonresume-theme packages for the browser with:
|
|
5
|
+
* - Per-theme fs shim with embedded assets (CSS, templates, etc.)
|
|
5
6
|
* - CSS extraction (separate .css files)
|
|
6
7
|
* - Build-time snapshots (pre-rendered HTML for instant preview)
|
|
7
|
-
* -
|
|
8
|
+
* - Node.js built-in shims (buffer, stream, util, events, os, etc.)
|
|
8
9
|
*
|
|
9
10
|
* Output:
|
|
10
11
|
* docs/themes/{name}.js — bundled render module
|
|
@@ -49,16 +50,23 @@ const POPULAR_THEMES = [
|
|
|
49
50
|
'compact',
|
|
50
51
|
];
|
|
51
52
|
|
|
53
|
+
// File extensions to embed in the fs shim
|
|
54
|
+
const TEXT_EXTS = new Set([
|
|
55
|
+
'css', 'hbs', 'html', 'json', 'txt', 'handlebars', 'mustache', 'template',
|
|
56
|
+
'pug', 'jade', 'ejs', 'svg', 'less', 'scss',
|
|
57
|
+
]);
|
|
58
|
+
|
|
52
59
|
// Sample resume for generating snapshots
|
|
53
60
|
const SAMPLE_RESUME = {
|
|
54
61
|
basics: {
|
|
55
62
|
name: 'Jane Smith',
|
|
56
63
|
label: 'Senior Software Engineer',
|
|
64
|
+
image: '',
|
|
57
65
|
email: 'jane@example.com',
|
|
58
66
|
phone: '+1-555-987-6543',
|
|
59
67
|
url: 'https://janesmith.dev',
|
|
60
68
|
summary: 'Full-stack engineer with 8+ years of experience building scalable web applications. Passionate about clean architecture, performance optimization, and mentoring teams.',
|
|
61
|
-
location: { city: 'San Francisco', countryCode: 'US', region: 'California' },
|
|
69
|
+
location: { address: '', postalCode: '', city: 'San Francisco', countryCode: 'US', region: 'California' },
|
|
62
70
|
profiles: [
|
|
63
71
|
{ network: 'LinkedIn', username: 'janesmith', url: 'https://linkedin.com/in/janesmith' },
|
|
64
72
|
{ network: 'GitHub', username: 'janesmith', url: 'https://github.com/janesmith' },
|
|
@@ -113,7 +121,6 @@ const SAMPLE_RESUME = {
|
|
|
113
121
|
highlights: ['Published to npm with 5,000+ weekly downloads'],
|
|
114
122
|
},
|
|
115
123
|
],
|
|
116
|
-
// Provide empty arrays for sections themes might iterate over
|
|
117
124
|
volunteer: [],
|
|
118
125
|
awards: [],
|
|
119
126
|
certificates: [],
|
|
@@ -123,6 +130,30 @@ const SAMPLE_RESUME = {
|
|
|
123
130
|
references: [],
|
|
124
131
|
};
|
|
125
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Pad resume with safe defaults so themes don't crash on missing fields.
|
|
135
|
+
* Ported from api/_lib/themeInstaller.ts.
|
|
136
|
+
*/
|
|
137
|
+
function padResume(r) {
|
|
138
|
+
const basics = r.basics ?? {};
|
|
139
|
+
const location = basics.location ?? {};
|
|
140
|
+
const safe = {
|
|
141
|
+
...r,
|
|
142
|
+
basics: {
|
|
143
|
+
name: '', label: '', image: '', email: '', phone: '', url: '', summary: '',
|
|
144
|
+
...basics,
|
|
145
|
+
location: { address: '', postalCode: '', city: '', countryCode: '', region: '', ...location },
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
const arraySections = ['work','volunteer','education','awards','certificates','publications','skills','languages','interests','references','projects'];
|
|
149
|
+
for (const key of arraySections) {
|
|
150
|
+
safe[key] = Array.isArray(safe[key]) ? safe[key] : [];
|
|
151
|
+
}
|
|
152
|
+
const safeBasics = safe.basics;
|
|
153
|
+
safeBasics.profiles = Array.isArray(safeBasics.profiles) ? safeBasics.profiles : [];
|
|
154
|
+
return safe;
|
|
155
|
+
}
|
|
156
|
+
|
|
126
157
|
// ── Theme file collection ────────────────────────────────────────────
|
|
127
158
|
|
|
128
159
|
/** Recursively collect text files from a theme directory for the fs shim. */
|
|
@@ -146,7 +177,7 @@ function collectThemeFiles(themeDir) {
|
|
|
146
177
|
walk(full, rel);
|
|
147
178
|
} else {
|
|
148
179
|
const ext = (entry.name.split('.').pop() || '').toLowerCase();
|
|
149
|
-
if (
|
|
180
|
+
if (TEXT_EXTS.has(ext)) {
|
|
150
181
|
try { files[rel] = readFileSync(full, 'utf-8'); }
|
|
151
182
|
catch { /* skip */ }
|
|
152
183
|
}
|
|
@@ -163,11 +194,11 @@ function collectThemeFiles(themeDir) {
|
|
|
163
194
|
function generateThemeFsShim(themeFiles) {
|
|
164
195
|
const { files, dirs } = themeFiles;
|
|
165
196
|
const lines = [
|
|
166
|
-
`
|
|
167
|
-
`
|
|
197
|
+
`var __files = ${JSON.stringify(files)};`,
|
|
198
|
+
`var __dirs = ${JSON.stringify(dirs)};`,
|
|
168
199
|
'',
|
|
169
200
|
'function normalizePath(p) {',
|
|
170
|
-
' return p.replace(/[\\\\]+/g, "/").replace(/^\\/+/, "");',
|
|
201
|
+
' return String(p).replace(/[\\\\]+/g, "/").replace(/^\\/+/, "");',
|
|
171
202
|
'}',
|
|
172
203
|
'',
|
|
173
204
|
'function matchFile(p) {',
|
|
@@ -175,7 +206,7 @@ function generateThemeFsShim(themeFiles) {
|
|
|
175
206
|
' if (__files[clean] !== undefined) return __files[clean];',
|
|
176
207
|
' var keys = Object.keys(__files);',
|
|
177
208
|
' for (var i = 0; i < keys.length; i++) {',
|
|
178
|
-
' if (clean.endsWith("/" + keys[i]) || clean
|
|
209
|
+
' if (clean.endsWith("/" + keys[i]) || clean === keys[i]) return __files[keys[i]];',
|
|
179
210
|
' }',
|
|
180
211
|
' return undefined;',
|
|
181
212
|
'}',
|
|
@@ -185,17 +216,40 @@ function generateThemeFsShim(themeFiles) {
|
|
|
185
216
|
' if (__dirs[clean] !== undefined) return __dirs[clean];',
|
|
186
217
|
' var keys = Object.keys(__dirs);',
|
|
187
218
|
' for (var i = 0; i < keys.length; i++) {',
|
|
188
|
-
' if (clean.endsWith("/" + keys[i]) || clean
|
|
219
|
+
' if (clean.endsWith("/" + keys[i]) || clean === keys[i]) return __dirs[keys[i]];',
|
|
189
220
|
' }',
|
|
190
221
|
' return undefined;',
|
|
191
222
|
'}',
|
|
192
223
|
'',
|
|
193
|
-
'export var readFileSync = function(p) {
|
|
194
|
-
'
|
|
224
|
+
'export var readFileSync = function(p, encoding) {',
|
|
225
|
+
' var r = matchFile(p);',
|
|
226
|
+
' if (r !== undefined) return r;',
|
|
227
|
+
' return typeof encoding === "string" ? "" : "";',
|
|
228
|
+
'};',
|
|
229
|
+
'',
|
|
230
|
+
'export var readdirSync = function(p, opts) {',
|
|
231
|
+
' var r = matchDir(p);',
|
|
232
|
+
' if (r === undefined) r = [];',
|
|
233
|
+
' if (opts && opts.withFileTypes) {',
|
|
234
|
+
' return r.map(function(name) {',
|
|
235
|
+
' var childPath = normalizePath(p) + "/" + name;',
|
|
236
|
+
' var isDir = matchDir(childPath) !== undefined;',
|
|
237
|
+
' return { name: name, isFile: function() { return !isDir; }, isDirectory: function() { return isDir; } };',
|
|
238
|
+
' });',
|
|
239
|
+
' }',
|
|
240
|
+
' return r;',
|
|
241
|
+
'};',
|
|
242
|
+
'',
|
|
195
243
|
'export var existsSync = function(p) { return matchFile(p) !== undefined || matchDir(p) !== undefined; };',
|
|
196
244
|
'export var writeFileSync = function() {};',
|
|
197
245
|
'export var mkdirSync = function() {};',
|
|
198
|
-
'export
|
|
246
|
+
'export var statSync = function() { return { isFile: function() { return true; }, isDirectory: function() { return false; }, size: 0, mtime: new Date() }; };',
|
|
247
|
+
'export var lstatSync = statSync;',
|
|
248
|
+
'export var unlinkSync = function() {};',
|
|
249
|
+
'export var rmdirSync = function() {};',
|
|
250
|
+
'export var createReadStream = function() { return { pipe: function(d) { return d; }, on: function() { return this; } }; };',
|
|
251
|
+
'export var createWriteStream = function() { return { write: function() {}, end: function() {}, on: function() { return this; } }; };',
|
|
252
|
+
'export default { readFileSync: readFileSync, readdirSync: readdirSync, existsSync: existsSync, writeFileSync: writeFileSync, mkdirSync: mkdirSync, statSync: statSync, lstatSync: lstatSync, unlinkSync: unlinkSync, rmdirSync: rmdirSync, createReadStream: createReadStream, createWriteStream: createWriteStream };',
|
|
199
253
|
];
|
|
200
254
|
return lines.join('\n');
|
|
201
255
|
}
|
|
@@ -230,14 +284,14 @@ function generateSnapshot(shortName, packageName) {
|
|
|
230
284
|
const render = theme.render || (theme.default && theme.default.render);
|
|
231
285
|
if (typeof render !== 'function') return null;
|
|
232
286
|
|
|
233
|
-
const
|
|
287
|
+
const paddedResume = padResume(SAMPLE_RESUME);
|
|
288
|
+
const result = render(paddedResume);
|
|
234
289
|
// render() may return a string or a Promise
|
|
235
290
|
if (typeof result === 'string') {
|
|
236
291
|
writeFileSync(resolve(THEMES_DIR, `${shortName}.snapshot.html`), result);
|
|
237
292
|
extractCss(result, shortName);
|
|
238
293
|
return result;
|
|
239
294
|
}
|
|
240
|
-
// If it's a promise, we need to await it
|
|
241
295
|
if (result && typeof result.then === 'function') {
|
|
242
296
|
return result.then((html) => {
|
|
243
297
|
if (typeof html === 'string') {
|
|
@@ -324,20 +378,38 @@ async function bundleTheme(shortName, packageName, shimsDir) {
|
|
|
324
378
|
outfile: resolve(THEMES_DIR, `${shortName}.js`),
|
|
325
379
|
define: {
|
|
326
380
|
'process.env.NODE_ENV': '"production"',
|
|
381
|
+
'process.env.LANG': '""',
|
|
327
382
|
'global': 'globalThis',
|
|
328
383
|
'__dirname': '"/"',
|
|
329
384
|
'__filename': '"/index.js"',
|
|
330
385
|
'process.browser': 'true',
|
|
331
386
|
'process.platform': '"browser"',
|
|
332
|
-
'process.version': '"
|
|
387
|
+
'process.version': '"v20.0.0"',
|
|
388
|
+
'process.versions': '{}',
|
|
389
|
+
'process.stdout': 'false',
|
|
390
|
+
'process.stderr': 'false',
|
|
333
391
|
},
|
|
334
392
|
alias: {
|
|
335
393
|
'path': resolve(shimsDir, 'path.js'),
|
|
336
394
|
'fs': resolve(shimsDir, 'fs.js'),
|
|
337
395
|
'url': resolve(shimsDir, 'url.js'),
|
|
396
|
+
'assert': resolve(shimsDir, 'assert.js'),
|
|
397
|
+
'buffer': resolve(shimsDir, 'buffer.js'),
|
|
398
|
+
'stream': resolve(shimsDir, 'stream.js'),
|
|
399
|
+
'util': resolve(shimsDir, 'util.js'),
|
|
400
|
+
'events': resolve(shimsDir, 'events.js'),
|
|
401
|
+
'os': resolve(shimsDir, 'os.js'),
|
|
402
|
+
// node: prefixed variants
|
|
403
|
+
'node:fs': resolve(shimsDir, 'fs.js'),
|
|
404
|
+
'node:path': resolve(shimsDir, 'path.js'),
|
|
338
405
|
'node:url': resolve(shimsDir, 'url.js'),
|
|
339
406
|
'node:crypto': resolve(shimsDir, 'crypto.js'),
|
|
340
|
-
'
|
|
407
|
+
'node:buffer': resolve(shimsDir, 'buffer.js'),
|
|
408
|
+
'node:stream': resolve(shimsDir, 'stream.js'),
|
|
409
|
+
'node:util': resolve(shimsDir, 'util.js'),
|
|
410
|
+
'node:events': resolve(shimsDir, 'events.js'),
|
|
411
|
+
'node:os': resolve(shimsDir, 'os.js'),
|
|
412
|
+
'node:assert': resolve(shimsDir, 'assert.js'),
|
|
341
413
|
},
|
|
342
414
|
logLevel: 'silent',
|
|
343
415
|
});
|
|
@@ -351,6 +423,212 @@ async function bundleTheme(shortName, packageName, shimsDir) {
|
|
|
351
423
|
}
|
|
352
424
|
}
|
|
353
425
|
|
|
426
|
+
// ── Shim file writers ────────────────────────────────────────────────
|
|
427
|
+
|
|
428
|
+
function writeShims(shimsDir) {
|
|
429
|
+
writeFileSync(resolve(shimsDir, 'path.js'), [
|
|
430
|
+
'export var join = function() { return [].slice.call(arguments).join("/"); };',
|
|
431
|
+
'export var resolve = function() { return [].slice.call(arguments).join("/"); };',
|
|
432
|
+
'export var dirname = function(p) { return p.split("/").slice(0, -1).join("/"); };',
|
|
433
|
+
'export var basename = function(p, ext) { var b = p.split("/").pop() || ""; return ext && b.endsWith(ext) ? b.slice(0, -ext.length) : b; };',
|
|
434
|
+
'export var extname = function(p) { var m = p.match(/\\.[^.]+$/); return m ? m[0] : ""; };',
|
|
435
|
+
'export var sep = "/";',
|
|
436
|
+
'export var isAbsolute = function(p) { return p.charAt(0) === "/"; };',
|
|
437
|
+
'export var normalize = function(p) { return p; };',
|
|
438
|
+
'export var relative = function(from, to) { return to; };',
|
|
439
|
+
'export var parse = function(p) { return { root: "", dir: dirname(p), base: basename(p), ext: extname(p), name: basename(p, extname(p)) }; };',
|
|
440
|
+
'export default { join: join, resolve: resolve, dirname: dirname, basename: basename, extname: extname, sep: sep, isAbsolute: isAbsolute, normalize: normalize, relative: relative, parse: parse };',
|
|
441
|
+
].join('\n'));
|
|
442
|
+
|
|
443
|
+
// fs shim is generated per-theme in bundleTheme()
|
|
444
|
+
|
|
445
|
+
writeFileSync(resolve(shimsDir, 'url.js'), [
|
|
446
|
+
'export var URL = globalThis.URL;',
|
|
447
|
+
'export var URLSearchParams = globalThis.URLSearchParams;',
|
|
448
|
+
'export var fileURLToPath = function(u) { return u.replace(/^file:\\/\\//, ""); };',
|
|
449
|
+
'export var pathToFileURL = function(p) { return new globalThis.URL("file://" + p); };',
|
|
450
|
+
'export var format = function(u) { return typeof u === "string" ? u : u.href; };',
|
|
451
|
+
'export var parse = function(u) { return new globalThis.URL(u); };',
|
|
452
|
+
'export default { URL: URL, URLSearchParams: URLSearchParams, fileURLToPath: fileURLToPath, pathToFileURL: pathToFileURL, format: format, parse: parse };',
|
|
453
|
+
].join('\n'));
|
|
454
|
+
|
|
455
|
+
writeFileSync(resolve(shimsDir, 'crypto.js'), [
|
|
456
|
+
'export var createHash = function() { return { update: function() { return this; }, digest: function() { return ""; } }; };',
|
|
457
|
+
'export var randomBytes = function(n) { return new Uint8Array(n); };',
|
|
458
|
+
'export var createHmac = function() { return { update: function() { return this; }, digest: function() { return ""; } }; };',
|
|
459
|
+
'export default { createHash: createHash, randomBytes: randomBytes, createHmac: createHmac };',
|
|
460
|
+
].join('\n'));
|
|
461
|
+
|
|
462
|
+
writeFileSync(resolve(shimsDir, 'assert.js'), [
|
|
463
|
+
'var assert = function(v, msg) { if (!v) throw new Error(msg || "Assertion failed"); };',
|
|
464
|
+
'assert.ok = assert;',
|
|
465
|
+
'assert.strictEqual = function(a, b) { if (a !== b) throw new Error("Not equal"); };',
|
|
466
|
+
'assert.deepStrictEqual = function() {};',
|
|
467
|
+
'assert.fail = function(msg) { throw new Error(msg); };',
|
|
468
|
+
'export default assert;',
|
|
469
|
+
'export var ok = assert;',
|
|
470
|
+
'export var strictEqual = assert.strictEqual;',
|
|
471
|
+
].join('\n'));
|
|
472
|
+
|
|
473
|
+
writeFileSync(resolve(shimsDir, 'buffer.js'), [
|
|
474
|
+
'var _enc = typeof TextEncoder !== "undefined" ? new TextEncoder() : null;',
|
|
475
|
+
'var _dec = typeof TextDecoder !== "undefined" ? new TextDecoder() : null;',
|
|
476
|
+
'',
|
|
477
|
+
'function BufferShim(arg) {',
|
|
478
|
+
' if (typeof arg === "number") return new Uint8Array(arg);',
|
|
479
|
+
' if (arg instanceof Uint8Array) return arg;',
|
|
480
|
+
' if (arg instanceof ArrayBuffer) return new Uint8Array(arg);',
|
|
481
|
+
' return new Uint8Array(0);',
|
|
482
|
+
'}',
|
|
483
|
+
'',
|
|
484
|
+
'BufferShim.from = function(data, encoding) {',
|
|
485
|
+
' if (typeof data === "string") {',
|
|
486
|
+
' if (encoding === "base64") {',
|
|
487
|
+
' var bin = atob(data);',
|
|
488
|
+
' var bytes = new Uint8Array(bin.length);',
|
|
489
|
+
' for (var i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);',
|
|
490
|
+
' return bytes;',
|
|
491
|
+
' }',
|
|
492
|
+
' return _enc ? _enc.encode(data) : new Uint8Array(0);',
|
|
493
|
+
' }',
|
|
494
|
+
' if (Array.isArray(data)) return new Uint8Array(data);',
|
|
495
|
+
' if (data instanceof ArrayBuffer) return new Uint8Array(data);',
|
|
496
|
+
' if (data instanceof Uint8Array) return new Uint8Array(data);',
|
|
497
|
+
' return new Uint8Array(0);',
|
|
498
|
+
'};',
|
|
499
|
+
'BufferShim.alloc = function(size) { return new Uint8Array(size); };',
|
|
500
|
+
'BufferShim.allocUnsafe = function(size) { return new Uint8Array(size); };',
|
|
501
|
+
'BufferShim.isBuffer = function(obj) { return obj instanceof Uint8Array; };',
|
|
502
|
+
'BufferShim.isEncoding = function() { return true; };',
|
|
503
|
+
'BufferShim.byteLength = function(str) { return _enc ? _enc.encode(str).length : str.length; };',
|
|
504
|
+
'BufferShim.concat = function(list) {',
|
|
505
|
+
' var total = 0;',
|
|
506
|
+
' for (var i = 0; i < list.length; i++) total += list[i].length;',
|
|
507
|
+
' var result = new Uint8Array(total);',
|
|
508
|
+
' var offset = 0;',
|
|
509
|
+
' for (var j = 0; j < list.length; j++) { result.set(list[j], offset); offset += list[j].length; }',
|
|
510
|
+
' return result;',
|
|
511
|
+
'};',
|
|
512
|
+
'',
|
|
513
|
+
'export var Buffer = BufferShim;',
|
|
514
|
+
'export var SlowBuffer = BufferShim;',
|
|
515
|
+
'export var INSPECT_MAX_BYTES = 50;',
|
|
516
|
+
'export var kMaxLength = 2147483647;',
|
|
517
|
+
'export default { Buffer: BufferShim, SlowBuffer: BufferShim, INSPECT_MAX_BYTES: 50, kMaxLength: 2147483647 };',
|
|
518
|
+
].join('\n'));
|
|
519
|
+
|
|
520
|
+
writeFileSync(resolve(shimsDir, 'stream.js'), [
|
|
521
|
+
'function EventBase() { this._e = {}; }',
|
|
522
|
+
'EventBase.prototype.on = function(ev, fn) { (this._e[ev] = this._e[ev] || []).push(fn); return this; };',
|
|
523
|
+
'EventBase.prototype.addListener = EventBase.prototype.on;',
|
|
524
|
+
'EventBase.prototype.once = function(ev, fn) { var self = this; var w = function() { self.removeListener(ev, w); fn.apply(null, arguments); }; return this.on(ev, w); };',
|
|
525
|
+
'EventBase.prototype.emit = function(ev) { var args = [].slice.call(arguments, 1); (this._e[ev] || []).forEach(function(fn) { fn.apply(null, args); }); return true; };',
|
|
526
|
+
'EventBase.prototype.removeListener = function(ev, fn) { var l = this._e[ev]; if (l) this._e[ev] = l.filter(function(f) { return f !== fn; }); return this; };',
|
|
527
|
+
'EventBase.prototype.off = EventBase.prototype.removeListener;',
|
|
528
|
+
'EventBase.prototype.removeAllListeners = function(ev) { if (ev) delete this._e[ev]; else this._e = {}; return this; };',
|
|
529
|
+
'EventBase.prototype.setMaxListeners = function() { return this; };',
|
|
530
|
+
'EventBase.prototype.listeners = function(ev) { return this._e[ev] || []; };',
|
|
531
|
+
'',
|
|
532
|
+
'function Readable() { EventBase.call(this); } Readable.prototype = Object.create(EventBase.prototype);',
|
|
533
|
+
'Readable.prototype.read = function() { return null; };',
|
|
534
|
+
'Readable.prototype.pipe = function(d) { return d; };',
|
|
535
|
+
'Readable.prototype.unpipe = function() { return this; };',
|
|
536
|
+
'Readable.prototype.destroy = function() { return this; };',
|
|
537
|
+
'',
|
|
538
|
+
'function Writable() { EventBase.call(this); } Writable.prototype = Object.create(EventBase.prototype);',
|
|
539
|
+
'Writable.prototype.write = function() { return true; };',
|
|
540
|
+
'Writable.prototype.end = function() { return this; };',
|
|
541
|
+
'Writable.prototype.destroy = function() { return this; };',
|
|
542
|
+
'',
|
|
543
|
+
'function Transform() { EventBase.call(this); } Transform.prototype = Object.create(EventBase.prototype);',
|
|
544
|
+
'Transform.prototype.write = function() { return true; };',
|
|
545
|
+
'Transform.prototype.end = function() { return this; };',
|
|
546
|
+
'Transform.prototype.pipe = function(d) { return d; };',
|
|
547
|
+
'Transform.prototype.destroy = function() { return this; };',
|
|
548
|
+
'',
|
|
549
|
+
'function PassThrough() { Transform.call(this); } PassThrough.prototype = Object.create(Transform.prototype);',
|
|
550
|
+
'function Duplex() { EventBase.call(this); } Duplex.prototype = Object.create(EventBase.prototype);',
|
|
551
|
+
'Duplex.prototype.write = function() { return true; };',
|
|
552
|
+
'Duplex.prototype.end = function() { return this; };',
|
|
553
|
+
'Duplex.prototype.read = function() { return null; };',
|
|
554
|
+
'Duplex.prototype.pipe = function(d) { return d; };',
|
|
555
|
+
'Duplex.prototype.destroy = function() { return this; };',
|
|
556
|
+
'',
|
|
557
|
+
'function Stream() { EventBase.call(this); } Stream.prototype = Object.create(EventBase.prototype);',
|
|
558
|
+
'Stream.prototype.pipe = function(d) { return d; };',
|
|
559
|
+
'Stream.Readable = Readable; Stream.Writable = Writable; Stream.Transform = Transform;',
|
|
560
|
+
'Stream.PassThrough = PassThrough; Stream.Duplex = Duplex; Stream.Stream = Stream;',
|
|
561
|
+
'',
|
|
562
|
+
'export { Readable, Writable, Transform, PassThrough, Duplex, Stream };',
|
|
563
|
+
'export default Stream;',
|
|
564
|
+
].join('\n'));
|
|
565
|
+
|
|
566
|
+
writeFileSync(resolve(shimsDir, 'util.js'), [
|
|
567
|
+
'export var inherits = function(ctor, superCtor) { ctor.super_ = superCtor; Object.setPrototypeOf(ctor.prototype, superCtor.prototype); };',
|
|
568
|
+
'export var deprecate = function(fn) { return fn; };',
|
|
569
|
+
'export var promisify = function(fn) { return function() { var args = [].slice.call(arguments); return new Promise(function(ok, fail) { args.push(function(err, res) { err ? fail(err) : ok(res); }); fn.apply(null, args); }); }; };',
|
|
570
|
+
'export var inspect = function(obj) { try { return JSON.stringify(obj); } catch(e) { return String(obj); } };',
|
|
571
|
+
'export var format = function(f) { if (typeof f !== "string") return [].map.call(arguments, function(a) { return inspect(a); }).join(" "); var i = 1; var args = arguments; return f.replace(/%[sdj%]/g, function(m) { if (m === "%%") return "%"; if (i >= args.length) return m; var v = args[i++]; if (m === "%s") return String(v); if (m === "%d") return Number(v); if (m === "%j") try { return JSON.stringify(v); } catch(e) { return "[Circular]"; } return m; }); };',
|
|
572
|
+
'export var debuglog = function() { return function() {}; };',
|
|
573
|
+
'export var isArray = Array.isArray;',
|
|
574
|
+
'export var isBoolean = function(v) { return typeof v === "boolean"; };',
|
|
575
|
+
'export var isNull = function(v) { return v === null; };',
|
|
576
|
+
'export var isNumber = function(v) { return typeof v === "number"; };',
|
|
577
|
+
'export var isString = function(v) { return typeof v === "string"; };',
|
|
578
|
+
'export var isUndefined = function(v) { return v === undefined; };',
|
|
579
|
+
'export var isObject = function(v) { return typeof v === "object" && v !== null; };',
|
|
580
|
+
'export var isFunction = function(v) { return typeof v === "function"; };',
|
|
581
|
+
'export var isRegExp = function(v) { return v instanceof RegExp; };',
|
|
582
|
+
'export var isDate = function(v) { return v instanceof Date; };',
|
|
583
|
+
'export var isError = function(v) { return v instanceof Error; };',
|
|
584
|
+
'export var isPrimitive = function(v) { return v === null || typeof v !== "object" && typeof v !== "function"; };',
|
|
585
|
+
'export var TextEncoder = globalThis.TextEncoder;',
|
|
586
|
+
'export var TextDecoder = globalThis.TextDecoder;',
|
|
587
|
+
'export var types = { isAnyArrayBuffer: function() { return false; }, isTypedArray: function(v) { return ArrayBuffer.isView(v); } };',
|
|
588
|
+
'export default { inherits: inherits, deprecate: deprecate, promisify: promisify, inspect: inspect, format: format, debuglog: debuglog, isArray: isArray, isBoolean: isBoolean, isNull: isNull, isNumber: isNumber, isString: isString, isUndefined: isUndefined, isObject: isObject, isFunction: isFunction, isRegExp: isRegExp, isDate: isDate, isError: isError, isPrimitive: isPrimitive, TextEncoder: TextEncoder, TextDecoder: TextDecoder, types: types };',
|
|
589
|
+
].join('\n'));
|
|
590
|
+
|
|
591
|
+
writeFileSync(resolve(shimsDir, 'events.js'), [
|
|
592
|
+
'function EventEmitter() { this._events = {}; this._maxListeners = 10; }',
|
|
593
|
+
'EventEmitter.prototype.on = function(ev, fn) { (this._events[ev] = this._events[ev] || []).push(fn); return this; };',
|
|
594
|
+
'EventEmitter.prototype.addListener = EventEmitter.prototype.on;',
|
|
595
|
+
'EventEmitter.prototype.once = function(ev, fn) { var self = this; var w = function() { self.removeListener(ev, w); fn.apply(null, arguments); }; return this.on(ev, w); };',
|
|
596
|
+
'EventEmitter.prototype.emit = function(ev) { var args = [].slice.call(arguments, 1); (this._events[ev] || []).forEach(function(fn) { fn.apply(null, args); }); return true; };',
|
|
597
|
+
'EventEmitter.prototype.removeListener = function(ev, fn) { var l = this._events[ev]; if (l) this._events[ev] = l.filter(function(f) { return f !== fn; }); return this; };',
|
|
598
|
+
'EventEmitter.prototype.off = EventEmitter.prototype.removeListener;',
|
|
599
|
+
'EventEmitter.prototype.removeAllListeners = function(ev) { if (ev) delete this._events[ev]; else this._events = {}; return this; };',
|
|
600
|
+
'EventEmitter.prototype.setMaxListeners = function(n) { this._maxListeners = n; return this; };',
|
|
601
|
+
'EventEmitter.prototype.getMaxListeners = function() { return this._maxListeners; };',
|
|
602
|
+
'EventEmitter.prototype.listeners = function(ev) { return this._events[ev] || []; };',
|
|
603
|
+
'EventEmitter.prototype.listenerCount = function(ev) { return (this._events[ev] || []).length; };',
|
|
604
|
+
'EventEmitter.prototype.prependListener = EventEmitter.prototype.on;',
|
|
605
|
+
'EventEmitter.prototype.prependOnceListener = EventEmitter.prototype.once;',
|
|
606
|
+
'EventEmitter.prototype.eventNames = function() { return Object.keys(this._events); };',
|
|
607
|
+
'EventEmitter.EventEmitter = EventEmitter;',
|
|
608
|
+
'EventEmitter.defaultMaxListeners = 10;',
|
|
609
|
+
'export { EventEmitter };',
|
|
610
|
+
'export default EventEmitter;',
|
|
611
|
+
].join('\n'));
|
|
612
|
+
|
|
613
|
+
writeFileSync(resolve(shimsDir, 'os.js'), [
|
|
614
|
+
'export var platform = function() { return "browser"; };',
|
|
615
|
+
'export var tmpdir = function() { return "/tmp"; };',
|
|
616
|
+
'export var homedir = function() { return "/"; };',
|
|
617
|
+
'export var hostname = function() { return "localhost"; };',
|
|
618
|
+
'export var type = function() { return "Browser"; };',
|
|
619
|
+
'export var arch = function() { return "wasm"; };',
|
|
620
|
+
'export var release = function() { return "0.0.0"; };',
|
|
621
|
+
'export var EOL = "\\n";',
|
|
622
|
+
'export var endianness = function() { return "LE"; };',
|
|
623
|
+
'export var cpus = function() { return []; };',
|
|
624
|
+
'export var totalmem = function() { return 0; };',
|
|
625
|
+
'export var freemem = function() { return 0; };',
|
|
626
|
+
'export var networkInterfaces = function() { return {}; };',
|
|
627
|
+
'export var userInfo = function() { return { username: "browser", uid: 0, gid: 0, shell: "", homedir: "/" }; };',
|
|
628
|
+
'export default { platform: platform, tmpdir: tmpdir, homedir: homedir, hostname: hostname, type: type, arch: arch, release: release, EOL: EOL, endianness: endianness, cpus: cpus, totalmem: totalmem, freemem: freemem, networkInterfaces: networkInterfaces, userInfo: userInfo };',
|
|
629
|
+
].join('\n'));
|
|
630
|
+
}
|
|
631
|
+
|
|
354
632
|
// ── Main ─────────────────────────────────────────────────────────────
|
|
355
633
|
|
|
356
634
|
async function main() {
|
|
@@ -361,45 +639,10 @@ async function main() {
|
|
|
361
639
|
|
|
362
640
|
mkdirSync(THEMES_DIR, { recursive: true });
|
|
363
641
|
|
|
364
|
-
// Create shims directory
|
|
642
|
+
// Create shims directory and write all shims
|
|
365
643
|
const shimsDir = resolve(__dirname, 'shims');
|
|
366
644
|
mkdirSync(shimsDir, { recursive: true });
|
|
367
|
-
|
|
368
|
-
writeFileSync(resolve(shimsDir, 'path.js'), `
|
|
369
|
-
export const join = (...parts) => parts.join('/');
|
|
370
|
-
export const resolve = (...parts) => parts.join('/');
|
|
371
|
-
export const dirname = (p) => p.split('/').slice(0, -1).join('/');
|
|
372
|
-
export const basename = (p) => p.split('/').pop();
|
|
373
|
-
export const extname = (p) => { const m = p.match(/\\.[^.]+$/); return m ? m[0] : ''; };
|
|
374
|
-
export const sep = '/';
|
|
375
|
-
export default { join, resolve, dirname, basename, extname, sep };
|
|
376
|
-
`);
|
|
377
|
-
// fs shim is generated per-theme in bundleTheme()
|
|
378
|
-
writeFileSync(resolve(shimsDir, 'url.js'), `
|
|
379
|
-
export const URL = globalThis.URL;
|
|
380
|
-
export const URLSearchParams = globalThis.URLSearchParams;
|
|
381
|
-
export const fileURLToPath = (u) => u.replace(/^file:\\/\\//, '');
|
|
382
|
-
export const pathToFileURL = (p) => new globalThis.URL('file://' + p);
|
|
383
|
-
export const format = (u) => (typeof u === 'string' ? u : u.href);
|
|
384
|
-
export const parse = (u) => new globalThis.URL(u);
|
|
385
|
-
export default { URL, URLSearchParams, fileURLToPath, pathToFileURL, format, parse };
|
|
386
|
-
`);
|
|
387
|
-
writeFileSync(resolve(shimsDir, 'crypto.js'), `
|
|
388
|
-
export const createHash = () => ({ update: function() { return this; }, digest: () => '' });
|
|
389
|
-
export const randomBytes = (n) => new Uint8Array(n);
|
|
390
|
-
export const createHmac = () => ({ update: function() { return this; }, digest: () => '' });
|
|
391
|
-
export default { createHash, randomBytes, createHmac };
|
|
392
|
-
`);
|
|
393
|
-
writeFileSync(resolve(shimsDir, 'assert.js'), `
|
|
394
|
-
const assert = (v, msg) => { if (!v) throw new Error(msg || 'Assertion failed'); };
|
|
395
|
-
assert.ok = assert;
|
|
396
|
-
assert.strictEqual = (a, b) => { if (a !== b) throw new Error('Not equal'); };
|
|
397
|
-
assert.deepStrictEqual = () => {};
|
|
398
|
-
assert.fail = (msg) => { throw new Error(msg); };
|
|
399
|
-
export default assert;
|
|
400
|
-
export const ok = assert;
|
|
401
|
-
export const strictEqual = assert.strictEqual;
|
|
402
|
-
`);
|
|
645
|
+
writeShims(shimsDir);
|
|
403
646
|
|
|
404
647
|
let themes;
|
|
405
648
|
if (specificThemes) {
|
|
@@ -429,9 +672,9 @@ async function main() {
|
|
|
429
672
|
|
|
430
673
|
// Install the theme
|
|
431
674
|
try {
|
|
432
|
-
execSync(`npm install --no-save ${theme.packageName} 2>/dev/null`, {
|
|
675
|
+
execSync(`npm install --no-save --prefer-offline ${theme.packageName} 2>/dev/null`, {
|
|
433
676
|
cwd: resolve(__dirname, '..'),
|
|
434
|
-
timeout:
|
|
677
|
+
timeout: 60000,
|
|
435
678
|
});
|
|
436
679
|
} catch {
|
|
437
680
|
console.log('❌ install failed');
|
|
@@ -452,7 +695,6 @@ async function main() {
|
|
|
452
695
|
let hasSnapshot = false;
|
|
453
696
|
let hasCss = false;
|
|
454
697
|
const snapshotResult = generateSnapshot(theme.name, theme.packageName);
|
|
455
|
-
// Handle promise if async render
|
|
456
698
|
if (snapshotResult && typeof snapshotResult.then === 'function') {
|
|
457
699
|
const html = await snapshotResult;
|
|
458
700
|
hasSnapshot = !!html;
|
|
@@ -488,7 +730,6 @@ async function main() {
|
|
|
488
730
|
if (hasSnapshot) parts.push('snapshot');
|
|
489
731
|
console.log(parts.join(', ') + ')');
|
|
490
732
|
} else {
|
|
491
|
-
// Bundle failed — still useful if we have a snapshot (server fallback)
|
|
492
733
|
manifest.push({
|
|
493
734
|
name: theme.name,
|
|
494
735
|
displayName: theme.name.charAt(0).toUpperCase() + theme.name.slice(1).replace(/-/g, ' '),
|