@file-viewer/vite-plugin 2.0.11

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/dist/index.js ADDED
@@ -0,0 +1,1032 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
+ import { copyFile, cp, mkdir, rm, stat, writeFile } from 'node:fs/promises';
3
+ import { createRequire } from 'node:module';
4
+ import { dirname, extname, isAbsolute, join, resolve } from 'node:path';
5
+ const virtualModuleId = 'virtual:file-viewer-renderers';
6
+ const resolvedVirtualModuleId = `\0${virtualModuleId}`;
7
+ const pluginRequire = createRequire(import.meta.url);
8
+ let workspacePackageJsonCache = null;
9
+ const rendererModules = [
10
+ {
11
+ id: 'pdf',
12
+ packageName: '@file-viewer/renderer-pdf',
13
+ exportName: 'pdfRenderer',
14
+ formats: ['pdf'],
15
+ rendererIds: ['pdf'],
16
+ chunkName: 'file-viewer-pdf'
17
+ },
18
+ {
19
+ id: 'ofd',
20
+ packageName: '@file-viewer/renderer-ofd',
21
+ exportName: 'ofdRenderer',
22
+ formats: ['ofd'],
23
+ rendererIds: ['ofd'],
24
+ chunkName: 'file-viewer-ofd'
25
+ },
26
+ {
27
+ id: 'cad',
28
+ packageName: '@file-viewer/renderer-cad',
29
+ exportName: 'cadRenderer',
30
+ formats: ['cad', 'dwg', 'dxf', 'dwf', 'dwfx', 'xps'],
31
+ rendererIds: ['cad'],
32
+ chunkName: 'file-viewer-cad'
33
+ },
34
+ {
35
+ id: 'typst',
36
+ packageName: '@file-viewer/renderer-typst',
37
+ exportName: 'typstRenderer',
38
+ formats: ['typ', 'typst'],
39
+ rendererIds: ['typst'],
40
+ chunkName: 'file-viewer-typst'
41
+ },
42
+ {
43
+ id: 'presentation',
44
+ packageName: '@file-viewer/renderer-presentation',
45
+ exportName: 'presentationRenderer',
46
+ formats: ['presentation', 'pptx', 'pptm', 'potx', 'potm', 'ppsx', 'ppsm'],
47
+ rendererIds: ['office-presentation'],
48
+ chunkName: 'file-viewer-presentation'
49
+ },
50
+ {
51
+ id: 'word',
52
+ packageName: '@file-viewer/renderer-word',
53
+ exportName: 'wordRenderer',
54
+ formats: ['word', 'doc', 'docx', 'docm', 'dot', 'dotx', 'dotm', 'odt', 'odp', 'rtf'],
55
+ rendererIds: ['office-word-openxml', 'office-word-binary', 'open-document'],
56
+ chunkName: 'file-viewer-word'
57
+ },
58
+ {
59
+ id: 'spreadsheet',
60
+ packageName: '@file-viewer/renderer-spreadsheet',
61
+ exportName: 'spreadsheetRenderer',
62
+ formats: ['spreadsheet', 'excel', 'xls', 'xlsx', 'xltx', 'xlsm', 'xlsb', 'xlt', 'xltm', 'csv', 'tsv', 'ods', 'fods', 'numbers'],
63
+ rendererIds: ['spreadsheet-openxml'],
64
+ chunkName: 'file-viewer-spreadsheet'
65
+ },
66
+ {
67
+ id: 'drawing',
68
+ packageName: '@file-viewer/renderer-drawing',
69
+ exportName: 'drawingRenderer',
70
+ formats: ['drawing', 'drawio', 'dio', 'excalidraw', 'mermaid', 'mmd', 'plantuml', 'puml'],
71
+ rendererIds: ['drawing'],
72
+ chunkName: 'file-viewer-drawing'
73
+ },
74
+ {
75
+ id: 'model',
76
+ packageName: '@file-viewer/renderer-3d',
77
+ exportName: 'modelRenderer',
78
+ formats: [
79
+ '3d',
80
+ 'model',
81
+ 'stl',
82
+ 'obj',
83
+ 'gltf',
84
+ 'glb',
85
+ 'fbx',
86
+ 'dae',
87
+ '3ds',
88
+ '3mf',
89
+ 'amf',
90
+ 'ply',
91
+ 'pcd',
92
+ 'vrml',
93
+ 'wrl',
94
+ 'vtk',
95
+ 'vtp',
96
+ 'xyz',
97
+ 'usd',
98
+ 'usda',
99
+ 'usdc',
100
+ 'usdz',
101
+ 'kmz',
102
+ 'step',
103
+ 'stp',
104
+ 'iges',
105
+ 'igs',
106
+ 'ifc',
107
+ '3dm',
108
+ 'brep'
109
+ ],
110
+ rendererIds: ['model'],
111
+ chunkName: 'file-viewer-3d'
112
+ },
113
+ {
114
+ id: 'archive',
115
+ packageName: '@file-viewer/renderer-archive',
116
+ exportName: 'archiveRenderer',
117
+ formats: [
118
+ 'archive',
119
+ 'zip',
120
+ 'zipx',
121
+ '7z',
122
+ 'rar',
123
+ 'tar',
124
+ 'gz',
125
+ 'gzip',
126
+ 'tgz',
127
+ 'bz2',
128
+ 'bzip2',
129
+ 'tbz',
130
+ 'tbz2',
131
+ 'xz',
132
+ 'txz',
133
+ 'lzma',
134
+ 'zst',
135
+ 'tzst',
136
+ 'cab',
137
+ 'ar',
138
+ 'cpio',
139
+ 'iso',
140
+ 'xar',
141
+ 'lha',
142
+ 'lzh',
143
+ 'jar',
144
+ 'war',
145
+ 'ear',
146
+ 'apk',
147
+ 'cbz',
148
+ 'cbr'
149
+ ],
150
+ rendererIds: ['archive'],
151
+ chunkName: 'file-viewer-archive'
152
+ },
153
+ {
154
+ id: 'email',
155
+ packageName: '@file-viewer/renderer-email',
156
+ exportName: 'emailRenderer',
157
+ formats: ['email', 'eml', 'msg', 'mbox'],
158
+ rendererIds: ['email'],
159
+ chunkName: 'file-viewer-email'
160
+ },
161
+ {
162
+ id: 'ebook',
163
+ packageName: '@file-viewer/renderer-epub',
164
+ exportName: 'ebookRenderer',
165
+ formats: ['ebook', 'epub', 'umd'],
166
+ rendererIds: ['epub', 'umd'],
167
+ chunkName: 'file-viewer-ebook'
168
+ },
169
+ {
170
+ id: 'text',
171
+ packageName: '@file-viewer/renderer-text',
172
+ exportName: 'textRenderer',
173
+ formats: [
174
+ 'text',
175
+ 'txt',
176
+ 'log',
177
+ 'code',
178
+ 'md',
179
+ 'markdown',
180
+ 'js',
181
+ 'mjs',
182
+ 'cjs',
183
+ 'jsx',
184
+ 'ts',
185
+ 'tsx',
186
+ 'json',
187
+ 'jsonc',
188
+ 'json5',
189
+ 'xml',
190
+ 'yaml',
191
+ 'yml',
192
+ 'toml',
193
+ 'ini',
194
+ 'htm',
195
+ 'html',
196
+ 'css',
197
+ 'vue',
198
+ 'py',
199
+ 'java',
200
+ 'go',
201
+ 'rs',
202
+ 'c',
203
+ 'cpp',
204
+ 'cc',
205
+ 'h',
206
+ 'hpp',
207
+ 'cs',
208
+ 'diff',
209
+ 'patch',
210
+ 'bundle',
211
+ 'bdl',
212
+ 'php',
213
+ 'rb',
214
+ 'swift',
215
+ 'kt',
216
+ 'sh',
217
+ 'bash',
218
+ 'sql',
219
+ 'json5',
220
+ 'proto',
221
+ 'hcl',
222
+ 'tex',
223
+ 'gv',
224
+ 'graphviz',
225
+ 'http',
226
+ 'react',
227
+ 'ipynb'
228
+ ],
229
+ rendererIds: ['code', 'markdown'],
230
+ chunkName: 'file-viewer-text'
231
+ },
232
+ {
233
+ id: 'image',
234
+ packageName: '@file-viewer/renderer-image',
235
+ exportName: 'imageRenderer',
236
+ formats: [
237
+ 'image',
238
+ 'jpg',
239
+ 'jpeg',
240
+ 'png',
241
+ 'gif',
242
+ 'webp',
243
+ 'svg',
244
+ 'bmp',
245
+ 'tiff',
246
+ 'tif',
247
+ 'avif',
248
+ 'ico',
249
+ 'heic',
250
+ 'heif',
251
+ 'jxl'
252
+ ],
253
+ rendererIds: ['image'],
254
+ chunkName: 'file-viewer-image'
255
+ },
256
+ {
257
+ id: 'media',
258
+ packageName: '@file-viewer/renderer-media',
259
+ exportName: 'mediaRenderer',
260
+ formats: [
261
+ 'media',
262
+ 'audio',
263
+ 'video',
264
+ 'mp3',
265
+ 'mpeg',
266
+ 'wav',
267
+ 'ogg',
268
+ 'oga',
269
+ 'opus',
270
+ 'flac',
271
+ 'aac',
272
+ 'm4a',
273
+ 'mp4',
274
+ 'webm',
275
+ 'weba',
276
+ 'mov',
277
+ 'm3u8',
278
+ 'midi',
279
+ 'mid'
280
+ ],
281
+ rendererIds: ['audio', 'video'],
282
+ chunkName: 'file-viewer-media'
283
+ },
284
+ {
285
+ id: 'mindmap',
286
+ packageName: '@file-viewer/renderer-mindmap',
287
+ exportName: 'mindmapRenderer',
288
+ formats: ['mindmap', 'xmind'],
289
+ rendererIds: ['mindmap'],
290
+ chunkName: 'file-viewer-mindmap'
291
+ },
292
+ {
293
+ id: 'geo',
294
+ packageName: '@file-viewer/renderer-geo',
295
+ exportName: 'geoRenderer',
296
+ formats: ['geo', 'geojson', 'kml', 'gpx', 'shp'],
297
+ rendererIds: ['geo'],
298
+ chunkName: 'file-viewer-geo'
299
+ },
300
+ {
301
+ id: 'data',
302
+ packageName: '@file-viewer/renderer-data',
303
+ exportName: 'dataRenderer',
304
+ formats: [
305
+ 'data',
306
+ 'data-asset',
307
+ 'sqlite',
308
+ 'db',
309
+ 'sqlite3',
310
+ 'parquet',
311
+ 'avro',
312
+ 'psd',
313
+ 'ai',
314
+ 'eps',
315
+ 'webarchive',
316
+ 'wasm',
317
+ 'ttf',
318
+ 'otf',
319
+ 'woff',
320
+ 'woff2'
321
+ ],
322
+ rendererIds: ['data-asset'],
323
+ chunkName: 'file-viewer-data'
324
+ },
325
+ {
326
+ id: 'eda',
327
+ packageName: '@file-viewer/renderer-eda',
328
+ exportName: 'edaRenderer',
329
+ formats: ['eda', 'gds', 'oas', 'oasis', 'olb', 'dra', 'dsn'],
330
+ rendererIds: ['eda'],
331
+ chunkName: 'file-viewer-eda'
332
+ }
333
+ ];
334
+ const descriptorsById = new Map(rendererModules.map((descriptor) => [descriptor.id, descriptor]));
335
+ const descriptorsByFormat = new Map();
336
+ const descriptorsByRendererId = new Map();
337
+ rendererModules.forEach((descriptor) => {
338
+ descriptor.formats.forEach((format) => descriptorsByFormat.set(format, descriptor));
339
+ descriptor.rendererIds.forEach((rendererId) => descriptorsByRendererId.set(rendererId, descriptor));
340
+ });
341
+ const presetRendererIds = {
342
+ all: rendererModules.map((descriptor) => descriptor.id),
343
+ lite: ['text', 'image', 'media'],
344
+ office: ['pdf', 'word', 'spreadsheet', 'presentation', 'ofd'],
345
+ engineering: [
346
+ 'cad',
347
+ 'model',
348
+ 'drawing',
349
+ 'mindmap',
350
+ 'geo',
351
+ 'typst',
352
+ 'archive',
353
+ 'data',
354
+ 'eda'
355
+ ]
356
+ };
357
+ const presetModules = {
358
+ all: {
359
+ id: 'all',
360
+ packageName: '@file-viewer/preset-all',
361
+ exportName: 'allRenderers',
362
+ rendererIds: presetRendererIds.all,
363
+ chunkName: 'file-viewer-preset-all'
364
+ },
365
+ lite: {
366
+ id: 'lite',
367
+ packageName: '@file-viewer/preset-lite',
368
+ exportName: 'liteRenderers',
369
+ rendererIds: presetRendererIds.lite,
370
+ chunkName: 'file-viewer-preset-lite'
371
+ },
372
+ office: {
373
+ id: 'office',
374
+ packageName: '@file-viewer/preset-office',
375
+ exportName: 'officeRenderers',
376
+ rendererIds: presetRendererIds.office,
377
+ chunkName: 'file-viewer-preset-office'
378
+ },
379
+ engineering: {
380
+ id: 'engineering',
381
+ packageName: '@file-viewer/preset-engineering',
382
+ exportName: 'engineeringRenderers',
383
+ rendererIds: presetRendererIds.engineering,
384
+ chunkName: 'file-viewer-preset-engineering'
385
+ }
386
+ };
387
+ const defaultScanRoots = ['src', 'app', 'pages', 'components'];
388
+ const defaultScanExtensions = [
389
+ 'js',
390
+ 'jsx',
391
+ 'ts',
392
+ 'tsx',
393
+ 'vue',
394
+ 'svelte',
395
+ 'html',
396
+ 'md',
397
+ 'mdx'
398
+ ];
399
+ const ignoredScanDirectories = new Set([
400
+ '.git',
401
+ '.idea',
402
+ '.next',
403
+ '.nuxt',
404
+ '.output',
405
+ '.release',
406
+ '.svelte-kit',
407
+ '.vite',
408
+ 'coverage',
409
+ 'dist',
410
+ 'node_modules'
411
+ ]);
412
+ const defaultScanMaxFileSize = 1024 * 1024;
413
+ const mimeFormatHints = {
414
+ 'application/pdf': ['pdf'],
415
+ 'application/ofd': ['ofd'],
416
+ 'application/zip': ['zip'],
417
+ 'application/x-zip-compressed': ['zip'],
418
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['docx'],
419
+ 'application/msword': ['doc'],
420
+ 'application/vnd.ms-excel': ['xls'],
421
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['xlsx'],
422
+ 'application/vnd.ms-powerpoint': ['ppt'],
423
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': ['pptx'],
424
+ 'application/json': ['json'],
425
+ 'application/x-ndjson': ['json'],
426
+ 'application/xml': ['xml'],
427
+ 'application/x-tar': ['tar'],
428
+ 'application/gzip': ['gz'],
429
+ 'application/x-7z-compressed': ['7z'],
430
+ 'application/vnd.rar': ['rar'],
431
+ 'text/markdown': ['md'],
432
+ 'text/csv': ['csv'],
433
+ 'text/html': ['html'],
434
+ 'text/css': ['css'],
435
+ 'text/plain': ['txt'],
436
+ 'image/*': ['image'],
437
+ 'image/tiff': ['tiff'],
438
+ 'image/heic': ['heic'],
439
+ 'image/heif': ['heif'],
440
+ 'audio/*': ['audio'],
441
+ 'audio/mpeg': ['mp3'],
442
+ 'audio/ogg': ['ogg'],
443
+ 'audio/flac': ['flac'],
444
+ 'audio/midi': ['midi'],
445
+ 'video/*': ['video']
446
+ };
447
+ function normalizeToken(value) {
448
+ return value.trim().toLowerCase().replace(/^\./, '');
449
+ }
450
+ function unique(items) {
451
+ return [...new Set(items)];
452
+ }
453
+ function normalizeScanExtension(value) {
454
+ return normalizeToken(value);
455
+ }
456
+ function collectQuotedTokens(value) {
457
+ return [...value.matchAll(/['"`]([^'"`]+)['"`]/g)].flatMap((match) => collectDelimitedTokens(match[1]));
458
+ }
459
+ function collectDelimitedTokens(value) {
460
+ return value
461
+ .split(/[\s,;|]+/g)
462
+ .map((item) => item.trim())
463
+ .filter(Boolean)
464
+ .flatMap((item) => mimeFormatHints[item.toLowerCase()] || [item])
465
+ .map(normalizeToken)
466
+ .filter(Boolean);
467
+ }
468
+ export function extractFileViewerRendererHintTokens(source) {
469
+ const tokens = [];
470
+ const push = (items) => {
471
+ items.forEach((item) => {
472
+ if (item) {
473
+ tokens.push(item);
474
+ }
475
+ });
476
+ };
477
+ // Explicit JavaScript/TypeScript hints:
478
+ // const fileViewerFormats = ['pdf', 'docx']
479
+ // fileViewerRenderers: ['pdf', 'cad']
480
+ for (const match of source.matchAll(/\bfileViewer(?:Formats?|Renderers?)\b\s*[:=]\s*\[([\s\S]*?)\]/g)) {
481
+ push(collectQuotedTokens(match[1]));
482
+ }
483
+ // HTML / template hints:
484
+ // <div data-file-viewer-formats="pdf,docx"></div>
485
+ // <input accept=".pdf,.docx,application/vnd.ms-excel">
486
+ for (const match of source.matchAll(/\bdata-file-viewer-(?:formats?|renderers?)\s*=\s*["']([^"']+)["']/g)) {
487
+ push(collectDelimitedTokens(match[1]));
488
+ }
489
+ for (const match of source.matchAll(/\baccept\s*=\s*["']([^"']+)["']/g)) {
490
+ push(collectDelimitedTokens(match[1]));
491
+ }
492
+ // Comment hints are useful in non-framework projects where the upload UI is
493
+ // assembled dynamically:
494
+ // // file-viewer-formats: pdf,docx,dwg
495
+ for (const match of source.matchAll(/file-viewer-(?:formats?|renderers?)\s*:\s*([^\n\r<]+)/g)) {
496
+ push(collectDelimitedTokens(match[1]));
497
+ }
498
+ return unique(tokens);
499
+ }
500
+ function normalizeScanOptions(value) {
501
+ if (!value) {
502
+ return null;
503
+ }
504
+ const raw = typeof value === 'object' ? value : {};
505
+ if (raw.enabled === false) {
506
+ return null;
507
+ }
508
+ return {
509
+ roots: raw.roots?.length ? [...raw.roots] : defaultScanRoots,
510
+ extensions: new Set((raw.extensions?.length ? raw.extensions : defaultScanExtensions).map(normalizeScanExtension)),
511
+ maxFileSize: raw.maxFileSize ?? defaultScanMaxFileSize
512
+ };
513
+ }
514
+ function scanFile(filePath, extensions, maxFileSize) {
515
+ const extension = normalizeScanExtension(extname(filePath));
516
+ if (!extensions.has(extension)) {
517
+ return [];
518
+ }
519
+ const info = statSyncSafe(filePath);
520
+ if (!info?.isFile() || info.size > maxFileSize) {
521
+ return [];
522
+ }
523
+ return extractFileViewerRendererHintTokens(readFileSync(filePath, 'utf8'));
524
+ }
525
+ function statSyncSafe(filePath) {
526
+ try {
527
+ return existsSync(filePath) ? statSync(filePath) : null;
528
+ }
529
+ catch {
530
+ return null;
531
+ }
532
+ }
533
+ function walkScanRoot(directory, extensions, maxFileSize, output) {
534
+ if (!existsSync(directory)) {
535
+ return;
536
+ }
537
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
538
+ if (entry.isDirectory()) {
539
+ if (!ignoredScanDirectories.has(entry.name)) {
540
+ walkScanRoot(join(directory, entry.name), extensions, maxFileSize, output);
541
+ }
542
+ continue;
543
+ }
544
+ if (entry.isFile()) {
545
+ output.push(...scanFile(join(directory, entry.name), extensions, maxFileSize));
546
+ }
547
+ }
548
+ }
549
+ export function collectFileViewerRendererScanTokens(projectRoot, scan) {
550
+ const normalized = normalizeScanOptions(scan);
551
+ if (!normalized) {
552
+ return [];
553
+ }
554
+ const tokens = [];
555
+ normalized.roots.forEach((root) => {
556
+ walkScanRoot(isAbsolute(root) ? root : resolve(projectRoot, root), normalized.extensions, normalized.maxFileSize, tokens);
557
+ });
558
+ return unique(tokens);
559
+ }
560
+ function selectRenderers(options) {
561
+ const preset = options.preset ?? null;
562
+ const presetCoveredIds = new Set(preset ? presetRendererIds[preset] : []);
563
+ const selected = new Map();
564
+ const missing = [];
565
+ const requestedTokens = [...(options.renderers || []), ...(options.formats || [])]
566
+ .map(normalizeToken)
567
+ .filter(Boolean);
568
+ requestedTokens.forEach((token) => {
569
+ const descriptor = descriptorsById.get(token) ||
570
+ descriptorsByRendererId.get(token) ||
571
+ descriptorsByFormat.get(token);
572
+ if (descriptor) {
573
+ if (!presetCoveredIds.has(descriptor.id)) {
574
+ selected.set(descriptor.id, descriptor);
575
+ }
576
+ return;
577
+ }
578
+ missing.push({
579
+ format: token,
580
+ note: 'No renderer mapping is registered for this format yet.'
581
+ });
582
+ });
583
+ return { preset, descriptors: [...selected.values()], missing };
584
+ }
585
+ function formatMissingRendererMessage(missing) {
586
+ return [
587
+ 'Some requested File Viewer formats do not have standalone renderer packages in this workspace yet:',
588
+ ...missing.map((item) => ` - ${item.format}${item.targetPackage ? ` -> ${item.targetPackage}` : ''}: ${item.note}`),
589
+ 'Use @file-viewer/preset-all for full compatibility while the remaining renderer lines are extracted, or remove those formats from @file-viewer/vite-plugin.'
590
+ ].join('\n');
591
+ }
592
+ function assertMissingRendererPolicy(selection, mode) {
593
+ if (!selection.missing.length || mode === 'ignore') {
594
+ return;
595
+ }
596
+ const message = formatMissingRendererMessage(selection.missing);
597
+ if (mode === 'warn') {
598
+ console.warn(`[file-viewer:vite-plugin]\n${message}`);
599
+ return;
600
+ }
601
+ throw new Error(`[file-viewer:vite-plugin]\n${message}`);
602
+ }
603
+ function expandDescriptorRendererIds(ids) {
604
+ return unique(ids.flatMap((id) => {
605
+ const descriptor = descriptorsById.get(id);
606
+ return descriptor ? [...descriptor.rendererIds] : [id];
607
+ }));
608
+ }
609
+ function renderVirtualModule(selection, formats) {
610
+ const presetModule = selection.preset ? presetModules[selection.preset] : null;
611
+ const presetImport = presetModule
612
+ ? `import { ${presetModule.exportName} as presetRenderers } from '${presetModule.packageName}';`
613
+ : null;
614
+ const rendererImports = selection.descriptors.map((descriptor, index) => `import { ${descriptor.exportName} as renderer${index} } from '${descriptor.packageName}';`);
615
+ const rendererNames = [
616
+ ...(presetModule ? ['presetRenderers'] : []),
617
+ ...selection.descriptors.map((_descriptor, index) => `renderer${index}`)
618
+ ];
619
+ const rendererIds = unique([
620
+ ...(presetModule ? expandDescriptorRendererIds(presetModule.rendererIds) : []),
621
+ ...selection.descriptors.flatMap((descriptor) => descriptor.rendererIds)
622
+ ]);
623
+ const packages = unique([
624
+ ...(presetModule ? [presetModule.packageName] : []),
625
+ ...selection.descriptors.map((descriptor) => descriptor.packageName)
626
+ ]);
627
+ const plan = {
628
+ preset: selection.preset,
629
+ formats,
630
+ rendererIds,
631
+ packages,
632
+ generatedBy: '@file-viewer/vite-plugin'
633
+ };
634
+ return [
635
+ ...[presetImport].filter(Boolean),
636
+ ...rendererImports,
637
+ '',
638
+ `export const configuredFileViewerRenderers = [${rendererNames.join(', ')}];`,
639
+ `export const fileViewerRendererPlan = ${JSON.stringify(plan, null, 2)};`,
640
+ 'export default configuredFileViewerRenderers;',
641
+ ''
642
+ ].join('\n');
643
+ }
644
+ function hasManualChunks(config) {
645
+ const output = config.build?.rollupOptions?.output;
646
+ if (Array.isArray(output)) {
647
+ return output.some((item) => Boolean(item.manualChunks));
648
+ }
649
+ return Boolean(output?.manualChunks);
650
+ }
651
+ function createManualChunks(selection) {
652
+ const packageToChunk = new Map();
653
+ if (selection.preset) {
654
+ const presetModule = presetModules[selection.preset];
655
+ packageToChunk.set(presetModule.packageName, presetModule.chunkName);
656
+ presetModule.rendererIds
657
+ .map((id) => descriptorsById.get(id))
658
+ .filter((descriptor) => Boolean(descriptor))
659
+ .forEach((descriptor) => {
660
+ packageToChunk.set(descriptor.packageName, descriptor.chunkName);
661
+ });
662
+ }
663
+ selection.descriptors.forEach((descriptor) => {
664
+ packageToChunk.set(descriptor.packageName, descriptor.chunkName);
665
+ });
666
+ return (id) => {
667
+ const normalized = id.replace(/\\/g, '/');
668
+ for (const [packageName, chunkName] of packageToChunk) {
669
+ if (normalized.includes(`/node_modules/${packageName}/`) ||
670
+ normalized.includes(`/node_modules/.pnpm/${packageName.replace('/', '+')}@`)) {
671
+ return chunkName;
672
+ }
673
+ }
674
+ return undefined;
675
+ };
676
+ }
677
+ function projectRequire() {
678
+ return createRequire(resolve(process.cwd(), 'package.json'));
679
+ }
680
+ function findPackageJsonFromEntry(entry) {
681
+ let current = dirname(entry);
682
+ while (current && current !== dirname(current)) {
683
+ const candidate = join(current, 'package.json');
684
+ if (existsSync(candidate)) {
685
+ return candidate;
686
+ }
687
+ current = dirname(current);
688
+ }
689
+ return null;
690
+ }
691
+ function tryResolvePackageJson(packageName, requireFn) {
692
+ try {
693
+ return requireFn.resolve(`${packageName}/package.json`);
694
+ }
695
+ catch {
696
+ try {
697
+ const entry = requireFn.resolve(packageName);
698
+ return findPackageJsonFromEntry(entry);
699
+ }
700
+ catch {
701
+ return null;
702
+ }
703
+ }
704
+ }
705
+ function collectWorkspacePackageJsons(root) {
706
+ const packageJsons = new Map();
707
+ const ignoredDirectoryNames = new Set(['node_modules', 'dist', 'vendor', '.git']);
708
+ const scanRoots = ['packages', 'apps'].map((item) => join(root, item));
709
+ const visit = (directory, depth) => {
710
+ if (depth < 0 || !existsSync(directory)) {
711
+ return;
712
+ }
713
+ const packageJsonPath = join(directory, 'package.json');
714
+ if (existsSync(packageJsonPath)) {
715
+ try {
716
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
717
+ if (packageJson.name) {
718
+ packageJsons.set(packageJson.name, packageJsonPath);
719
+ }
720
+ }
721
+ catch {
722
+ // Ignore malformed package.json files in unrelated workspace folders.
723
+ }
724
+ }
725
+ for (const entry of readdirSync(directory)) {
726
+ if (ignoredDirectoryNames.has(entry)) {
727
+ continue;
728
+ }
729
+ const candidate = join(directory, entry);
730
+ if (statSync(candidate).isDirectory()) {
731
+ visit(candidate, depth - 1);
732
+ }
733
+ }
734
+ };
735
+ scanRoots.forEach((scanRoot) => visit(scanRoot, 4));
736
+ return packageJsons;
737
+ }
738
+ function resolveWorkspacePackageJson(packageName) {
739
+ if (!workspacePackageJsonCache) {
740
+ workspacePackageJsonCache = collectWorkspacePackageJsons(process.cwd());
741
+ }
742
+ return workspacePackageJsonCache.get(packageName) || null;
743
+ }
744
+ function resolvePackageJson(packageName, anchorPackages = []) {
745
+ const requireFns = [projectRequire(), pluginRequire];
746
+ const effectiveAnchorPackages = unique([
747
+ ...anchorPackages,
748
+ ...Object.values(presetModules).map((preset) => preset.packageName)
749
+ ]);
750
+ effectiveAnchorPackages.forEach((anchorPackage) => {
751
+ const anchorPackageJson = requireFns
752
+ .map((requireFn) => tryResolvePackageJson(anchorPackage, requireFn))
753
+ .find(Boolean);
754
+ if (anchorPackageJson) {
755
+ requireFns.push(createRequire(anchorPackageJson));
756
+ }
757
+ });
758
+ for (const requireFn of requireFns) {
759
+ const packageJson = tryResolvePackageJson(packageName, requireFn);
760
+ if (packageJson) {
761
+ return packageJson;
762
+ }
763
+ }
764
+ return resolveWorkspacePackageJson(packageName);
765
+ }
766
+ function resolvePackageRoot(packageName, anchorPackages = []) {
767
+ const packageJson = resolvePackageJson(packageName, anchorPackages);
768
+ return packageJson ? dirname(packageJson) : null;
769
+ }
770
+ async function copyFileIfPresent(from, to) {
771
+ if (!from || !existsSync(from)) {
772
+ return false;
773
+ }
774
+ const info = await stat(from);
775
+ if (!info.isFile()) {
776
+ return false;
777
+ }
778
+ await mkdir(dirname(to), { recursive: true });
779
+ await copyFile(from, to);
780
+ return true;
781
+ }
782
+ async function copyDirectoryIfPresent(from, to) {
783
+ if (!from || !existsSync(from)) {
784
+ return false;
785
+ }
786
+ const info = await stat(from);
787
+ if (!info.isDirectory()) {
788
+ return false;
789
+ }
790
+ await rm(to, { recursive: true, force: true });
791
+ await mkdir(dirname(to), { recursive: true });
792
+ await cp(from, to, { recursive: true, force: true });
793
+ return true;
794
+ }
795
+ async function copyKnownRendererAssets(targetRoot, rendererIds) {
796
+ const selected = new Set(rendererIds);
797
+ const results = [];
798
+ const push = async (rendererId, id, to, copyAction, reason) => {
799
+ if (!selected.has(rendererId)) {
800
+ return;
801
+ }
802
+ const copied = await copyAction();
803
+ results.push({
804
+ rendererId,
805
+ id,
806
+ to,
807
+ copied,
808
+ reason: copied ? undefined : reason || 'source asset not found'
809
+ });
810
+ };
811
+ const pdfRoot = resolvePackageRoot('pdfjs-dist', ['@file-viewer/renderer-pdf']);
812
+ await push('pdf', 'pdf-worker', join(targetRoot, 'vendor/pdf/pdf.worker.mjs'), () => copyFileIfPresent(pdfRoot ? join(pdfRoot, 'legacy/build/pdf.worker.mjs') : null, join(targetRoot, 'vendor/pdf/pdf.worker.mjs')));
813
+ await push('pdf', 'pdf-cmaps', join(targetRoot, 'vendor/pdf/cmaps'), () => copyDirectoryIfPresent(pdfRoot ? join(pdfRoot, 'cmaps') : null, join(targetRoot, 'vendor/pdf/cmaps')));
814
+ await push('pdf', 'pdf-wasm', join(targetRoot, 'vendor/pdf/wasm'), () => copyDirectoryIfPresent(pdfRoot ? join(pdfRoot, 'wasm') : null, join(targetRoot, 'vendor/pdf/wasm')));
815
+ await push('pdf', 'pdf-standard-fonts', join(targetRoot, 'vendor/pdf/standard_fonts'), () => copyDirectoryIfPresent(pdfRoot ? join(pdfRoot, 'standard_fonts') : null, join(targetRoot, 'vendor/pdf/standard_fonts')));
816
+ const cadRoot = resolvePackageRoot('@flyfish-dev/cad-viewer', ['@file-viewer/renderer-cad']);
817
+ await push('cad', 'cad-wasm-directory', join(targetRoot, 'wasm/cad'), () => copyDirectoryIfPresent(cadRoot ? join(cadRoot, 'dist/wasm') : null, join(targetRoot, 'wasm/cad')));
818
+ const typstCompilerRoot = resolvePackageRoot('@myriaddreamin/typst-ts-web-compiler', [
819
+ '@file-viewer/renderer-typst'
820
+ ]);
821
+ const typstRendererRoot = resolvePackageRoot('@myriaddreamin/typst-ts-renderer', [
822
+ '@file-viewer/renderer-typst'
823
+ ]);
824
+ await push('typst', 'typst-compiler-wasm', join(targetRoot, 'wasm/typst/typst_ts_web_compiler_bg.wasm'), () => copyFileIfPresent(typstCompilerRoot ? join(typstCompilerRoot, 'pkg/typst_ts_web_compiler_bg.wasm') : null, join(targetRoot, 'wasm/typst/typst_ts_web_compiler_bg.wasm')));
825
+ await push('typst', 'typst-renderer-wasm', join(targetRoot, 'wasm/typst/typst_ts_renderer_bg.wasm'), () => copyFileIfPresent(typstRendererRoot ? join(typstRendererRoot, 'pkg/typst_ts_renderer_bg.wasm') : null, join(targetRoot, 'wasm/typst/typst_ts_renderer_bg.wasm')));
826
+ const archiveRoot = resolvePackageRoot('libarchive.js', ['@file-viewer/renderer-archive']);
827
+ await push('archive', 'libarchive-worker', join(targetRoot, 'vendor/libarchive/worker-bundle.js'), () => copyFileIfPresent(archiveRoot ? join(archiveRoot, 'dist/worker-bundle.js') : null, join(targetRoot, 'vendor/libarchive/worker-bundle.js')));
828
+ await push('archive', 'libarchive-wasm', join(targetRoot, 'vendor/libarchive/libarchive.wasm'), () => copyFileIfPresent(archiveRoot ? join(archiveRoot, 'dist/libarchive.wasm') : null, join(targetRoot, 'vendor/libarchive/libarchive.wasm')));
829
+ const sqlJsRoot = resolvePackageRoot('sql.js', ['@file-viewer/renderer-data']);
830
+ await push('data-asset', 'data-sql-wasm', join(targetRoot, 'wasm/data/sql-wasm.wasm'), () => copyFileIfPresent(sqlJsRoot ? join(sqlJsRoot, 'dist/sql-wasm.wasm') : null, join(targetRoot, 'wasm/data/sql-wasm.wasm')));
831
+ await mkdir(targetRoot, { recursive: true });
832
+ await writeFile(join(targetRoot, 'flyfish-viewer-assets.json'), `${JSON.stringify({
833
+ schemaVersion: 1,
834
+ generatedBy: '@file-viewer/vite-plugin',
835
+ rendererIds,
836
+ copiedAt: new Date().toISOString(),
837
+ assets: results
838
+ }, null, 2)}\n`);
839
+ return results;
840
+ }
841
+ function resolveTargetDir(value, fallback) {
842
+ const target = value || fallback;
843
+ return isAbsolute(target) ? target : resolve(process.cwd(), target);
844
+ }
845
+ function copyOptions(value) {
846
+ return typeof value === 'object' ? value : {};
847
+ }
848
+ function collectAssetRendererIds(selection) {
849
+ const presetIds = selection.preset
850
+ ? expandDescriptorRendererIds(presetModules[selection.preset].rendererIds)
851
+ : [];
852
+ return unique([
853
+ ...presetIds,
854
+ ...selection.descriptors.flatMap((descriptor) => descriptor.rendererIds)
855
+ ]);
856
+ }
857
+ function reportAssetCopy(results, targetRoot, mode) {
858
+ const failedRequired = results.filter((result) => !result.copied && ['pdf', 'cad', 'typst'].includes(result.rendererId));
859
+ if (!results.length) {
860
+ return;
861
+ }
862
+ const summary = `[file-viewer:vite-plugin] Copied ${results.filter((result) => result.copied).length}/${results.length} renderer assets to ${targetRoot}`;
863
+ if (!failedRequired.length || mode === 'ignore') {
864
+ console.log(summary);
865
+ return;
866
+ }
867
+ const details = failedRequired
868
+ .map((result) => ` - ${result.rendererId}:${result.id} -> ${result.to} (${result.reason})`)
869
+ .join('\n');
870
+ if (mode === 'warn') {
871
+ console.warn(`${summary}\nMissing required assets:\n${details}`);
872
+ return;
873
+ }
874
+ throw new Error(`${summary}\nMissing required assets:\n${details}`);
875
+ }
876
+ export function fileViewerRenderers(options = {}) {
877
+ const moduleId = options.moduleId || virtualModuleId;
878
+ const resolvedModuleId = `\0${moduleId}`;
879
+ const missingMode = options.missingRenderer || 'error';
880
+ const explicitFormats = [...(options.formats || []), ...(options.renderers || [])]
881
+ .map(normalizeToken)
882
+ .filter(Boolean);
883
+ let scanFormats = [];
884
+ let requestedFormats = unique([...explicitFormats, ...scanFormats]);
885
+ let selection = selectRenderers({
886
+ ...options,
887
+ formats: [...(options.formats || []), ...scanFormats]
888
+ });
889
+ let resolvedConfig = null;
890
+ const refreshSelection = (projectRoot) => {
891
+ if (projectRoot) {
892
+ scanFormats = collectFileViewerRendererScanTokens(projectRoot, options.scan);
893
+ }
894
+ requestedFormats = unique([...explicitFormats, ...scanFormats]);
895
+ selection = selectRenderers({
896
+ ...options,
897
+ formats: [...(options.formats || []), ...scanFormats]
898
+ });
899
+ };
900
+ return {
901
+ name: 'file-viewer-renderers',
902
+ enforce: 'pre',
903
+ config(userConfig) {
904
+ const projectRoot = resolve(process.cwd(), userConfig.root || '.');
905
+ refreshSelection(projectRoot);
906
+ if ((options.chunkStrategy || 'renderer') === 'none' || hasManualChunks(userConfig)) {
907
+ return undefined;
908
+ }
909
+ return {
910
+ build: {
911
+ rollupOptions: {
912
+ output: {
913
+ manualChunks: createManualChunks(selection)
914
+ }
915
+ }
916
+ }
917
+ };
918
+ },
919
+ configResolved(config) {
920
+ resolvedConfig = config;
921
+ refreshSelection(config.root);
922
+ },
923
+ buildStart() {
924
+ assertMissingRendererPolicy(selection, missingMode);
925
+ const packages = unique([
926
+ ...(selection.preset ? [presetModules[selection.preset].packageName] : []),
927
+ ...selection.descriptors.map((descriptor) => descriptor.packageName)
928
+ ]);
929
+ const missingPackages = packages.filter((packageName) => !resolvePackageJson(packageName));
930
+ if (missingPackages.length && missingMode !== 'ignore') {
931
+ const message = `Missing File Viewer preset/renderer package(s): ${missingPackages.join(', ')}. Install them or remove the matching preset/formats from @file-viewer/vite-plugin.`;
932
+ if (missingMode === 'warn') {
933
+ console.warn(`[file-viewer:vite-plugin] ${message}`);
934
+ }
935
+ else {
936
+ throw new Error(`[file-viewer:vite-plugin] ${message}`);
937
+ }
938
+ }
939
+ },
940
+ async configureServer() {
941
+ if (!options.copyAssets || copyOptions(options.copyAssets).mode === 'build') {
942
+ return;
943
+ }
944
+ const targetRoot = resolveTargetDir(copyOptions(options.copyAssets).publicDir, resolvedConfig?.publicDir || 'public');
945
+ const results = await copyKnownRendererAssets(targetRoot, collectAssetRendererIds(selection));
946
+ reportAssetCopy(results, targetRoot, missingMode);
947
+ },
948
+ handleHotUpdate(context) {
949
+ if (!options.scan || !resolvedConfig) {
950
+ return undefined;
951
+ }
952
+ const previous = requestedFormats.join(',');
953
+ refreshSelection(resolvedConfig.root);
954
+ if (requestedFormats.join(',') === previous) {
955
+ return undefined;
956
+ }
957
+ const modules = [
958
+ context.server.moduleGraph.getModuleById(resolvedModuleId),
959
+ context.server.moduleGraph.getModuleById(resolvedVirtualModuleId)
960
+ ].filter(Boolean);
961
+ modules.forEach((module) => {
962
+ if (module) {
963
+ context.server.moduleGraph.invalidateModule(module);
964
+ }
965
+ });
966
+ context.server.ws.send({
967
+ type: 'full-reload'
968
+ });
969
+ return [];
970
+ },
971
+ async closeBundle() {
972
+ if (!options.copyAssets || copyOptions(options.copyAssets).mode === 'dev') {
973
+ return;
974
+ }
975
+ const outDir = resolvedConfig?.build.outDir || 'dist';
976
+ const targetRoot = resolveTargetDir(copyOptions(options.copyAssets).outDir, outDir);
977
+ const results = await copyKnownRendererAssets(targetRoot, collectAssetRendererIds(selection));
978
+ reportAssetCopy(results, targetRoot, missingMode);
979
+ },
980
+ resolveId(id) {
981
+ if (id === moduleId || id === virtualModuleId) {
982
+ return id === virtualModuleId ? resolvedVirtualModuleId : resolvedModuleId;
983
+ }
984
+ return undefined;
985
+ },
986
+ load(id) {
987
+ if (id === resolvedModuleId || id === resolvedVirtualModuleId) {
988
+ return renderVirtualModule(selection, requestedFormats);
989
+ }
990
+ return undefined;
991
+ }
992
+ };
993
+ }
994
+ export function createFileViewerManualChunks(options = {}) {
995
+ return createManualChunks(selectRenderers(options));
996
+ }
997
+ export function resolveFileViewerRendererSelection(options = {}, projectRoot = process.cwd()) {
998
+ const scanFormats = collectFileViewerRendererScanTokens(projectRoot, options.scan);
999
+ const requestedFormats = unique([
1000
+ ...(options.formats || []),
1001
+ ...(options.renderers || []),
1002
+ ...scanFormats
1003
+ ].map(normalizeToken).filter(Boolean));
1004
+ const selection = selectRenderers({
1005
+ ...options,
1006
+ formats: [...(options.formats || []), ...scanFormats]
1007
+ });
1008
+ const presetModule = selection.preset ? presetModules[selection.preset] : null;
1009
+ const packages = unique([
1010
+ ...(presetModule ? [presetModule.packageName] : []),
1011
+ ...selection.descriptors.map((descriptor) => descriptor.packageName)
1012
+ ]);
1013
+ return {
1014
+ preset: selection.preset,
1015
+ presetPackage: presetModule?.packageName ?? null,
1016
+ formats: requestedFormats,
1017
+ packages,
1018
+ rendererIds: unique([
1019
+ ...(presetModule ? expandDescriptorRendererIds(presetModule.rendererIds) : []),
1020
+ ...selection.descriptors.flatMap((descriptor) => descriptor.rendererIds)
1021
+ ]),
1022
+ renderers: selection.descriptors.map((descriptor) => ({
1023
+ id: descriptor.id,
1024
+ packageName: descriptor.packageName,
1025
+ formats: [...descriptor.formats],
1026
+ rendererIds: [...descriptor.rendererIds],
1027
+ chunkName: descriptor.chunkName
1028
+ })),
1029
+ missing: selection.missing
1030
+ };
1031
+ }
1032
+ export default fileViewerRenderers;