@file-viewer/vite-plugin 2.4.0 → 3.0.1

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.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Plugin } from 'vite';
2
- export type FileViewerVitePreset = 'all' | 'lite' | 'office' | 'engineering';
2
+ export type FileViewerVitePreset = 'all' | 'lite' | 'standard' | 'office' | 'engineering';
3
3
  export type FileViewerVitePresetMode = FileViewerVitePreset | 'auto';
4
4
  export type FileViewerMissingRendererMode = 'error' | 'warn' | 'ignore';
5
5
  export type FileViewerChunkStrategy = 'renderer' | 'none';
@@ -122,6 +122,7 @@ export interface FileViewerCopyAssetsTarget {
122
122
  targetRoot: string;
123
123
  baseDir: string;
124
124
  installedFullPackages: string[];
125
+ installedAssetPackages: string[];
125
126
  }
126
127
  /**
127
128
  * Resolves the exact directory used by copyAssets without running a Vite build.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
- import { copyFile, cp, mkdir, readFile, realpath, rm, stat, writeFile } from 'node:fs/promises';
2
+ import { copyFile, cp, lstat, mkdir, readFile, readdir, realpath, rm, stat, writeFile } from 'node:fs/promises';
3
3
  import { createRequire } from 'node:module';
4
4
  import { dirname, extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
5
5
  const virtualModuleId = 'virtual:file-viewer-renderers';
@@ -12,7 +12,10 @@ const isPresentationRendererImporter = (importer) => {
12
12
  const normalized = importer.replace(/\\/g, '/');
13
13
  return (normalized.includes('/@file-viewer/renderer-presentation/') ||
14
14
  normalized.includes('/@file-viewer+renderer-presentation@') ||
15
- normalized.includes('/packages/renderers/presentation/'));
15
+ normalized.includes('/packages/renderers/presentation/') ||
16
+ normalized.includes('/@file-viewer/renderer-ppt/') ||
17
+ normalized.includes('/@file-viewer+renderer-ppt@') ||
18
+ normalized.includes('/packages/renderers/presentation-ppt/'));
16
19
  };
17
20
  const pluginRequire = createRequire(import.meta.url);
18
21
  let workspacePackageJsonCache = null;
@@ -59,7 +62,7 @@ const rendererModules = [
59
62
  },
60
63
  {
61
64
  id: 'presentation-binary',
62
- packageName: '@file-viewer/renderer-presentation/ppt',
65
+ packageName: '@file-viewer/renderer-ppt',
63
66
  exportName: 'pptRenderer',
64
67
  formats: ['ppt', 'pot'],
65
68
  rendererIds: ['office-presentation-binary'],
@@ -67,7 +70,7 @@ const rendererModules = [
67
70
  },
68
71
  {
69
72
  id: 'presentation-openxml',
70
- packageName: '@file-viewer/renderer-presentation/pptx',
73
+ packageName: '@file-viewer/renderer-pptx',
71
74
  exportName: 'pptxRenderer',
72
75
  formats: ['pptx', 'pptm', 'potx', 'potm', 'ppsx', 'ppsm'],
73
76
  rendererIds: ['office-presentation'],
@@ -200,6 +203,14 @@ const rendererModules = [
200
203
  rendererIds: ['archive'],
201
204
  chunkName: 'file-viewer-archive'
202
205
  },
206
+ {
207
+ id: 'chm',
208
+ packageName: '@file-viewer/renderer-chm',
209
+ exportName: 'chmRenderer',
210
+ formats: ['chm'],
211
+ rendererIds: ['chm'],
212
+ chunkName: 'file-viewer-chm'
213
+ },
203
214
  {
204
215
  id: 'email',
205
216
  packageName: '@file-viewer/renderer-email',
@@ -303,6 +314,46 @@ const rendererModules = [
303
314
  rendererIds: ['image'],
304
315
  chunkName: 'file-viewer-image'
305
316
  },
317
+ {
318
+ id: 'dicom',
319
+ packageName: '@file-viewer/renderer-dicom',
320
+ exportName: 'dicomRenderer',
321
+ formats: ['dicom', 'dcm'],
322
+ rendererIds: ['dicom'],
323
+ chunkName: 'file-viewer-dicom',
324
+ includeInPresetAll: false
325
+ },
326
+ {
327
+ id: 'signature',
328
+ packageName: '@file-viewer/renderer-signature',
329
+ exportName: 'signatureRenderer',
330
+ formats: [
331
+ 'p7m',
332
+ 'p7s',
333
+ 'p7c',
334
+ 'p7b',
335
+ 'pkcs7',
336
+ 'cms',
337
+ 'cmsc',
338
+ 'tsd',
339
+ 'tst',
340
+ 'tsq',
341
+ 'tsr',
342
+ 'asics',
343
+ 'scs',
344
+ 'asice',
345
+ 'sce',
346
+ 'ers',
347
+ 'asc',
348
+ 'sig',
349
+ 'pgp',
350
+ 'gpg',
351
+ 'jws'
352
+ ],
353
+ rendererIds: ['signature'],
354
+ chunkName: 'file-viewer-signature',
355
+ includeInPresetAll: false
356
+ },
306
357
  {
307
358
  id: 'media',
308
359
  packageName: '@file-viewer/renderer-media',
@@ -347,6 +398,51 @@ const rendererModules = [
347
398
  rendererIds: ['geo'],
348
399
  chunkName: 'file-viewer-geo'
349
400
  },
401
+ {
402
+ id: 'design',
403
+ packageName: '@file-viewer/renderer-design',
404
+ exportName: 'designRenderer',
405
+ includeInPresetAll: false,
406
+ formats: [
407
+ 'design',
408
+ 'photoshop-design',
409
+ 'illustrator-pdf-design',
410
+ 'postscript-design',
411
+ 'adobe-palette-design',
412
+ 'photoshop-resource-design',
413
+ 'indesign-idml-design',
414
+ 'indesign-exchange-design',
415
+ 'adobe-animate-xfl-design',
416
+ 'adobe-xd-design',
417
+ 'indesign-native-design',
418
+ 'psd',
419
+ 'psb',
420
+ 'pdd',
421
+ 'psdt',
422
+ 'ai',
423
+ 'ait',
424
+ 'eps',
425
+ 'ps',
426
+ 'ase',
427
+ 'aco',
428
+ 'abr',
429
+ 'csh',
430
+ 'pat',
431
+ 'grd',
432
+ 'asl',
433
+ 'idml',
434
+ 'icml',
435
+ 'idms',
436
+ 'inx',
437
+ 'fla',
438
+ 'xfl',
439
+ 'xd',
440
+ 'indd',
441
+ 'indt'
442
+ ],
443
+ rendererIds: ['photoshop-design', 'illustrator-pdf-design', 'postscript-design', 'adobe-palette-design', 'photoshop-resource-design', 'indesign-idml-design', 'indesign-exchange-design', 'adobe-animate-xfl-design', 'adobe-xd-design', 'indesign-native-design'],
444
+ chunkName: 'file-viewer-design'
445
+ },
350
446
  {
351
447
  id: 'data',
352
448
  packageName: '@file-viewer/renderer-data',
@@ -359,9 +455,6 @@ const rendererModules = [
359
455
  'sqlite3',
360
456
  'parquet',
361
457
  'avro',
362
- 'psd',
363
- 'ai',
364
- 'eps',
365
458
  'webarchive',
366
459
  'wasm',
367
460
  'ttf',
@@ -389,8 +482,22 @@ rendererModules.forEach((descriptor) => {
389
482
  descriptor.rendererIds.forEach((rendererId) => descriptorsByRendererId.set(rendererId, descriptor));
390
483
  });
391
484
  const presetRendererIds = {
392
- all: rendererModules.map((descriptor) => descriptor.id),
485
+ all: rendererModules
486
+ .filter((descriptor) => descriptor.includeInPresetAll !== false)
487
+ .map((descriptor) => descriptor.id),
393
488
  lite: ['text', 'image', 'media'],
489
+ standard: [
490
+ 'word',
491
+ 'pdf',
492
+ 'ofd',
493
+ 'presentation-openxml',
494
+ 'spreadsheet',
495
+ 'archive',
496
+ 'email',
497
+ 'text',
498
+ 'image',
499
+ 'media'
500
+ ],
394
501
  office: ['pdf', 'word', 'spreadsheet', 'presentation', 'ofd', 'iwork', 'wordperfect', 'hangul'],
395
502
  engineering: [
396
503
  'cad',
@@ -419,6 +526,13 @@ const presetModules = {
419
526
  rendererIds: presetRendererIds.lite,
420
527
  chunkName: 'file-viewer-preset-lite'
421
528
  },
529
+ standard: {
530
+ id: 'standard',
531
+ packageName: '@file-viewer/preset-standard',
532
+ exportName: 'standardRenderers',
533
+ rendererIds: presetRendererIds.standard,
534
+ chunkName: 'file-viewer-preset-standard'
535
+ },
422
536
  office: {
423
537
  id: 'office',
424
538
  packageName: '@file-viewer/preset-office',
@@ -459,9 +573,11 @@ const fileViewerOptimizationExcludedPackages = [
459
573
  const cjsInteropPackages = [
460
574
  '@file-viewer/docx',
461
575
  '@xmldom/xmldom',
576
+ 'diff2html',
462
577
  'keynote-archives',
463
578
  'occt-import-js',
464
- 'jszip'
579
+ 'jszip',
580
+ 'pdf-lib'
465
581
  ];
466
582
  const defaultScanRoots = ['src', 'app', 'pages', 'components'];
467
583
  const defaultScanExtensions = [
@@ -947,6 +1063,9 @@ function createManualChunks(selection, autoPresetIds = []) {
947
1063
  });
948
1064
  return (id) => {
949
1065
  const normalized = id.replace(/\\/g, '/');
1066
+ if (normalized.endsWith('/dist/vendor/pdfjs/legacy/build/pdf.worker.mjs')) {
1067
+ return undefined;
1068
+ }
950
1069
  for (const [packageName, chunkName] of packageToChunk) {
951
1070
  if (normalized.includes(`/node_modules/${packageName}/`) ||
952
1071
  normalized.includes(`/node_modules/.pnpm/${packageName.replace('/', '+')}@`)) {
@@ -1107,6 +1226,22 @@ function collectDependencyAnchorPackages(selection, autoPresetIds = []) {
1107
1226
  ]);
1108
1227
  return unique([
1109
1228
  ...collectSelectedPackages(selection, autoPresetIds),
1229
+ ...presetIds.flatMap((presetId) => {
1230
+ const packageJsonPath = resolvePackageJson(presetModules[presetId].packageName);
1231
+ if (!packageJsonPath) {
1232
+ return [];
1233
+ }
1234
+ try {
1235
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
1236
+ return Object.keys({
1237
+ ...(packageJson.dependencies || {}),
1238
+ ...(packageJson.optionalDependencies || {})
1239
+ }).filter(packageName => packageName.startsWith('@file-viewer/capability-'));
1240
+ }
1241
+ catch {
1242
+ return [];
1243
+ }
1244
+ }),
1110
1245
  ...presetIds.flatMap((presetId) => presetModules[presetId].rendererIds
1111
1246
  .map((rendererId) => descriptorsById.get(rendererId)?.packageName)
1112
1247
  .filter((packageName) => Boolean(packageName))),
@@ -1351,7 +1486,7 @@ async function copyPdfCjkFontAssets(from, to) {
1351
1486
  async function copyKnownRendererAssets(targetRoot, rendererIds) {
1352
1487
  const selected = new Set(rendererIds);
1353
1488
  const results = [];
1354
- const push = async (rendererId, id, to, copyAction, reason, source) => {
1489
+ const push = async (rendererId, id, to, copyAction, reason, source, required) => {
1355
1490
  if (!selected.has(rendererId)) {
1356
1491
  return;
1357
1492
  }
@@ -1362,11 +1497,42 @@ async function copyKnownRendererAssets(targetRoot, rendererIds) {
1362
1497
  to,
1363
1498
  copied,
1364
1499
  reason: copied ? undefined : reason || 'source asset not found',
1500
+ required,
1365
1501
  ...source,
1366
1502
  });
1367
1503
  };
1368
- const pdfRoot = resolvePackageRoot('pdfjs-dist', ['@file-viewer/renderer-pdf']);
1369
- const pdfVersion = assertOwnedPackageVersion('pdfjs-dist', pdfRoot, '@file-viewer/renderer-pdf');
1504
+ const rendererPdfRoot = resolvePackageRoot('@file-viewer/renderer-pdf');
1505
+ const stagedPdfRoot = rendererPdfRoot
1506
+ ? join(rendererPdfRoot, 'dist/vendor/pdfjs')
1507
+ : null;
1508
+ const stagedPdfProvenancePath = stagedPdfRoot
1509
+ ? join(stagedPdfRoot, 'provenance.json')
1510
+ : null;
1511
+ let stagedPdfVersion = null;
1512
+ if (stagedPdfProvenancePath && existsSync(stagedPdfProvenancePath)) {
1513
+ try {
1514
+ const provenance = JSON.parse(readFileSync(stagedPdfProvenancePath, 'utf8'));
1515
+ if (provenance.packageName === 'pdfjs-dist' && provenance.version) {
1516
+ stagedPdfVersion = provenance.version;
1517
+ }
1518
+ }
1519
+ catch {
1520
+ stagedPdfVersion = null;
1521
+ }
1522
+ }
1523
+ const hasStagedPdfAssets = Boolean(stagedPdfRoot &&
1524
+ stagedPdfVersion &&
1525
+ existsSync(join(stagedPdfRoot, 'legacy/build/pdf.worker.mjs')) &&
1526
+ existsSync(join(stagedPdfRoot, 'cmaps')) &&
1527
+ existsSync(join(stagedPdfRoot, 'wasm')) &&
1528
+ existsSync(join(stagedPdfRoot, 'standard_fonts')));
1529
+ const dependencyPdfRoot = hasStagedPdfAssets
1530
+ ? null
1531
+ : resolvePackageRoot('pdfjs-dist', ['@file-viewer/renderer-pdf']);
1532
+ const pdfRoot = hasStagedPdfAssets ? stagedPdfRoot : dependencyPdfRoot;
1533
+ const pdfVersion = hasStagedPdfAssets
1534
+ ? stagedPdfVersion
1535
+ : assertOwnedPackageVersion('pdfjs-dist', dependencyPdfRoot, '@file-viewer/renderer-pdf');
1370
1536
  const pdfSource = pdfRoot
1371
1537
  ? { sourcePackage: 'pdfjs-dist', sourceVersion: pdfVersion || undefined }
1372
1538
  : undefined;
@@ -1374,13 +1540,45 @@ async function copyKnownRendererAssets(targetRoot, rendererIds) {
1374
1540
  await push('pdf', 'pdf-cmaps', join(targetRoot, 'vendor/pdf/cmaps'), () => copyDirectoryIfPresent(pdfRoot ? join(pdfRoot, 'cmaps') : null, join(targetRoot, 'vendor/pdf/cmaps')), undefined, pdfSource);
1375
1541
  await push('pdf', 'pdf-wasm', join(targetRoot, 'vendor/pdf/wasm'), () => copyDirectoryIfPresent(pdfRoot ? join(pdfRoot, 'wasm') : null, join(targetRoot, 'vendor/pdf/wasm')), undefined, pdfSource);
1376
1542
  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')), undefined, pdfSource);
1377
- const pdfCjkFontRoot = resolvePackageRoot('@fontsource-variable/noto-sans-sc', [
1543
+ const stagedPdfCjkFontRoot = rendererPdfRoot
1544
+ ? join(rendererPdfRoot, 'dist/vendor/noto-sans-sc')
1545
+ : null;
1546
+ const standardAssetsRoot = resolvePackageRoot('@file-viewer/assets-standard');
1547
+ const standardPdfCjkFontRoot = standardAssetsRoot
1548
+ ? join(standardAssetsRoot, 'viewer/vendor/pdf/fonts')
1549
+ : null;
1550
+ const dependencyPdfCjkFontRoot = resolvePackageRoot('@fontsource-variable/noto-sans-sc', [
1378
1551
  '@file-viewer/renderer-pdf'
1379
1552
  ]);
1380
- await push('pdf', 'pdf-cjk-font-fallback', join(targetRoot, 'vendor/pdf/fonts'), () => copyPdfCjkFontAssets(pdfCjkFontRoot, join(targetRoot, 'vendor/pdf/fonts')));
1381
- const pptxRoot = resolvePackageRoot('@file-viewer/pptx', ['@file-viewer/renderer-presentation']);
1553
+ const pdfCjkFontTarget = join(targetRoot, 'vendor/pdf/fonts');
1554
+ // The standard profile pack owns the CJK fallback font; presets that activate the pdf
1555
+ // renderer without that pack declare the font package directly. renderer-pdf must not
1556
+ // vendor a second copy, so when no source is installed the build warns instead of
1557
+ // failing and PDF rendering keeps using embedded fonts.
1558
+ const pdfCjkFontSourceAvailable = Boolean(standardPdfCjkFontRoot && existsSync(join(standardPdfCjkFontRoot, 'noto-sans-sc.css'))) ||
1559
+ Boolean(stagedPdfCjkFontRoot && existsSync(join(stagedPdfCjkFontRoot, 'wght.css'))) ||
1560
+ Boolean(dependencyPdfCjkFontRoot);
1561
+ await push('pdf', 'pdf-cjk-font-fallback', pdfCjkFontTarget, async () => {
1562
+ if (standardPdfCjkFontRoot &&
1563
+ existsSync(join(standardPdfCjkFontRoot, 'noto-sans-sc.css'))) {
1564
+ return copyDirectoryIfPresent(standardPdfCjkFontRoot, pdfCjkFontTarget);
1565
+ }
1566
+ if (stagedPdfCjkFontRoot && existsSync(join(stagedPdfCjkFontRoot, 'wght.css'))) {
1567
+ return copyPdfCjkFontAssets(stagedPdfCjkFontRoot, pdfCjkFontTarget);
1568
+ }
1569
+ return copyPdfCjkFontAssets(dependencyPdfCjkFontRoot, pdfCjkFontTarget);
1570
+ }, pdfCjkFontSourceAvailable
1571
+ ? undefined
1572
+ : `install ${independentlyOwnedAssetRendererIds.get('pdf')} or @fontsource-variable/noto-sans-sc`, undefined, pdfCjkFontSourceAvailable ? undefined : false);
1573
+ const pptxRoot = resolvePackageRoot('@file-viewer/pptx', [
1574
+ '@file-viewer/renderer-pptx',
1575
+ '@file-viewer/renderer-presentation'
1576
+ ]);
1382
1577
  await push('office-presentation', 'pptx-worker', join(targetRoot, 'vendor/pptx/pptx.worker.js'), () => copyFileIfPresent(pptxRoot ? join(pptxRoot, 'dist/worker/pptx.worker.js') : null, join(targetRoot, 'vendor/pptx/pptx.worker.js')));
1383
- const pptRoot = resolvePackageRoot('@file-viewer/ppt', ['@file-viewer/renderer-presentation']);
1578
+ const pptRoot = resolvePackageRoot('@file-viewer/ppt', [
1579
+ '@file-viewer/renderer-ppt',
1580
+ '@file-viewer/renderer-presentation'
1581
+ ]);
1384
1582
  const pptVersion = readPackageVersion(pptRoot);
1385
1583
  const pptSource = pptRoot
1386
1584
  ? { sourcePackage: '@file-viewer/ppt', sourceVersion: pptVersion || undefined }
@@ -1461,7 +1659,59 @@ async function copyKnownRendererAssets(targetRoot, rendererIds) {
1461
1659
  const archiveRoot = resolvePackageRoot('libarchive.js', ['@file-viewer/renderer-archive']);
1462
1660
  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')));
1463
1661
  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')));
1662
+ const chmRoot = resolvePackageRoot('@file-viewer/renderer-chm', ['@file-viewer/renderer-chm']);
1663
+ const chmSource = chmRoot
1664
+ ? { sourcePackage: '@file-viewer/renderer-chm', sourceVersion: readPackageVersion(chmRoot) || undefined }
1665
+ : undefined;
1666
+ for (const [id, sourceName, targetName] of [
1667
+ ['chm-worker', 'dist/chm.worker.js', 'chm.worker.js'],
1668
+ ['chm-wasm-module', 'dist/chm_wasm.js', 'chm_wasm.js'],
1669
+ ['chm-wasm', 'dist/chm_wasm_bg.wasm', 'chm_wasm_bg.wasm'],
1670
+ ['chm-license', 'LICENSE', 'LICENSE'],
1671
+ ['chm-third-party-notices', 'THIRD_PARTY_NOTICES.md', 'THIRD_PARTY_NOTICES.md'],
1672
+ ['chm-rust-notice', 'rust/NOTICE.md', 'RUST_NOTICE.md'],
1673
+ ['chm-rust-third-party-licenses', 'rust/THIRD_PARTY_LICENSES.md', 'RUST_THIRD_PARTY_LICENSES.md']
1674
+ ]) {
1675
+ await push('chm', id, join(targetRoot, `vendor/chm/${targetName}`), () => copyFileIfPresent(chmRoot ? join(chmRoot, sourceName) : null, join(targetRoot, `vendor/chm/${targetName}`)), undefined, chmSource);
1676
+ }
1464
1677
  const sqlJsRoot = resolvePackageRoot('sql.js', ['@file-viewer/renderer-data']);
1678
+ const designRendererRoot = resolvePackageRoot('@file-viewer/renderer-design');
1679
+ const introspectWasmRoot = resolvePackageRoot('@paged-media/introspect-wasm', [
1680
+ '@file-viewer/renderer-design'
1681
+ ]);
1682
+ const introspectWasmVersion = assertOwnedPackageVersion('@paged-media/introspect-wasm', introspectWasmRoot, '@file-viewer/renderer-design');
1683
+ const introspectWasmSource = introspectWasmRoot
1684
+ ? {
1685
+ sourcePackage: '@paged-media/introspect-wasm',
1686
+ sourceVersion: introspectWasmVersion || undefined
1687
+ }
1688
+ : undefined;
1689
+ await push('illustrator-pdf-design', 'illustrator-pgf-worker', join(targetRoot, 'vendor/design/illustrator-pgf.worker.js'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'dist/illustrator-pgf.worker.js') : null, join(targetRoot, 'vendor/design/illustrator-pgf.worker.js')));
1690
+ await push('photoshop-design', 'photoshop-design-worker', join(targetRoot, 'vendor/design/photoshop.worker.js'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'dist/photoshop.worker.js') : null, join(targetRoot, 'vendor/design/photoshop.worker.js')));
1691
+ await push('photoshop-resource-design', 'photoshop-resource-worker', join(targetRoot, 'vendor/design/adobe-resource.worker.js'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'dist/adobe-resource.worker.js') : null, join(targetRoot, 'vendor/design/adobe-resource.worker.js')));
1692
+ await push('adobe-palette-design', 'adobe-palette-container-worker', join(targetRoot, 'vendor/design/adobe-container.worker.js'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'dist/adobe-container.worker.js') : null, join(targetRoot, 'vendor/design/adobe-container.worker.js')));
1693
+ await push('indesign-exchange-design', 'indesign-exchange-container-worker', join(targetRoot, 'vendor/design/adobe-container.worker.js'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'dist/adobe-container.worker.js') : null, join(targetRoot, 'vendor/design/adobe-container.worker.js')));
1694
+ await push('adobe-animate-xfl-design', 'adobe-animate-xfl-container-worker', join(targetRoot, 'vendor/design/adobe-container.worker.js'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'dist/adobe-container.worker.js') : null, join(targetRoot, 'vendor/design/adobe-container.worker.js')));
1695
+ await push('adobe-xd-design', 'adobe-xd-container-worker', join(targetRoot, 'vendor/design/adobe-container.worker.js'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'dist/adobe-container.worker.js') : null, join(targetRoot, 'vendor/design/adobe-container.worker.js')));
1696
+ await push('indesign-native-design', 'indesign-native-container-worker', join(targetRoot, 'vendor/design/adobe-container.worker.js'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'dist/adobe-container.worker.js') : null, join(targetRoot, 'vendor/design/adobe-container.worker.js')));
1697
+ await push('postscript-design', 'postscript-design-worker', join(targetRoot, 'vendor/design/postscript.worker.js'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'dist/postscript.worker.js') : null, join(targetRoot, 'vendor/design/postscript.worker.js')));
1698
+ await push('postscript-design', 'postscript-design-wasm', join(targetRoot, 'vendor/design/stet_wasm_bg.wasm'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'runtime/postscript/stet_wasm_bg.wasm') : null, join(targetRoot, 'vendor/design/stet_wasm_bg.wasm')));
1699
+ const postscriptLicenses = [
1700
+ ['postscript-design-license-stet-apache', 'Stet-Apache-2.0.txt', 'LICENSE.stet-Apache-2.0.txt'],
1701
+ ['postscript-design-license-stet-mit', 'Stet-MIT.txt', 'LICENSE.stet-MIT.txt'],
1702
+ ['postscript-design-license-carlito-ofl', 'Carlito-OFL-1.1.txt', 'LICENSE.Carlito-OFL-1.1.txt'],
1703
+ ['postscript-design-license-tinos-ofl', 'Tinos-OFL-1.1.txt', 'LICENSE.Tinos-OFL-1.1.txt'],
1704
+ ['postscript-design-license-cousine-ofl', 'Cousine-OFL-1.1.txt', 'LICENSE.Cousine-OFL-1.1.txt'],
1705
+ ['postscript-design-license-noto-symbols-ofl', 'NotoSansSymbols2-OFL-1.1.txt', 'LICENSE.NotoSansSymbols2-OFL-1.1.txt']
1706
+ ];
1707
+ for (const [assetId, sourceName, targetName] of postscriptLicenses) {
1708
+ await push('postscript-design', assetId, join(targetRoot, 'vendor/design', targetName), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'LICENSES', sourceName) : null, join(targetRoot, 'vendor/design', targetName)));
1709
+ }
1710
+ await push('indesign-idml-design', 'indesign-idml-worker', join(targetRoot, 'vendor/design/idml.worker.js'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'dist/idml.worker.js') : null, join(targetRoot, 'vendor/design/idml.worker.js')));
1711
+ await push('indesign-idml-design', 'indesign-idml-wasm', join(targetRoot, 'vendor/design/paged_introspect_wasm_bg.wasm'), () => copyFileIfPresent(introspectWasmRoot
1712
+ ? join(introspectWasmRoot, 'paged_introspect_wasm_bg.wasm')
1713
+ : null, join(targetRoot, 'vendor/design/paged_introspect_wasm_bg.wasm')), undefined, introspectWasmSource);
1714
+ await push('indesign-idml-design', 'indesign-idml-license', join(targetRoot, 'vendor/design/LICENSE.introspect-wasm-MPL-2.0.txt'), () => copyFileIfPresent(designRendererRoot ? join(designRendererRoot, 'LICENSES/MPL-2.0.txt') : null, join(targetRoot, 'vendor/design/LICENSE.introspect-wasm-MPL-2.0.txt')));
1465
1715
  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')));
1466
1716
  await mkdir(targetRoot, { recursive: true });
1467
1717
  await writeFile(join(targetRoot, 'flyfish-viewer-assets.json'), `${JSON.stringify({
@@ -1473,6 +1723,151 @@ async function copyKnownRendererAssets(targetRoot, rendererIds) {
1473
1723
  }, null, 2)}\n`);
1474
1724
  return results;
1475
1725
  }
1726
+ const independentlyOwnedAssetRendererIds = new Map([
1727
+ ['archive', '@file-viewer/assets-standard'],
1728
+ ['chm', '@file-viewer/assets-chm'],
1729
+ ['pdf', '@file-viewer/assets-standard'],
1730
+ ['office-word-openxml', '@file-viewer/assets-standard'],
1731
+ ['office-presentation', '@file-viewer/assets-standard'],
1732
+ ['spreadsheet-openxml', '@file-viewer/assets-standard'],
1733
+ ['cad', '@file-viewer/assets-cad'],
1734
+ ['typst', '@file-viewer/assets-typst'],
1735
+ ['model', '@file-viewer/assets-model'],
1736
+ ['apple-pages', '@file-viewer/assets-iwork'],
1737
+ ['apple-numbers', '@file-viewer/assets-iwork'],
1738
+ ['apple-keynote', '@file-viewer/assets-iwork'],
1739
+ ['office-presentation-binary', '@file-viewer/assets-ppt'],
1740
+ ['office-hangul', '@file-viewer/assets-hangul'],
1741
+ ['office-wordperfect', '@file-viewer/assets-wordperfect'],
1742
+ ['data-asset', '@file-viewer/assets-data']
1743
+ ]);
1744
+ function resolveInstalledCapabilityAssetSources(installedAssetPackages, anchorPackages) {
1745
+ return installedAssetPackages.map((packageName) => {
1746
+ if (packageName === 'file-viewer-copy-assets') {
1747
+ throw new Error('[file-viewer:vite-plugin] The v2 aggregate asset carrier is forbidden in a v3 capability plan.');
1748
+ }
1749
+ const packageRoot = resolvePackageRoot(packageName, anchorPackages);
1750
+ if (!packageRoot)
1751
+ throw new Error(`[file-viewer:vite-plugin] Could not resolve installed asset owner ${packageName}.`);
1752
+ const packageVersion = readPackageVersion(packageRoot);
1753
+ if (!packageVersion)
1754
+ throw new Error(`[file-viewer:vite-plugin] Could not read ${packageName} version.`);
1755
+ const payloadRoot = join(packageRoot, 'viewer');
1756
+ const manifestPath = [
1757
+ join(payloadRoot, 'file-viewer-asset-pack.json'),
1758
+ join(payloadRoot, 'flyfish-viewer-assets.json')
1759
+ ].find(candidate => existsSync(candidate) && statSync(candidate).isFile());
1760
+ if (!manifestPath)
1761
+ throw new Error(`[file-viewer:vite-plugin] ${packageName} has no staged capability asset manifest.`);
1762
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
1763
+ if (manifest.schemaVersion !== 1 || !Array.isArray(manifest.rendererAssetManifests) ||
1764
+ manifest.packageName !== packageName || manifest.packageVersion !== packageVersion) {
1765
+ throw new Error(`[file-viewer:vite-plugin] ${packageName}@${packageVersion} has an invalid or stale asset manifest.`);
1766
+ }
1767
+ return { packageName, packageRoot, packageVersion, payloadRoot, manifest };
1768
+ });
1769
+ }
1770
+ async function copyAssetPathWithoutConflicts(sourceRoot, targetRoot, relativePath) {
1771
+ const normalized = relativePath.replace(/\\/g, '/').replace(/^\/+/, '');
1772
+ const source = resolve(sourceRoot, ...normalized.split('/'));
1773
+ const destination = resolve(targetRoot, ...normalized.split('/'));
1774
+ if (!isPathWithin(sourceRoot, source) || !isPathWithin(targetRoot, destination)) {
1775
+ throw new Error(`[file-viewer:vite-plugin] Asset path escapes its package or output root: ${relativePath}.`);
1776
+ }
1777
+ const sourceInfo = await lstat(source);
1778
+ if (sourceInfo.isSymbolicLink())
1779
+ throw new Error(`[file-viewer:vite-plugin] Asset source is a symbolic link: ${source}.`);
1780
+ if (sourceInfo.isDirectory()) {
1781
+ await mkdir(destination, { recursive: true });
1782
+ for (const entry of await readdir(source, { withFileTypes: true })) {
1783
+ if (entry.isSymbolicLink())
1784
+ throw new Error(`[file-viewer:vite-plugin] Asset source is a symbolic link: ${join(source, entry.name)}.`);
1785
+ await copyAssetPathWithoutConflicts(sourceRoot, targetRoot, `${normalized}/${entry.name}`);
1786
+ }
1787
+ return;
1788
+ }
1789
+ if (!sourceInfo.isFile())
1790
+ throw new Error(`[file-viewer:vite-plugin] Asset source is not a regular file: ${source}.`);
1791
+ await mkdir(dirname(destination), { recursive: true });
1792
+ if (existsSync(destination)) {
1793
+ const destinationInfo = await lstat(destination);
1794
+ if (destinationInfo.isSymbolicLink() || !destinationInfo.isFile()) {
1795
+ throw new Error(`[file-viewer:vite-plugin] Refusing unsafe asset destination ${destination}.`);
1796
+ }
1797
+ const [sourceContent, destinationContent] = await Promise.all([readFile(source), readFile(destination)]);
1798
+ if (!sourceContent.equals(destinationContent)) {
1799
+ throw new Error(`[file-viewer:vite-plugin] Asset owners conflict at ${normalized}.`);
1800
+ }
1801
+ return;
1802
+ }
1803
+ await copyFile(source, destination);
1804
+ }
1805
+ async function copyInstalledCapabilityAssetPacks(targetRoot, rendererIds, installedAssetPackages, anchorPackages) {
1806
+ const selected = new Set(rendererIds);
1807
+ const sources = resolveInstalledCapabilityAssetSources(installedAssetPackages, anchorPackages);
1808
+ const rendererOwners = new Map();
1809
+ const results = [];
1810
+ await mkdir(targetRoot, { recursive: true });
1811
+ for (const source of sources) {
1812
+ for (const rendererManifest of source.manifest.rendererAssetManifests || []) {
1813
+ if (!selected.has(rendererManifest.rendererId))
1814
+ continue;
1815
+ const existing = rendererOwners.get(rendererManifest.rendererId);
1816
+ if (existing) {
1817
+ throw new Error(`[file-viewer:vite-plugin] Renderer asset group ${rendererManifest.rendererId} is owned by both ` +
1818
+ `${existing.packageName} and ${source.packageName}.`);
1819
+ }
1820
+ rendererOwners.set(rendererManifest.rendererId, {
1821
+ packageName: source.packageName,
1822
+ manifest: rendererManifest
1823
+ });
1824
+ for (const asset of rendererManifest.assets || []) {
1825
+ if (asset.target !== 'public' || !asset.defaultPath)
1826
+ continue;
1827
+ const destination = resolve(targetRoot, ...asset.defaultPath.replace(/\\/g, '/').replace(/^\/+/, '').split('/'));
1828
+ try {
1829
+ await copyAssetPathWithoutConflicts(source.payloadRoot, targetRoot, asset.defaultPath);
1830
+ results.push({
1831
+ rendererId: asset.rendererId,
1832
+ id: asset.id,
1833
+ to: destination,
1834
+ copied: true,
1835
+ required: asset.required,
1836
+ sourcePackage: source.packageName,
1837
+ sourceVersion: source.packageVersion
1838
+ });
1839
+ }
1840
+ catch (error) {
1841
+ results.push({
1842
+ rendererId: asset.rendererId,
1843
+ id: asset.id,
1844
+ to: destination,
1845
+ copied: false,
1846
+ required: asset.required,
1847
+ reason: error.message,
1848
+ sourcePackage: source.packageName,
1849
+ sourceVersion: source.packageVersion
1850
+ });
1851
+ }
1852
+ }
1853
+ }
1854
+ }
1855
+ const manifests = [...rendererOwners]
1856
+ .sort(([left], [right]) => left.localeCompare(right))
1857
+ .map(([, value]) => value.manifest);
1858
+ await writeFile(join(targetRoot, 'flyfish-viewer-assets.json'), `${JSON.stringify({
1859
+ schemaVersion: 1,
1860
+ generatedBy: '@file-viewer/vite-plugin',
1861
+ rendererAssetManifests: manifests
1862
+ }, null, 2)}\n`);
1863
+ await writeFile(join(targetRoot, 'flyfish-viewer-manifest.json'), `${JSON.stringify({
1864
+ schemaVersion: 1,
1865
+ kind: 'file-viewer-capability-assets',
1866
+ owners: sources.map(source => ({ packageName: source.packageName, packageVersion: source.packageVersion }))
1867
+ .sort((left, right) => left.packageName.localeCompare(right.packageName))
1868
+ }, null, 2)}\n`);
1869
+ return { results, rendererIds: [...rendererOwners.keys()].sort() };
1870
+ }
1476
1871
  function matchesBundledAssetKind(kind, info) {
1477
1872
  return kind === 'directory' || kind === 'wasm-directory'
1478
1873
  ? info.isDirectory()
@@ -1621,6 +2016,25 @@ async function copyRendererAssets(target, rendererIds) {
1621
2016
  return bundledResults;
1622
2017
  }
1623
2018
  }
2019
+ if (target.installedAssetPackages.length) {
2020
+ const bundled = await copyInstalledCapabilityAssetPacks(target.targetRoot, rendererIds, target.installedAssetPackages, target.installedFullPackages);
2021
+ const covered = new Set(bundled.rendererIds);
2022
+ for (const rendererId of rendererIds) {
2023
+ const requiredOwner = independentlyOwnedAssetRendererIds.get(rendererId);
2024
+ if (requiredOwner && !covered.has(rendererId)) {
2025
+ bundled.results.push({
2026
+ rendererId,
2027
+ id: 'missing-independent-asset-owner',
2028
+ to: target.targetRoot,
2029
+ copied: false,
2030
+ // Specialist offline payloads stay opt-in; warn instead of failing the build.
2031
+ required: false,
2032
+ reason: `install ${requiredOwner} for this capability`
2033
+ });
2034
+ }
2035
+ }
2036
+ return bundled.results;
2037
+ }
1624
2038
  const results = await copyKnownRendererAssets(target.targetRoot, rendererIds);
1625
2039
  if (target.installedFullPackages.length) {
1626
2040
  results.push({
@@ -1672,6 +2086,25 @@ function resolveInstalledFullPackages(projectRoot) {
1672
2086
  return [];
1673
2087
  }
1674
2088
  }
2089
+ function resolveInstalledAssetPackages(projectRoot) {
2090
+ const packageJsonPath = findNearestProjectPackageJson(projectRoot);
2091
+ if (!packageJsonPath)
2092
+ return [];
2093
+ try {
2094
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
2095
+ const declared = unique([
2096
+ ...Object.keys(packageJson.dependencies || {}),
2097
+ ...Object.keys(packageJson.devDependencies || {}),
2098
+ ...Object.keys(packageJson.optionalDependencies || {})
2099
+ ]).filter(packageName => packageName.startsWith('@file-viewer/assets-'));
2100
+ const require = createRequire(packageJsonPath);
2101
+ const resolved = declared.filter(packageName => Boolean(tryResolvePackageJson(packageName, require)));
2102
+ return unique(resolved).sort();
2103
+ }
2104
+ catch {
2105
+ return [];
2106
+ }
2107
+ }
1675
2108
  function normalizeCopyAssetsBaseDir(value) {
1676
2109
  const normalized = value.trim().replace(/\\/g, '/');
1677
2110
  if (!normalized) {
@@ -1722,6 +2155,7 @@ export function resolveFileViewerCopyAssetsTarget(mode, value = true, config = {
1722
2155
  const projectRoot = resolve(config.projectRoot || process.cwd());
1723
2156
  const options = copyOptions(value);
1724
2157
  const installedFullPackages = resolveInstalledFullPackages(projectRoot);
2158
+ const installedAssetPackages = resolveInstalledAssetPackages(projectRoot);
1725
2159
  const baseDir = normalizeCopyAssetsBaseDir(typeof options.baseDir === 'string'
1726
2160
  ? options.baseDir
1727
2161
  : installedFullPackages.length
@@ -1739,7 +2173,8 @@ export function resolveFileViewerCopyAssetsTarget(mode, value = true, config = {
1739
2173
  outputRoot,
1740
2174
  targetRoot,
1741
2175
  baseDir,
1742
- installedFullPackages: [...installedFullPackages]
2176
+ installedFullPackages: [...installedFullPackages],
2177
+ installedAssetPackages
1743
2178
  };
1744
2179
  }
1745
2180
  function normalizeViteDevBasePath(base) {
@@ -1819,22 +2254,33 @@ function collectAssetRendererIds(selection, autoPresetIds = [], includeFullPrese
1819
2254
  ...selection.descriptors.flatMap((descriptor) => descriptor.rendererIds)
1820
2255
  ]);
1821
2256
  }
2257
+ const defaultRequiredAssetRendererIds = [
2258
+ 'pdf',
2259
+ 'office-word-openxml',
2260
+ 'office-presentation-binary',
2261
+ 'office-presentation',
2262
+ 'archive',
2263
+ 'cad',
2264
+ 'typst'
2265
+ ];
2266
+ function isRequiredAssetResult(result) {
2267
+ return result.required ?? defaultRequiredAssetRendererIds.includes(result.rendererId);
2268
+ }
1822
2269
  function reportAssetCopy(results, targetRoot, mode) {
1823
- const failedRequired = results.filter((result) => !result.copied && (result.required ?? [
1824
- 'pdf',
1825
- 'office-word-openxml',
1826
- 'office-presentation-binary',
1827
- 'office-presentation',
1828
- 'archive',
1829
- 'cad',
1830
- 'typst'
1831
- ].includes(result.rendererId)));
2270
+ const failedRequired = results.filter((result) => !result.copied && isRequiredAssetResult(result));
2271
+ const missingOptional = results.filter((result) => !result.copied && !isRequiredAssetResult(result));
1832
2272
  if (!results.length) {
1833
2273
  return;
1834
2274
  }
1835
2275
  const summary = `[file-viewer:vite-plugin] Copied ${results.filter((result) => result.copied).length}/${results.length} renderer assets to ${targetRoot}`;
2276
+ console.log(summary);
2277
+ if (missingOptional.length) {
2278
+ const optionalDetails = missingOptional
2279
+ .map((result) => ` - ${result.rendererId}:${result.id} -> ${result.to} (${result.reason || 'source asset not found'})`)
2280
+ .join('\n');
2281
+ console.warn(`[file-viewer:vite-plugin] Skipped optional offline assets; install the owning package to self-host them:\n${optionalDetails}`);
2282
+ }
1836
2283
  if (!failedRequired.length || mode === 'ignore') {
1837
- console.log(summary);
1838
2284
  return;
1839
2285
  }
1840
2286
  const details = failedRequired
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/vite-plugin",
3
- "version": "2.4.0",
3
+ "version": "3.0.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Vite plugin for File Viewer on-demand renderer assembly, chunk planning, and offline asset deployment.",
@@ -63,13 +63,14 @@
63
63
  }
64
64
  },
65
65
  "devDependencies": {
66
- "@types/node": "^25.9.2",
66
+ "@types/node": "^26.4.0",
67
67
  "typescript": "^6.0.3",
68
- "vite": "^8.0.16"
68
+ "vite": "^8.2.2"
69
69
  },
70
70
  "license": "Apache-2.0",
71
71
  "scripts": {
72
72
  "build": "tsc -b tsconfig.json",
73
- "type-check": "tsc -b tsconfig.json"
73
+ "type-check": "tsc -b tsconfig.json",
74
+ "verify:github-242": "node scripts/verify-github-242.mjs"
74
75
  }
75
76
  }