@file-viewer/vite-plugin 2.3.3 → 3.0.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/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'],
@@ -303,6 +306,46 @@ const rendererModules = [
303
306
  rendererIds: ['image'],
304
307
  chunkName: 'file-viewer-image'
305
308
  },
309
+ {
310
+ id: 'dicom',
311
+ packageName: '@file-viewer/renderer-dicom',
312
+ exportName: 'dicomRenderer',
313
+ formats: ['dicom', 'dcm'],
314
+ rendererIds: ['dicom'],
315
+ chunkName: 'file-viewer-dicom',
316
+ includeInPresetAll: false
317
+ },
318
+ {
319
+ id: 'signature',
320
+ packageName: '@file-viewer/renderer-signature',
321
+ exportName: 'signatureRenderer',
322
+ formats: [
323
+ 'p7m',
324
+ 'p7s',
325
+ 'p7c',
326
+ 'p7b',
327
+ 'pkcs7',
328
+ 'cms',
329
+ 'cmsc',
330
+ 'tsd',
331
+ 'tst',
332
+ 'tsq',
333
+ 'tsr',
334
+ 'asics',
335
+ 'scs',
336
+ 'asice',
337
+ 'sce',
338
+ 'ers',
339
+ 'asc',
340
+ 'sig',
341
+ 'pgp',
342
+ 'gpg',
343
+ 'jws'
344
+ ],
345
+ rendererIds: ['signature'],
346
+ chunkName: 'file-viewer-signature',
347
+ includeInPresetAll: false
348
+ },
306
349
  {
307
350
  id: 'media',
308
351
  packageName: '@file-viewer/renderer-media',
@@ -389,8 +432,22 @@ rendererModules.forEach((descriptor) => {
389
432
  descriptor.rendererIds.forEach((rendererId) => descriptorsByRendererId.set(rendererId, descriptor));
390
433
  });
391
434
  const presetRendererIds = {
392
- all: rendererModules.map((descriptor) => descriptor.id),
435
+ all: rendererModules
436
+ .filter((descriptor) => descriptor.includeInPresetAll !== false)
437
+ .map((descriptor) => descriptor.id),
393
438
  lite: ['text', 'image', 'media'],
439
+ standard: [
440
+ 'word',
441
+ 'pdf',
442
+ 'ofd',
443
+ 'presentation-openxml',
444
+ 'spreadsheet',
445
+ 'archive',
446
+ 'email',
447
+ 'text',
448
+ 'image',
449
+ 'media'
450
+ ],
394
451
  office: ['pdf', 'word', 'spreadsheet', 'presentation', 'ofd', 'iwork', 'wordperfect', 'hangul'],
395
452
  engineering: [
396
453
  'cad',
@@ -419,6 +476,13 @@ const presetModules = {
419
476
  rendererIds: presetRendererIds.lite,
420
477
  chunkName: 'file-viewer-preset-lite'
421
478
  },
479
+ standard: {
480
+ id: 'standard',
481
+ packageName: '@file-viewer/preset-standard',
482
+ exportName: 'standardRenderers',
483
+ rendererIds: presetRendererIds.standard,
484
+ chunkName: 'file-viewer-preset-standard'
485
+ },
422
486
  office: {
423
487
  id: 'office',
424
488
  packageName: '@file-viewer/preset-office',
@@ -461,7 +525,8 @@ const cjsInteropPackages = [
461
525
  '@xmldom/xmldom',
462
526
  'keynote-archives',
463
527
  'occt-import-js',
464
- 'jszip'
528
+ 'jszip',
529
+ 'pdf-lib'
465
530
  ];
466
531
  const defaultScanRoots = ['src', 'app', 'pages', 'components'];
467
532
  const defaultScanExtensions = [
@@ -1107,6 +1172,22 @@ function collectDependencyAnchorPackages(selection, autoPresetIds = []) {
1107
1172
  ]);
1108
1173
  return unique([
1109
1174
  ...collectSelectedPackages(selection, autoPresetIds),
1175
+ ...presetIds.flatMap((presetId) => {
1176
+ const packageJsonPath = resolvePackageJson(presetModules[presetId].packageName);
1177
+ if (!packageJsonPath) {
1178
+ return [];
1179
+ }
1180
+ try {
1181
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
1182
+ return Object.keys({
1183
+ ...(packageJson.dependencies || {}),
1184
+ ...(packageJson.optionalDependencies || {})
1185
+ }).filter(packageName => packageName.startsWith('@file-viewer/capability-'));
1186
+ }
1187
+ catch {
1188
+ return [];
1189
+ }
1190
+ }),
1110
1191
  ...presetIds.flatMap((presetId) => presetModules[presetId].rendererIds
1111
1192
  .map((rendererId) => descriptorsById.get(rendererId)?.packageName)
1112
1193
  .filter((packageName) => Boolean(packageName))),
@@ -1378,9 +1459,15 @@ async function copyKnownRendererAssets(targetRoot, rendererIds) {
1378
1459
  '@file-viewer/renderer-pdf'
1379
1460
  ]);
1380
1461
  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']);
1462
+ const pptxRoot = resolvePackageRoot('@file-viewer/pptx', [
1463
+ '@file-viewer/renderer-pptx',
1464
+ '@file-viewer/renderer-presentation'
1465
+ ]);
1382
1466
  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']);
1467
+ const pptRoot = resolvePackageRoot('@file-viewer/ppt', [
1468
+ '@file-viewer/renderer-ppt',
1469
+ '@file-viewer/renderer-presentation'
1470
+ ]);
1384
1471
  const pptVersion = readPackageVersion(pptRoot);
1385
1472
  const pptSource = pptRoot
1386
1473
  ? { sourcePackage: '@file-viewer/ppt', sourceVersion: pptVersion || undefined }
@@ -1473,6 +1560,150 @@ async function copyKnownRendererAssets(targetRoot, rendererIds) {
1473
1560
  }, null, 2)}\n`);
1474
1561
  return results;
1475
1562
  }
1563
+ const independentlyOwnedAssetRendererIds = new Map([
1564
+ ['archive', '@file-viewer/assets-standard'],
1565
+ ['pdf', '@file-viewer/assets-standard'],
1566
+ ['office-word-openxml', '@file-viewer/assets-standard'],
1567
+ ['office-presentation', '@file-viewer/assets-standard'],
1568
+ ['spreadsheet-openxml', '@file-viewer/assets-standard'],
1569
+ ['cad', '@file-viewer/assets-cad'],
1570
+ ['typst', '@file-viewer/assets-typst'],
1571
+ ['model', '@file-viewer/assets-model'],
1572
+ ['apple-pages', '@file-viewer/assets-iwork'],
1573
+ ['apple-numbers', '@file-viewer/assets-iwork'],
1574
+ ['apple-keynote', '@file-viewer/assets-iwork'],
1575
+ ['office-presentation-binary', '@file-viewer/assets-ppt'],
1576
+ ['office-hangul', '@file-viewer/assets-hangul'],
1577
+ ['office-wordperfect', '@file-viewer/assets-wordperfect'],
1578
+ ['data-asset', '@file-viewer/assets-data']
1579
+ ]);
1580
+ function resolveInstalledCapabilityAssetSources(installedAssetPackages, anchorPackages) {
1581
+ return installedAssetPackages.map((packageName) => {
1582
+ if (packageName === 'file-viewer-copy-assets') {
1583
+ throw new Error('[file-viewer:vite-plugin] The v2 aggregate asset carrier is forbidden in a v3 capability plan.');
1584
+ }
1585
+ const packageRoot = resolvePackageRoot(packageName, anchorPackages);
1586
+ if (!packageRoot)
1587
+ throw new Error(`[file-viewer:vite-plugin] Could not resolve installed asset owner ${packageName}.`);
1588
+ const packageVersion = readPackageVersion(packageRoot);
1589
+ if (!packageVersion)
1590
+ throw new Error(`[file-viewer:vite-plugin] Could not read ${packageName} version.`);
1591
+ const payloadRoot = join(packageRoot, 'viewer');
1592
+ const manifestPath = [
1593
+ join(payloadRoot, 'file-viewer-asset-pack.json'),
1594
+ join(payloadRoot, 'flyfish-viewer-assets.json')
1595
+ ].find(candidate => existsSync(candidate) && statSync(candidate).isFile());
1596
+ if (!manifestPath)
1597
+ throw new Error(`[file-viewer:vite-plugin] ${packageName} has no staged capability asset manifest.`);
1598
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
1599
+ if (manifest.schemaVersion !== 1 || !Array.isArray(manifest.rendererAssetManifests) ||
1600
+ manifest.packageName !== packageName || manifest.packageVersion !== packageVersion) {
1601
+ throw new Error(`[file-viewer:vite-plugin] ${packageName}@${packageVersion} has an invalid or stale asset manifest.`);
1602
+ }
1603
+ return { packageName, packageRoot, packageVersion, payloadRoot, manifest };
1604
+ });
1605
+ }
1606
+ async function copyAssetPathWithoutConflicts(sourceRoot, targetRoot, relativePath) {
1607
+ const normalized = relativePath.replace(/\\/g, '/').replace(/^\/+/, '');
1608
+ const source = resolve(sourceRoot, ...normalized.split('/'));
1609
+ const destination = resolve(targetRoot, ...normalized.split('/'));
1610
+ if (!isPathWithin(sourceRoot, source) || !isPathWithin(targetRoot, destination)) {
1611
+ throw new Error(`[file-viewer:vite-plugin] Asset path escapes its package or output root: ${relativePath}.`);
1612
+ }
1613
+ const sourceInfo = await lstat(source);
1614
+ if (sourceInfo.isSymbolicLink())
1615
+ throw new Error(`[file-viewer:vite-plugin] Asset source is a symbolic link: ${source}.`);
1616
+ if (sourceInfo.isDirectory()) {
1617
+ await mkdir(destination, { recursive: true });
1618
+ for (const entry of await readdir(source, { withFileTypes: true })) {
1619
+ if (entry.isSymbolicLink())
1620
+ throw new Error(`[file-viewer:vite-plugin] Asset source is a symbolic link: ${join(source, entry.name)}.`);
1621
+ await copyAssetPathWithoutConflicts(sourceRoot, targetRoot, `${normalized}/${entry.name}`);
1622
+ }
1623
+ return;
1624
+ }
1625
+ if (!sourceInfo.isFile())
1626
+ throw new Error(`[file-viewer:vite-plugin] Asset source is not a regular file: ${source}.`);
1627
+ await mkdir(dirname(destination), { recursive: true });
1628
+ if (existsSync(destination)) {
1629
+ const destinationInfo = await lstat(destination);
1630
+ if (destinationInfo.isSymbolicLink() || !destinationInfo.isFile()) {
1631
+ throw new Error(`[file-viewer:vite-plugin] Refusing unsafe asset destination ${destination}.`);
1632
+ }
1633
+ const [sourceContent, destinationContent] = await Promise.all([readFile(source), readFile(destination)]);
1634
+ if (!sourceContent.equals(destinationContent)) {
1635
+ throw new Error(`[file-viewer:vite-plugin] Asset owners conflict at ${normalized}.`);
1636
+ }
1637
+ return;
1638
+ }
1639
+ await copyFile(source, destination);
1640
+ }
1641
+ async function copyInstalledCapabilityAssetPacks(targetRoot, rendererIds, installedAssetPackages, anchorPackages) {
1642
+ const selected = new Set(rendererIds);
1643
+ const sources = resolveInstalledCapabilityAssetSources(installedAssetPackages, anchorPackages);
1644
+ const rendererOwners = new Map();
1645
+ const results = [];
1646
+ await mkdir(targetRoot, { recursive: true });
1647
+ for (const source of sources) {
1648
+ for (const rendererManifest of source.manifest.rendererAssetManifests || []) {
1649
+ if (!selected.has(rendererManifest.rendererId))
1650
+ continue;
1651
+ const existing = rendererOwners.get(rendererManifest.rendererId);
1652
+ if (existing) {
1653
+ throw new Error(`[file-viewer:vite-plugin] Renderer asset group ${rendererManifest.rendererId} is owned by both ` +
1654
+ `${existing.packageName} and ${source.packageName}.`);
1655
+ }
1656
+ rendererOwners.set(rendererManifest.rendererId, {
1657
+ packageName: source.packageName,
1658
+ manifest: rendererManifest
1659
+ });
1660
+ for (const asset of rendererManifest.assets || []) {
1661
+ if (asset.target !== 'public' || !asset.defaultPath)
1662
+ continue;
1663
+ const destination = resolve(targetRoot, ...asset.defaultPath.replace(/\\/g, '/').replace(/^\/+/, '').split('/'));
1664
+ try {
1665
+ await copyAssetPathWithoutConflicts(source.payloadRoot, targetRoot, asset.defaultPath);
1666
+ results.push({
1667
+ rendererId: asset.rendererId,
1668
+ id: asset.id,
1669
+ to: destination,
1670
+ copied: true,
1671
+ required: asset.required,
1672
+ sourcePackage: source.packageName,
1673
+ sourceVersion: source.packageVersion
1674
+ });
1675
+ }
1676
+ catch (error) {
1677
+ results.push({
1678
+ rendererId: asset.rendererId,
1679
+ id: asset.id,
1680
+ to: destination,
1681
+ copied: false,
1682
+ required: asset.required,
1683
+ reason: error.message,
1684
+ sourcePackage: source.packageName,
1685
+ sourceVersion: source.packageVersion
1686
+ });
1687
+ }
1688
+ }
1689
+ }
1690
+ }
1691
+ const manifests = [...rendererOwners]
1692
+ .sort(([left], [right]) => left.localeCompare(right))
1693
+ .map(([, value]) => value.manifest);
1694
+ await writeFile(join(targetRoot, 'flyfish-viewer-assets.json'), `${JSON.stringify({
1695
+ schemaVersion: 1,
1696
+ generatedBy: '@file-viewer/vite-plugin',
1697
+ rendererAssetManifests: manifests
1698
+ }, null, 2)}\n`);
1699
+ await writeFile(join(targetRoot, 'flyfish-viewer-manifest.json'), `${JSON.stringify({
1700
+ schemaVersion: 1,
1701
+ kind: 'file-viewer-capability-assets',
1702
+ owners: sources.map(source => ({ packageName: source.packageName, packageVersion: source.packageVersion }))
1703
+ .sort((left, right) => left.packageName.localeCompare(right.packageName))
1704
+ }, null, 2)}\n`);
1705
+ return { results, rendererIds: [...rendererOwners.keys()].sort() };
1706
+ }
1476
1707
  function matchesBundledAssetKind(kind, info) {
1477
1708
  return kind === 'directory' || kind === 'wasm-directory'
1478
1709
  ? info.isDirectory()
@@ -1621,6 +1852,24 @@ async function copyRendererAssets(target, rendererIds) {
1621
1852
  return bundledResults;
1622
1853
  }
1623
1854
  }
1855
+ if (target.installedAssetPackages.length) {
1856
+ const bundled = await copyInstalledCapabilityAssetPacks(target.targetRoot, rendererIds, target.installedAssetPackages, target.installedFullPackages);
1857
+ const covered = new Set(bundled.rendererIds);
1858
+ for (const rendererId of rendererIds) {
1859
+ const requiredOwner = independentlyOwnedAssetRendererIds.get(rendererId);
1860
+ if (requiredOwner && !covered.has(rendererId)) {
1861
+ bundled.results.push({
1862
+ rendererId,
1863
+ id: 'missing-independent-asset-owner',
1864
+ to: target.targetRoot,
1865
+ copied: false,
1866
+ required: true,
1867
+ reason: `install ${requiredOwner} for this capability`
1868
+ });
1869
+ }
1870
+ }
1871
+ return bundled.results;
1872
+ }
1624
1873
  const results = await copyKnownRendererAssets(target.targetRoot, rendererIds);
1625
1874
  if (target.installedFullPackages.length) {
1626
1875
  results.push({
@@ -1672,6 +1921,25 @@ function resolveInstalledFullPackages(projectRoot) {
1672
1921
  return [];
1673
1922
  }
1674
1923
  }
1924
+ function resolveInstalledAssetPackages(projectRoot) {
1925
+ const packageJsonPath = findNearestProjectPackageJson(projectRoot);
1926
+ if (!packageJsonPath)
1927
+ return [];
1928
+ try {
1929
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
1930
+ const declared = unique([
1931
+ ...Object.keys(packageJson.dependencies || {}),
1932
+ ...Object.keys(packageJson.devDependencies || {}),
1933
+ ...Object.keys(packageJson.optionalDependencies || {})
1934
+ ]).filter(packageName => packageName.startsWith('@file-viewer/assets-'));
1935
+ const require = createRequire(packageJsonPath);
1936
+ const resolved = declared.filter(packageName => Boolean(tryResolvePackageJson(packageName, require)));
1937
+ return unique(resolved).sort();
1938
+ }
1939
+ catch {
1940
+ return [];
1941
+ }
1942
+ }
1675
1943
  function normalizeCopyAssetsBaseDir(value) {
1676
1944
  const normalized = value.trim().replace(/\\/g, '/');
1677
1945
  if (!normalized) {
@@ -1722,6 +1990,7 @@ export function resolveFileViewerCopyAssetsTarget(mode, value = true, config = {
1722
1990
  const projectRoot = resolve(config.projectRoot || process.cwd());
1723
1991
  const options = copyOptions(value);
1724
1992
  const installedFullPackages = resolveInstalledFullPackages(projectRoot);
1993
+ const installedAssetPackages = resolveInstalledAssetPackages(projectRoot);
1725
1994
  const baseDir = normalizeCopyAssetsBaseDir(typeof options.baseDir === 'string'
1726
1995
  ? options.baseDir
1727
1996
  : installedFullPackages.length
@@ -1739,7 +2008,8 @@ export function resolveFileViewerCopyAssetsTarget(mode, value = true, config = {
1739
2008
  outputRoot,
1740
2009
  targetRoot,
1741
2010
  baseDir,
1742
- installedFullPackages: [...installedFullPackages]
2011
+ installedFullPackages: [...installedFullPackages],
2012
+ installedAssetPackages
1743
2013
  };
1744
2014
  }
1745
2015
  function normalizeViteDevBasePath(base) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/vite-plugin",
3
- "version": "2.3.3",
3
+ "version": "3.0.0",
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.",