@file-viewer/vite-plugin 2.1.29 → 2.1.30
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/README.en.md +13 -9
- package/README.md +13 -9
- package/dist/index.d.ts +28 -0
- package/dist/index.js +462 -69
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# @file-viewer/vite-plugin
|
|
2
2
|
|
|
3
|
-
Vite plugin for Flyfish File Viewer on-demand renderer assembly. It can auto-discover installed `@file-viewer/preset-*`
|
|
3
|
+
Vite plugin for Flyfish File Viewer on-demand renderer assembly. It can auto-discover installed `@file-viewer/preset-*` and `@file-viewer/*-full` packages, so Vue, React, Svelte, jQuery, and Vanilla JS components receive matching preview capability without application code passing `renderers` manually. It can also generate `virtual:file-viewer-renderers` from declared formats, import only matching renderer packages, and provide renderer-oriented chunk planning plus offline worker/WASM/font asset copying.
|
|
4
4
|
|
|
5
5
|
## Fastest Start
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
For complete format support, the fastest path is one full package and one Vite plugin registration. Full packages already include `preset-all`; do not install another preset. `copyAssets:true` matches the installed full package and publishes its Worker, WASM, font, and vendor files under `file-viewer/` at the deployment base (`/file-viewer/` for a root deployment) in both dev and build:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
pnpm add @file-viewer/vue3
|
|
10
|
+
pnpm add @file-viewer/vue3-full
|
|
11
11
|
pnpm add -D @file-viewer/vite-plugin
|
|
12
12
|
```
|
|
13
13
|
|
|
@@ -24,15 +24,17 @@ export default defineConfig({
|
|
|
24
24
|
})
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Installing a full package without publishing these assets leaves PDF Worker, CAD/Typst/Archive WASM, and other formats without runtime files. That is partial support, not a complete setup.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
For a smaller install, use a standard component with one preset and keep the same Vite config. The plugin auto-discovers installed presets, so application code does not hand-write renderer imports and the plugin does not need `preset:'office'`:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
pnpm add @file-viewer/vue3 @file-viewer/preset-
|
|
32
|
+
pnpm add @file-viewer/vue3 @file-viewer/preset-office
|
|
33
33
|
pnpm add -D @file-viewer/vite-plugin
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
Components keep `autoRenderers:true` by default, so `@file-viewer/vue3`, `@file-viewer/react`, `@file-viewer/web`, `@file-viewer/svelte`, `@file-viewer/jquery`, `@file-viewer/vue2.7`, and `@file-viewer/vue2.6` automatically consume the injected preset / renderer registry.
|
|
37
|
+
|
|
36
38
|
Available presets:
|
|
37
39
|
|
|
38
40
|
- `@file-viewer/preset-lite`: text, Markdown, code, image, audio, and video.
|
|
@@ -44,7 +46,7 @@ Install individual renderer packages only for strict cuts, such as `@file-viewer
|
|
|
44
46
|
|
|
45
47
|
## vite.config.ts
|
|
46
48
|
|
|
47
|
-
Prefer preset auto assembly for normal products. With
|
|
49
|
+
Prefer full/preset auto assembly for normal products. With only `copyAssets:true`, the plugin auto-discovers installed `@file-viewer/*-full` or `@file-viewer/preset-*` packages:
|
|
48
50
|
|
|
49
51
|
```ts
|
|
50
52
|
fileViewerRenderers({
|
|
@@ -83,7 +85,7 @@ const options = {
|
|
|
83
85
|
|
|
84
86
|
| Option | Description |
|
|
85
87
|
| --- | --- |
|
|
86
|
-
| `copyAssets` | `true` or `{ publicDir, outDir, mode }`; copies
|
|
88
|
+
| `copyAssets` | `true` or `{ publicDir, outDir, mode, baseDir }`; copies matching Worker, WASM, font, and vendor assets. Installed full packages default to `<deployment-base>/file-viewer/`, which is required for complete full support; standard packages/presets keep the existing root behavior |
|
|
87
89
|
| `preset` | `'auto' | 'lite' | 'office' | 'engineering' | 'all'`; with no explicit formats, installed presets are discovered automatically |
|
|
88
90
|
| `autoPresets` | `true` or a preset list; useful with `scan:true` when installed presets should stay active |
|
|
89
91
|
| `formats` | File extensions or format tokens, for example `['pdf', 'docx', 'dwg']` |
|
|
@@ -96,6 +98,8 @@ const options = {
|
|
|
96
98
|
|
|
97
99
|
When host apps split `node_modules` by package name, circular dependencies between `@codemirror/*`, `@lezer/*`, and Sandpack can show up in production as `codemirror-view.* Cannot access ... before initialization`. The plugin wraps existing `manualChunks` functions by default, stabilizes only those known interop chunks, and preserves the rest of the app's custom grouping. Set `stabilizeInteropChunks:false` only when the host build needs total control.
|
|
98
100
|
|
|
101
|
+
`copyAssets.baseDir` overrides automatic placement. It must be relative and cannot contain `..`; an empty string explicitly selects the root. The plugin also updates the full package runtime asset base. A manual `setDefaultFullAssetBaseUrl()` call is only needed when a custom `baseDir` is combined with `inject:false`. With Vite `base:'/app/'`, full-package dev URLs start with `/app/file-viewer/...`, while production files remain under `outDir/file-viewer/`.
|
|
102
|
+
|
|
99
103
|
For strict registry control, disable injection and pass the virtual module explicitly:
|
|
100
104
|
|
|
101
105
|
```ts
|
|
@@ -145,7 +149,7 @@ When a file extension is in the supported matrix but the current project has not
|
|
|
145
149
|
|
|
146
150
|
## Current Boundary
|
|
147
151
|
|
|
148
|
-
The plugin currently generates imports for extracted renderer packages: Word, Spreadsheet, PDF, OFD, Presentation, CAD, Draw.io/Excalidraw/Mermaid/PlantUML, 3D, Data, EDA, Typst, archives, email, EPUB, code/Markdown/Patch/Git Bundle, image, media, XMind, and Geo. Declare them explicitly with `formats`, or let `scan: true` discover source hints automatically; core-supported extensions such as `.zipx`, `.cbz`, `.tiff`, `.mjs`, `.gv`, `.patch`, `.bundle`, `.mermaid`, `.puml`, and `.mpeg` also resolve to their renderer packages. With `copyAssets:true`, the plugin
|
|
152
|
+
The plugin currently generates imports for extracted renderer packages: Word, Spreadsheet, PDF, OFD, Presentation, CAD, Draw.io/Excalidraw/Mermaid/PlantUML, 3D, Data, EDA, Typst, archives, email, EPUB, code/Markdown/Patch/Git Bundle, image, media, XMind, and Geo. Declare them explicitly with `formats`, or let `scan: true` discover source hints automatically; core-supported extensions such as `.zipx`, `.cbz`, `.tiff`, `.mjs`, `.gv`, `.patch`, `.bundle`, `.mermaid`, `.puml`, and `.mpeg` also resolve to their renderer packages. With `copyAssets:true`, the plugin detects installed capability packages. Full packages copy matching assets into dev `publicDir/file-viewer/` and production `outDir/file-viewer/`; standard packages/presets keep the existing root placement, including the Typst compiler / renderer WASM files and default fonts. `preset: 'auto' | 'lite' | 'office' | 'engineering' | 'all'` imports matching `@file-viewer/preset-*` packages; when `formats` are also present, the plugin adds extra renderers outside the preset.
|
|
149
153
|
|
|
150
154
|
## Documentation
|
|
151
155
|
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# @file-viewer/vite-plugin
|
|
2
2
|
|
|
3
|
-
Flyfish File Viewer 的 Vite 按需 renderer 自动装配插件。它可以自动发现已安装的 `@file-viewer/preset-*`
|
|
3
|
+
Flyfish File Viewer 的 Vite 按需 renderer 自动装配插件。它可以自动发现已安装的 `@file-viewer/preset-*` 和 `@file-viewer/*-full` 包,让 Vue、React、Svelte、jQuery 和 Vanilla JS 组件无需手动传 `renderers` 就获得对应预览能力;也可以根据业务声明的文件格式生成 `virtual:file-viewer-renderers`,只 import 命中的 renderer 包,并提供 chunk 分组和离线 worker/WASM/字体资源复制能力。
|
|
4
4
|
|
|
5
5
|
## 最快开始
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
需要完整格式支持时,最快路径是“full 包 + 注册一次 Vite 插件”。full 已内置 `preset-all`,不要再安装 preset;`copyAssets:true` 会在开发和构建时自动匹配 full 包,并把 Worker、WASM、字体和 vendor 文件发布到部署基址下的 `file-viewer/`(根部署即 `/file-viewer/`):
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
pnpm add @file-viewer/vue3
|
|
10
|
+
pnpm add @file-viewer/vue3-full
|
|
11
11
|
pnpm add -D @file-viewer/vite-plugin
|
|
12
12
|
```
|
|
13
13
|
|
|
@@ -24,15 +24,17 @@ export default defineConfig({
|
|
|
24
24
|
})
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
只安装 full 包而不启用资源复制,PDF Worker、CAD/Typst/Archive WASM 等格式会缺少运行文件,只能获得部分格式支持。
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
需要更小体积时,改用“标准组件包 + 一个 preset”,Vite 配置保持不变。插件会自动发现已安装的 preset,无需手写 renderer import,也无需显式写 `preset:'office'`:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
pnpm add @file-viewer/vue3 @file-viewer/preset-
|
|
32
|
+
pnpm add @file-viewer/vue3 @file-viewer/preset-office
|
|
33
33
|
pnpm add -D @file-viewer/vite-plugin
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
组件默认 `autoRenderers:true`,会读取 Vite 插件注入的 preset / renderer registry。常规业务代码只要使用 `@file-viewer/vue3`、`@file-viewer/react`、`@file-viewer/web`、`@file-viewer/svelte`、`@file-viewer/jquery`、`@file-viewer/vue2.7` 或 `@file-viewer/vue2.6` 即可获得对应能力。
|
|
37
|
+
|
|
36
38
|
可选 preset:
|
|
37
39
|
|
|
38
40
|
- `@file-viewer/preset-lite`: 文本、Markdown、代码、图片、音频、视频。
|
|
@@ -44,7 +46,7 @@ pnpm add -D @file-viewer/vite-plugin
|
|
|
44
46
|
|
|
45
47
|
## vite.config.ts
|
|
46
48
|
|
|
47
|
-
推荐默认使用 preset
|
|
49
|
+
推荐默认使用 full/preset 自动装配。只传 `copyAssets:true` 时,插件会自动发现已安装的 `@file-viewer/*-full` 或 `@file-viewer/preset-*`,无需写 `preset:'all'`:
|
|
48
50
|
|
|
49
51
|
```ts
|
|
50
52
|
fileViewerRenderers({
|
|
@@ -83,7 +85,7 @@ const options = {
|
|
|
83
85
|
|
|
84
86
|
| 选项 | 说明 |
|
|
85
87
|
| --- | --- |
|
|
86
|
-
| `copyAssets` | `true` 或 `{ publicDir, outDir, mode }
|
|
88
|
+
| `copyAssets` | `true` 或 `{ publicDir, outDir, mode, baseDir }`;复制匹配的 Worker、WASM、字体和 vendor 资源。检测到 full 时默认发布到 `<部署基址>/file-viewer/`,这是 full 完整格式支持的必需项;标准包/preset 保持原有根目录行为 |
|
|
87
89
|
| `preset` | `'auto' | 'lite' | 'office' | 'engineering' | 'all'`;默认无显式格式时自动发现已安装 preset |
|
|
88
90
|
| `autoPresets` | `true` 或 preset 列表;常用于 `scan:true` 时继续自动激活已安装 preset |
|
|
89
91
|
| `formats` | 文件扩展名或格式 token,例如 `['pdf', 'docx', 'dwg']` |
|
|
@@ -96,6 +98,8 @@ const options = {
|
|
|
96
98
|
|
|
97
99
|
如果宿主项目按包名拆分 `node_modules`,`@codemirror/*`、`@lezer/*` 和 Sandpack 之间的循环依赖可能在生产构建中表现为 `codemirror-view.* Cannot access ... before initialization`。插件默认会包裹已有的 `manualChunks` 函数,只稳定这些已知互操作 chunk,保留其它自定义分组;确实需要完全接管时可设置 `stabilizeInteropChunks:false`。
|
|
98
100
|
|
|
101
|
+
`copyAssets.baseDir` 可以覆盖自动目录:必须是相对路径,不能包含 `..`;传空字符串表示显式复制到根目录。插件会同步 full 包的运行时资源根,无需再手动调用 `setDefaultFullAssetBaseUrl()`;同时自定义 `baseDir` 并设置 `inject:false` 时,才需由业务自行设置。Vite 配置了 `base:'/app/'` 时,full 包开发 URL 为 `/app/file-viewer/...`,构建文件仍位于 `outDir/file-viewer/`。
|
|
102
|
+
|
|
99
103
|
如果你需要严格控制 registry,可以关闭注入并手动传入:
|
|
100
104
|
|
|
101
105
|
```ts
|
|
@@ -145,7 +149,7 @@ export const fileViewerFormats = ['pdf', 'docx', 'xlsx']
|
|
|
145
149
|
|
|
146
150
|
## 当前边界
|
|
147
151
|
|
|
148
|
-
当前插件会为已经拆出的 renderer 包生成导入:Word、Spreadsheet、PDF、OFD、Presentation、CAD、Draw.io/Excalidraw/Mermaid/PlantUML、3D、Data、EDA、Typst、压缩包、邮件、EPUB、代码/Markdown/Patch/Git Bundle、图片、媒体、XMind 和 Geo。可以通过 `formats` 显式声明,也可以通过 `scan: true` 从源码 hint 自动发现;`.zipx`、`.cbz`、`.tiff`、`.mjs`、`.gv`、`.patch`、`.bundle`、`.mermaid`、`.puml`、`.mpeg` 等 core 支持的扩展也会映射到对应 renderer。开启 `copyAssets:true`
|
|
152
|
+
当前插件会为已经拆出的 renderer 包生成导入:Word、Spreadsheet、PDF、OFD、Presentation、CAD、Draw.io/Excalidraw/Mermaid/PlantUML、3D、Data、EDA、Typst、压缩包、邮件、EPUB、代码/Markdown/Patch/Git Bundle、图片、媒体、XMind 和 Geo。可以通过 `formats` 显式声明,也可以通过 `scan: true` 从源码 hint 自动发现;`.zipx`、`.cbz`、`.tiff`、`.mjs`、`.gv`、`.patch`、`.bundle`、`.mermaid`、`.puml`、`.mpeg` 等 core 支持的扩展也会映射到对应 renderer。开启 `copyAssets:true` 时会识别已安装能力;full 包把匹配资源复制到开发 `publicDir/file-viewer/` 与构建 `outDir/file-viewer/`,标准包/preset 维持根目录行为,包括 Typst compiler / renderer WASM 与默认字体目录。`preset: 'auto' | 'lite' | 'office' | 'engineering' | 'all'` 会导入对应 `@file-viewer/preset-*` 包;如果同时声明 `formats`,插件会在 preset 之外补充额外 renderer。
|
|
149
153
|
|
|
150
154
|
## 文档
|
|
151
155
|
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,14 @@ export interface FileViewerCopyAssetsOptions {
|
|
|
31
31
|
* Directory used after production build. Defaults to build.outDir.
|
|
32
32
|
*/
|
|
33
33
|
outDir?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Directory below publicDir/outDir where assets are published. When omitted,
|
|
36
|
+
* projects with an installed @file-viewer/*-full package use `file-viewer`;
|
|
37
|
+
* other projects keep the existing output-root layout. Use an empty string
|
|
38
|
+
* to explicitly publish at the output root. For full packages, the injected
|
|
39
|
+
* runtime module keeps the default asset base aligned with this directory.
|
|
40
|
+
*/
|
|
41
|
+
baseDir?: string;
|
|
34
42
|
/**
|
|
35
43
|
* Copy during dev server startup, build closeBundle, or both.
|
|
36
44
|
*/
|
|
@@ -100,6 +108,26 @@ interface MissingRendererNotice {
|
|
|
100
108
|
}
|
|
101
109
|
export declare function extractFileViewerRendererHintTokens(source: string): string[];
|
|
102
110
|
export declare function collectFileViewerRendererScanTokens(projectRoot: string, scan: FileViewerRenderersPluginOptions['scan']): string[];
|
|
111
|
+
export interface FileViewerCopyAssetsTargetConfig {
|
|
112
|
+
/** Vite project root. Defaults to process.cwd(). */
|
|
113
|
+
projectRoot?: string;
|
|
114
|
+
/** Resolved Vite publicDir. Defaults to `public`. */
|
|
115
|
+
publicDir?: string;
|
|
116
|
+
/** Resolved Vite build.outDir. Defaults to `dist`. */
|
|
117
|
+
outDir?: string;
|
|
118
|
+
}
|
|
119
|
+
export interface FileViewerCopyAssetsTarget {
|
|
120
|
+
mode: 'dev' | 'build';
|
|
121
|
+
outputRoot: string;
|
|
122
|
+
targetRoot: string;
|
|
123
|
+
baseDir: string;
|
|
124
|
+
installedFullPackages: string[];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolves the exact directory used by copyAssets without running a Vite build.
|
|
128
|
+
* This is exported so integrations can validate their deployment contract.
|
|
129
|
+
*/
|
|
130
|
+
export declare function resolveFileViewerCopyAssetsTarget(mode: 'dev' | 'build', value?: true | FileViewerCopyAssetsOptions, config?: FileViewerCopyAssetsTargetConfig): FileViewerCopyAssetsTarget;
|
|
103
131
|
export declare function fileViewerRenderers(options?: FileViewerRenderersPluginOptions): Plugin;
|
|
104
132
|
export declare function createFileViewerManualChunks(options?: FileViewerRenderersPluginOptions): (id: string) => string | undefined;
|
|
105
133
|
export declare function resolveFileViewerRendererSelection(options?: FileViewerRenderersPluginOptions, projectRoot?: string): {
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
-
import { copyFile, cp, mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { copyFile, cp, mkdir, readFile, realpath, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
|
-
import { dirname, extname, isAbsolute, join, resolve, sep } from 'node:path';
|
|
4
|
+
import { dirname, extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
5
5
|
const virtualModuleId = 'virtual:file-viewer-renderers';
|
|
6
6
|
const resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
7
7
|
const pluginRequire = createRequire(import.meta.url);
|
|
@@ -384,6 +384,16 @@ const presetModules = {
|
|
|
384
384
|
chunkName: 'file-viewer-preset-engineering'
|
|
385
385
|
}
|
|
386
386
|
};
|
|
387
|
+
const fileViewerFullPackages = [
|
|
388
|
+
'@file-viewer/react-full',
|
|
389
|
+
'@file-viewer/react-legacy-full',
|
|
390
|
+
'@file-viewer/vue3-full',
|
|
391
|
+
'@file-viewer/vue2.7-full',
|
|
392
|
+
'@file-viewer/vue2.6-full',
|
|
393
|
+
'@file-viewer/web-full',
|
|
394
|
+
'@file-viewer/jquery-full',
|
|
395
|
+
'@file-viewer/svelte-full'
|
|
396
|
+
];
|
|
387
397
|
// Vite dep optimization rewrites import.meta.url into node_modules/.vite/deps
|
|
388
398
|
// and can trigger full-preset dev-server re-optimization reloads. Keep File
|
|
389
399
|
// Viewer packages out of dep optimization while still optimizing their
|
|
@@ -393,14 +403,7 @@ const fileViewerOptimizationExcludedPackages = [
|
|
|
393
403
|
'@file-viewer/pptx',
|
|
394
404
|
...rendererModules.map((descriptor) => descriptor.packageName),
|
|
395
405
|
...Object.values(presetModules).map((preset) => preset.packageName),
|
|
396
|
-
|
|
397
|
-
'@file-viewer/react-legacy-full',
|
|
398
|
-
'@file-viewer/vue3-full',
|
|
399
|
-
'@file-viewer/vue2.7-full',
|
|
400
|
-
'@file-viewer/vue2.6-full',
|
|
401
|
-
'@file-viewer/web-full',
|
|
402
|
-
'@file-viewer/jquery-full',
|
|
403
|
-
'@file-viewer/svelte-full'
|
|
406
|
+
...fileViewerFullPackages
|
|
404
407
|
];
|
|
405
408
|
const cjsInteropPackages = [
|
|
406
409
|
'@file-viewer/docx',
|
|
@@ -632,7 +635,7 @@ function expandDescriptorRendererIds(ids) {
|
|
|
632
635
|
return descriptor ? [...descriptor.rendererIds] : [id];
|
|
633
636
|
}));
|
|
634
637
|
}
|
|
635
|
-
function renderVirtualModule(selection, formats, autoPresetIds = []) {
|
|
638
|
+
function renderVirtualModule(selection, formats, autoPresetIds = [], fullAssetBase = null) {
|
|
636
639
|
const presetModule = selection.preset ? presetModules[selection.preset] : null;
|
|
637
640
|
const autoPresetModules = autoPresetIds
|
|
638
641
|
.filter((id) => id !== selection.preset)
|
|
@@ -672,15 +675,38 @@ function renderVirtualModule(selection, formats, autoPresetIds = []) {
|
|
|
672
675
|
: !selection.preset && autoPresetModules.length === 1
|
|
673
676
|
? autoPresetModules[0].id
|
|
674
677
|
: '@file-viewer/vite-plugin:configured';
|
|
678
|
+
const fullAssetBaseImport = fullAssetBase
|
|
679
|
+
? fullAssetBase.kind === 'deployment'
|
|
680
|
+
? `import { DEFAULT_FULL_ASSET_BASE_PATH, resolveDefaultFullAssetBaseUrl, setDefaultFullAssetBaseUrl } from '@file-viewer/preset-all';`
|
|
681
|
+
: `import { setDefaultFullAssetBaseUrl } from '@file-viewer/preset-all';`
|
|
682
|
+
: null;
|
|
683
|
+
const autoRegistrationImport = rendererNames.length
|
|
684
|
+
? `import { registerFileViewerAutoRendererPreset } from '@file-viewer/core';`
|
|
685
|
+
: null;
|
|
686
|
+
const fullAssetBaseSetup = !fullAssetBase
|
|
687
|
+
? []
|
|
688
|
+
: fullAssetBase.kind === 'literal'
|
|
689
|
+
? [`setDefaultFullAssetBaseUrl(${JSON.stringify(fullAssetBase.url)});`, '']
|
|
690
|
+
: [
|
|
691
|
+
'const fileViewerDefaultFullAssetBaseUrl = resolveDefaultFullAssetBaseUrl();',
|
|
692
|
+
'const fileViewerDeploymentBaseUrl = fileViewerDefaultFullAssetBaseUrl.endsWith(DEFAULT_FULL_ASSET_BASE_PATH)',
|
|
693
|
+
' ? fileViewerDefaultFullAssetBaseUrl.slice(0, -DEFAULT_FULL_ASSET_BASE_PATH.length)',
|
|
694
|
+
' : fileViewerDefaultFullAssetBaseUrl;',
|
|
695
|
+
`setDefaultFullAssetBaseUrl(fileViewerDeploymentBaseUrl + ${JSON.stringify(fullAssetBase.encodedBaseDir)});`,
|
|
696
|
+
''
|
|
697
|
+
];
|
|
675
698
|
return [
|
|
676
|
-
|
|
699
|
+
...[autoRegistrationImport, fullAssetBaseImport].filter(Boolean),
|
|
677
700
|
...[presetImport].filter(Boolean),
|
|
678
701
|
...autoPresetImports,
|
|
679
702
|
...rendererImports,
|
|
680
703
|
'',
|
|
704
|
+
...fullAssetBaseSetup,
|
|
681
705
|
`export const configuredFileViewerRenderers = [${rendererNames.join(', ')}];`,
|
|
682
706
|
`export const fileViewerRendererPlan = ${JSON.stringify(plan, null, 2)};`,
|
|
683
|
-
|
|
707
|
+
...(rendererNames.length
|
|
708
|
+
? [`registerFileViewerAutoRendererPreset(configuredFileViewerRenderers, { id: ${JSON.stringify(autoRegistrationId)} });`]
|
|
709
|
+
: []),
|
|
684
710
|
'export default configuredFileViewerRenderers;',
|
|
685
711
|
''
|
|
686
712
|
].join('\n');
|
|
@@ -963,7 +989,10 @@ function assertOwnedPackageVersion(packageName, packageRoot, ownerPackageName) {
|
|
|
963
989
|
return readPackageVersion(packageRoot);
|
|
964
990
|
}
|
|
965
991
|
const ownerPackageJson = JSON.parse(readFileSync(ownerPackageJsonPath, 'utf8'));
|
|
966
|
-
const
|
|
992
|
+
const configuredVersion = ownerPackageJson.dependencies?.[packageName];
|
|
993
|
+
const expectedVersion = configuredVersion?.startsWith('workspace:')
|
|
994
|
+
? configuredVersion.slice('workspace:'.length)
|
|
995
|
+
: configuredVersion;
|
|
967
996
|
const actualVersion = readPackageVersion(packageRoot);
|
|
968
997
|
if (expectedVersion &&
|
|
969
998
|
/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(expectedVersion) &&
|
|
@@ -1199,14 +1228,339 @@ async function copyKnownRendererAssets(targetRoot, rendererIds) {
|
|
|
1199
1228
|
}, null, 2)}\n`);
|
|
1200
1229
|
return results;
|
|
1201
1230
|
}
|
|
1202
|
-
function
|
|
1203
|
-
|
|
1204
|
-
|
|
1231
|
+
function matchesBundledAssetKind(kind, info) {
|
|
1232
|
+
return kind === 'directory' || kind === 'wasm-directory'
|
|
1233
|
+
? info.isDirectory()
|
|
1234
|
+
: info.isFile();
|
|
1235
|
+
}
|
|
1236
|
+
function isBundledFullAssetPayload(payloadRoot) {
|
|
1237
|
+
return Boolean(payloadRoot &&
|
|
1238
|
+
existsSync(payloadRoot) &&
|
|
1239
|
+
statSync(payloadRoot).isDirectory() &&
|
|
1240
|
+
existsSync(join(payloadRoot, 'flyfish-viewer-assets.json')));
|
|
1241
|
+
}
|
|
1242
|
+
function resolveBundledFullAssetSource(installedFullPackages) {
|
|
1243
|
+
const copyAssetsPackageRoot = resolvePackageRoot('file-viewer-copy-assets', installedFullPackages);
|
|
1244
|
+
const copyAssetsPayloadRoot = copyAssetsPackageRoot
|
|
1245
|
+
? join(copyAssetsPackageRoot, 'viewer')
|
|
1246
|
+
: null;
|
|
1247
|
+
if (copyAssetsPackageRoot && copyAssetsPayloadRoot && isBundledFullAssetPayload(copyAssetsPayloadRoot)) {
|
|
1248
|
+
installedFullPackages.forEach((ownerPackageName) => {
|
|
1249
|
+
assertOwnedPackageVersion('file-viewer-copy-assets', copyAssetsPackageRoot, ownerPackageName);
|
|
1250
|
+
});
|
|
1251
|
+
const sourceVersion = readPackageVersion(copyAssetsPackageRoot);
|
|
1252
|
+
if (sourceVersion) {
|
|
1253
|
+
return {
|
|
1254
|
+
packageName: 'file-viewer-copy-assets',
|
|
1255
|
+
packageRoot: copyAssetsPackageRoot,
|
|
1256
|
+
payloadRoot: copyAssetsPayloadRoot,
|
|
1257
|
+
sourceVersion
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
if (installedFullPackages.includes('@file-viewer/web-full')) {
|
|
1262
|
+
const webFullPackageRoot = resolvePackageRoot('@file-viewer/web-full', installedFullPackages);
|
|
1263
|
+
const webFullPayloadRoot = webFullPackageRoot
|
|
1264
|
+
? join(webFullPackageRoot, 'dist')
|
|
1265
|
+
: null;
|
|
1266
|
+
const sourceVersion = readPackageVersion(webFullPackageRoot);
|
|
1267
|
+
if (webFullPackageRoot &&
|
|
1268
|
+
webFullPayloadRoot &&
|
|
1269
|
+
sourceVersion &&
|
|
1270
|
+
isBundledFullAssetPayload(webFullPayloadRoot)) {
|
|
1271
|
+
return {
|
|
1272
|
+
packageName: '@file-viewer/web-full',
|
|
1273
|
+
packageRoot: webFullPackageRoot,
|
|
1274
|
+
payloadRoot: webFullPayloadRoot,
|
|
1275
|
+
sourceVersion
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
// Source workspaces may not have staged file-viewer-copy-assets/viewer yet.
|
|
1280
|
+
// @file-viewer/web keeps the same tracked payload for local development.
|
|
1281
|
+
const webPackageRoot = resolvePackageRoot('@file-viewer/web', installedFullPackages);
|
|
1282
|
+
const webPayloadRoot = webPackageRoot ? join(webPackageRoot, 'viewer') : null;
|
|
1283
|
+
const sourceVersion = readPackageVersion(webPackageRoot);
|
|
1284
|
+
if (webPackageRoot &&
|
|
1285
|
+
webPayloadRoot &&
|
|
1286
|
+
sourceVersion &&
|
|
1287
|
+
isBundledFullAssetPayload(webPayloadRoot)) {
|
|
1288
|
+
return {
|
|
1289
|
+
packageName: '@file-viewer/web',
|
|
1290
|
+
packageRoot: webPackageRoot,
|
|
1291
|
+
payloadRoot: webPayloadRoot,
|
|
1292
|
+
sourceVersion
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
return null;
|
|
1296
|
+
}
|
|
1297
|
+
function assertBundledFullAssetSourceVersion(source, installedFullPackages) {
|
|
1298
|
+
const payloadManifestPath = join(source.payloadRoot, 'flyfish-viewer-manifest.json');
|
|
1299
|
+
if (!existsSync(payloadManifestPath)) {
|
|
1300
|
+
throw new Error(`[file-viewer:vite-plugin] Missing version manifest in ${source.packageName} payload: ${payloadManifestPath}.`);
|
|
1301
|
+
}
|
|
1302
|
+
const payloadManifest = JSON.parse(readFileSync(payloadManifestPath, 'utf8'));
|
|
1303
|
+
if (!payloadManifest.version || payloadManifest.version !== source.sourceVersion) {
|
|
1304
|
+
throw new Error(`[file-viewer:vite-plugin] ${source.packageName}@${source.sourceVersion} contains ` +
|
|
1305
|
+
`viewer assets for ${payloadManifest.version || 'an unknown version'}.`);
|
|
1306
|
+
}
|
|
1307
|
+
installedFullPackages.forEach((packageName) => {
|
|
1308
|
+
const packageRoot = resolvePackageRoot(packageName, installedFullPackages);
|
|
1309
|
+
const packageVersion = readPackageVersion(packageRoot);
|
|
1310
|
+
if (packageVersion && packageVersion !== payloadManifest.version) {
|
|
1311
|
+
throw new Error(`[file-viewer:vite-plugin] ${packageName}@${packageVersion} cannot use ` +
|
|
1312
|
+
`${source.packageName} viewer assets at ${payloadManifest.version}.`);
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
async function copyBundledFullRendererAssets(targetRoot, rendererIds, installedFullPackages) {
|
|
1317
|
+
const source = resolveBundledFullAssetSource(installedFullPackages);
|
|
1318
|
+
if (!source) {
|
|
1319
|
+
return null;
|
|
1320
|
+
}
|
|
1321
|
+
assertBundledFullAssetSourceVersion(source, installedFullPackages);
|
|
1322
|
+
const sourceManifestPath = join(source.payloadRoot, 'flyfish-viewer-assets.json');
|
|
1323
|
+
const manifest = JSON.parse(readFileSync(sourceManifestPath, 'utf8'));
|
|
1324
|
+
if (manifest.schemaVersion !== 1 || !Array.isArray(manifest.rendererAssetManifests)) {
|
|
1325
|
+
throw new Error(`[file-viewer:vite-plugin] Unsupported full asset manifest in ${sourceManifestPath}.`);
|
|
1326
|
+
}
|
|
1327
|
+
await mkdir(targetRoot, { recursive: true });
|
|
1328
|
+
await cp(source.payloadRoot, targetRoot, { recursive: true, force: true });
|
|
1329
|
+
const results = [];
|
|
1330
|
+
for (const asset of manifest.rendererAssetManifests.flatMap((rendererManifest) => rendererManifest.assets)) {
|
|
1331
|
+
if (asset.target !== 'public' || !asset.defaultPath) {
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
const relativePath = asset.defaultPath.replace(/\\/g, '/').replace(/^\/+/, '');
|
|
1335
|
+
const destination = resolve(targetRoot, ...relativePath.split('/'));
|
|
1336
|
+
let copied = false;
|
|
1337
|
+
let reason;
|
|
1338
|
+
if (!isPathWithin(targetRoot, destination)) {
|
|
1339
|
+
reason = 'asset manifest path escapes the copy target';
|
|
1340
|
+
}
|
|
1341
|
+
else {
|
|
1342
|
+
try {
|
|
1343
|
+
copied = matchesBundledAssetKind(asset.kind, await stat(destination));
|
|
1344
|
+
if (!copied) {
|
|
1345
|
+
reason = `copied path does not match asset kind ${asset.kind}`;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
catch {
|
|
1349
|
+
reason = 'bundled full asset not found after copy';
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
results.push({
|
|
1353
|
+
rendererId: asset.rendererId,
|
|
1354
|
+
id: asset.id,
|
|
1355
|
+
to: destination,
|
|
1356
|
+
copied,
|
|
1357
|
+
required: asset.required,
|
|
1358
|
+
reason,
|
|
1359
|
+
sourcePackage: source.packageName,
|
|
1360
|
+
sourceVersion: source.sourceVersion
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
await writeFile(join(targetRoot, 'flyfish-viewer-assets.json'), `${JSON.stringify({
|
|
1364
|
+
...manifest,
|
|
1365
|
+
generatedBy: '@file-viewer/vite-plugin',
|
|
1366
|
+
copiedAt: new Date().toISOString(),
|
|
1367
|
+
rendererIds,
|
|
1368
|
+
assets: results
|
|
1369
|
+
}, null, 2)}\n`);
|
|
1370
|
+
return results;
|
|
1371
|
+
}
|
|
1372
|
+
async function copyRendererAssets(target, rendererIds) {
|
|
1373
|
+
if (target.installedFullPackages.length) {
|
|
1374
|
+
const bundledResults = await copyBundledFullRendererAssets(target.targetRoot, rendererIds, target.installedFullPackages);
|
|
1375
|
+
if (bundledResults) {
|
|
1376
|
+
return bundledResults;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
const results = await copyKnownRendererAssets(target.targetRoot, rendererIds);
|
|
1380
|
+
if (target.installedFullPackages.length) {
|
|
1381
|
+
results.push({
|
|
1382
|
+
rendererId: 'full',
|
|
1383
|
+
id: 'full-asset-payload',
|
|
1384
|
+
to: target.targetRoot,
|
|
1385
|
+
copied: false,
|
|
1386
|
+
required: true,
|
|
1387
|
+
reason: 'no version-matched full viewer payload was found in file-viewer-copy-assets, @file-viewer/web-full, or @file-viewer/web'
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
return results;
|
|
1205
1391
|
}
|
|
1206
1392
|
function copyOptions(value) {
|
|
1207
1393
|
return typeof value === 'object' ? value : {};
|
|
1208
1394
|
}
|
|
1209
|
-
function
|
|
1395
|
+
function findNearestProjectPackageJson(projectRoot) {
|
|
1396
|
+
let current = resolve(projectRoot);
|
|
1397
|
+
while (true) {
|
|
1398
|
+
const packageJsonPath = join(current, 'package.json');
|
|
1399
|
+
if (existsSync(packageJsonPath) && statSyncSafe(packageJsonPath)?.isFile()) {
|
|
1400
|
+
return packageJsonPath;
|
|
1401
|
+
}
|
|
1402
|
+
const parent = dirname(current);
|
|
1403
|
+
if (parent === current) {
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
current = parent;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
function resolveInstalledFullPackages(projectRoot) {
|
|
1410
|
+
const packageJsonPath = findNearestProjectPackageJson(projectRoot);
|
|
1411
|
+
if (!packageJsonPath) {
|
|
1412
|
+
return [];
|
|
1413
|
+
}
|
|
1414
|
+
try {
|
|
1415
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
1416
|
+
const declaredPackages = new Set([
|
|
1417
|
+
...Object.keys(packageJson.dependencies || {}),
|
|
1418
|
+
...Object.keys(packageJson.devDependencies || {}),
|
|
1419
|
+
...Object.keys(packageJson.optionalDependencies || {}),
|
|
1420
|
+
...Object.keys(packageJson.peerDependencies || {})
|
|
1421
|
+
]);
|
|
1422
|
+
const projectPackageRequire = createRequire(packageJsonPath);
|
|
1423
|
+
return fileViewerFullPackages.filter((packageName) => declaredPackages.has(packageName) &&
|
|
1424
|
+
Boolean(tryResolvePackageJson(packageName, projectPackageRequire)));
|
|
1425
|
+
}
|
|
1426
|
+
catch {
|
|
1427
|
+
return [];
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
function normalizeCopyAssetsBaseDir(value) {
|
|
1431
|
+
const normalized = value.trim().replace(/\\/g, '/');
|
|
1432
|
+
if (!normalized) {
|
|
1433
|
+
return '';
|
|
1434
|
+
}
|
|
1435
|
+
if (normalized.startsWith('/') ||
|
|
1436
|
+
isAbsolute(value) ||
|
|
1437
|
+
/^[A-Za-z]:\//.test(normalized) ||
|
|
1438
|
+
normalized.includes('\0')) {
|
|
1439
|
+
throw new Error(`[file-viewer:vite-plugin] copyAssets.baseDir must be a relative directory, received ${JSON.stringify(value)}.`);
|
|
1440
|
+
}
|
|
1441
|
+
const segments = normalized.split('/').filter(Boolean);
|
|
1442
|
+
const decodedSegments = [];
|
|
1443
|
+
for (const segment of segments) {
|
|
1444
|
+
let decodedSegment = segment;
|
|
1445
|
+
try {
|
|
1446
|
+
decodedSegment = decodeURIComponent(segment);
|
|
1447
|
+
}
|
|
1448
|
+
catch {
|
|
1449
|
+
throw new Error(`[file-viewer:vite-plugin] copyAssets.baseDir contains invalid URL encoding: ${JSON.stringify(value)}.`);
|
|
1450
|
+
}
|
|
1451
|
+
if (decodedSegment === '.' ||
|
|
1452
|
+
decodedSegment === '..' ||
|
|
1453
|
+
decodedSegment.includes('/') ||
|
|
1454
|
+
decodedSegment.includes('\\') ||
|
|
1455
|
+
decodedSegment.includes('\0')) {
|
|
1456
|
+
throw new Error(`[file-viewer:vite-plugin] copyAssets.baseDir cannot escape its output directory: ${JSON.stringify(value)}.`);
|
|
1457
|
+
}
|
|
1458
|
+
decodedSegments.push(decodedSegment);
|
|
1459
|
+
}
|
|
1460
|
+
return decodedSegments.join('/');
|
|
1461
|
+
}
|
|
1462
|
+
function isPathWithin(parent, candidate) {
|
|
1463
|
+
const relativePath = relative(parent, candidate);
|
|
1464
|
+
return relativePath === '' || (relativePath !== '..' &&
|
|
1465
|
+
!relativePath.startsWith(`..${sep}`) &&
|
|
1466
|
+
!isAbsolute(relativePath));
|
|
1467
|
+
}
|
|
1468
|
+
function resolveOutputRoot(value, fallback, projectRoot) {
|
|
1469
|
+
const output = value || fallback;
|
|
1470
|
+
return isAbsolute(output) ? resolve(output) : resolve(projectRoot, output);
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Resolves the exact directory used by copyAssets without running a Vite build.
|
|
1474
|
+
* This is exported so integrations can validate their deployment contract.
|
|
1475
|
+
*/
|
|
1476
|
+
export function resolveFileViewerCopyAssetsTarget(mode, value = true, config = {}) {
|
|
1477
|
+
const projectRoot = resolve(config.projectRoot || process.cwd());
|
|
1478
|
+
const options = copyOptions(value);
|
|
1479
|
+
const installedFullPackages = resolveInstalledFullPackages(projectRoot);
|
|
1480
|
+
const baseDir = normalizeCopyAssetsBaseDir(typeof options.baseDir === 'string'
|
|
1481
|
+
? options.baseDir
|
|
1482
|
+
: installedFullPackages.length
|
|
1483
|
+
? 'file-viewer'
|
|
1484
|
+
: '');
|
|
1485
|
+
const outputRoot = resolveOutputRoot(mode === 'dev' ? options.publicDir : options.outDir, mode === 'dev' ? config.publicDir || 'public' : config.outDir || 'dist', projectRoot);
|
|
1486
|
+
const targetRoot = baseDir
|
|
1487
|
+
? resolve(outputRoot, ...baseDir.split('/'))
|
|
1488
|
+
: outputRoot;
|
|
1489
|
+
if (!isPathWithin(outputRoot, targetRoot)) {
|
|
1490
|
+
throw new Error(`[file-viewer:vite-plugin] Refusing to copy assets outside ${outputRoot}: ${targetRoot}.`);
|
|
1491
|
+
}
|
|
1492
|
+
return {
|
|
1493
|
+
mode,
|
|
1494
|
+
outputRoot,
|
|
1495
|
+
targetRoot,
|
|
1496
|
+
baseDir,
|
|
1497
|
+
installedFullPackages: [...installedFullPackages]
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
function normalizeViteDevBasePath(base) {
|
|
1501
|
+
let pathname = base;
|
|
1502
|
+
if (/^[A-Za-z][A-Za-z\d+.-]*:\/\//.test(base)) {
|
|
1503
|
+
try {
|
|
1504
|
+
pathname = new URL(base).pathname;
|
|
1505
|
+
}
|
|
1506
|
+
catch {
|
|
1507
|
+
return '/';
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
if (!pathname.startsWith('/')) {
|
|
1511
|
+
return '/';
|
|
1512
|
+
}
|
|
1513
|
+
return pathname.endsWith('/') ? pathname : `${pathname}/`;
|
|
1514
|
+
}
|
|
1515
|
+
function resolveViteFullAssetBase(base, baseDir) {
|
|
1516
|
+
const encodedBaseDir = baseDir
|
|
1517
|
+
.split('/')
|
|
1518
|
+
.filter(Boolean)
|
|
1519
|
+
.map((segment) => encodeURIComponent(segment))
|
|
1520
|
+
.join('/');
|
|
1521
|
+
const encodedBaseDirWithSlash = encodedBaseDir ? `${encodedBaseDir}/` : '';
|
|
1522
|
+
const resolvedBase = base === undefined ? '/' : base.trim();
|
|
1523
|
+
const isAbsoluteBase = resolvedBase.startsWith('/') || /^[A-Za-z][A-Za-z\d+.-]*:\/\//.test(resolvedBase);
|
|
1524
|
+
if (!isAbsoluteBase) {
|
|
1525
|
+
return { kind: 'deployment', encodedBaseDir: encodedBaseDirWithSlash };
|
|
1526
|
+
}
|
|
1527
|
+
const normalizedBase = resolvedBase.endsWith('/') ? resolvedBase : `${resolvedBase}/`;
|
|
1528
|
+
return { kind: 'literal', url: `${normalizedBase}${encodedBaseDirWithSlash}` };
|
|
1529
|
+
}
|
|
1530
|
+
function resolveDevAssetRequestPath(targetRoot, baseDir, requestPathname, viteBasePath) {
|
|
1531
|
+
if (!requestPathname.startsWith(viteBasePath)) {
|
|
1532
|
+
return null;
|
|
1533
|
+
}
|
|
1534
|
+
let requestRelativePath;
|
|
1535
|
+
try {
|
|
1536
|
+
requestRelativePath = decodeURIComponent(requestPathname.slice(viteBasePath.length));
|
|
1537
|
+
}
|
|
1538
|
+
catch {
|
|
1539
|
+
return null;
|
|
1540
|
+
}
|
|
1541
|
+
requestRelativePath = requestRelativePath.replace(/\\/g, '/');
|
|
1542
|
+
if (requestRelativePath.includes('\0')) {
|
|
1543
|
+
return null;
|
|
1544
|
+
}
|
|
1545
|
+
const basePrefix = baseDir ? `${baseDir}/` : '';
|
|
1546
|
+
if (basePrefix && !requestRelativePath.startsWith(basePrefix)) {
|
|
1547
|
+
return null;
|
|
1548
|
+
}
|
|
1549
|
+
const assetRelativePath = requestRelativePath.slice(basePrefix.length);
|
|
1550
|
+
const isViewerManifest = assetRelativePath === 'flyfish-viewer-assets.json' ||
|
|
1551
|
+
assetRelativePath === 'flyfish-viewer-manifest.json';
|
|
1552
|
+
if (!isViewerManifest &&
|
|
1553
|
+
!assetRelativePath.startsWith('vendor/') &&
|
|
1554
|
+
!assetRelativePath.startsWith('wasm/')) {
|
|
1555
|
+
return null;
|
|
1556
|
+
}
|
|
1557
|
+
const assetPath = resolve(targetRoot, ...assetRelativePath.split('/'));
|
|
1558
|
+
return isPathWithin(targetRoot, assetPath) ? assetPath : null;
|
|
1559
|
+
}
|
|
1560
|
+
function collectAssetRendererIds(selection, autoPresetIds = [], includeFullPreset = false) {
|
|
1561
|
+
if (includeFullPreset) {
|
|
1562
|
+
return expandDescriptorRendererIds(presetModules.all.rendererIds);
|
|
1563
|
+
}
|
|
1210
1564
|
const presetIds = [
|
|
1211
1565
|
...(selection.preset ? expandDescriptorRendererIds(presetModules[selection.preset].rendererIds) : []),
|
|
1212
1566
|
...autoPresetIds.flatMap((presetId) => expandDescriptorRendererIds(presetModules[presetId].rendererIds))
|
|
@@ -1217,14 +1571,14 @@ function collectAssetRendererIds(selection, autoPresetIds = []) {
|
|
|
1217
1571
|
]);
|
|
1218
1572
|
}
|
|
1219
1573
|
function reportAssetCopy(results, targetRoot, mode) {
|
|
1220
|
-
const failedRequired = results.filter((result) => !result.copied && [
|
|
1574
|
+
const failedRequired = results.filter((result) => !result.copied && (result.required ?? [
|
|
1221
1575
|
'pdf',
|
|
1222
1576
|
'office-word-openxml',
|
|
1223
1577
|
'office-presentation',
|
|
1224
1578
|
'archive',
|
|
1225
1579
|
'cad',
|
|
1226
1580
|
'typst'
|
|
1227
|
-
].includes(result.rendererId));
|
|
1581
|
+
].includes(result.rendererId)));
|
|
1228
1582
|
if (!results.length) {
|
|
1229
1583
|
return;
|
|
1230
1584
|
}
|
|
@@ -1258,6 +1612,8 @@ export function fileViewerRenderers(options = {}) {
|
|
|
1258
1612
|
});
|
|
1259
1613
|
let autoPresetIds = resolveAutoPresetIds(options);
|
|
1260
1614
|
let resolvedConfig = null;
|
|
1615
|
+
let installedFullPackages = [];
|
|
1616
|
+
let fullAssetBase = null;
|
|
1261
1617
|
const refreshSelection = (projectRoot) => {
|
|
1262
1618
|
if (projectRoot) {
|
|
1263
1619
|
scanFormats = collectFileViewerRendererScanTokens(projectRoot, options.scan);
|
|
@@ -1269,6 +1625,20 @@ export function fileViewerRenderers(options = {}) {
|
|
|
1269
1625
|
});
|
|
1270
1626
|
autoPresetIds = resolveAutoPresetIds(options);
|
|
1271
1627
|
};
|
|
1628
|
+
const refreshFullAssetRuntime = (projectRoot, viteBase) => {
|
|
1629
|
+
if (!options.copyAssets) {
|
|
1630
|
+
installedFullPackages = [];
|
|
1631
|
+
fullAssetBase = null;
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
const target = resolveFileViewerCopyAssetsTarget('dev', options.copyAssets, {
|
|
1635
|
+
projectRoot
|
|
1636
|
+
});
|
|
1637
|
+
installedFullPackages = target.installedFullPackages;
|
|
1638
|
+
fullAssetBase = installedFullPackages.length
|
|
1639
|
+
? resolveViteFullAssetBase(viteBase, target.baseDir)
|
|
1640
|
+
: null;
|
|
1641
|
+
};
|
|
1272
1642
|
const hasConfiguredRenderers = () => Boolean(selection.preset || selection.descriptors.length || autoPresetIds.length);
|
|
1273
1643
|
return {
|
|
1274
1644
|
name: 'file-viewer-renderers',
|
|
@@ -1276,19 +1646,27 @@ export function fileViewerRenderers(options = {}) {
|
|
|
1276
1646
|
config(userConfig) {
|
|
1277
1647
|
const projectRoot = resolve(process.cwd(), userConfig.root || '.');
|
|
1278
1648
|
refreshSelection(projectRoot);
|
|
1649
|
+
refreshFullAssetRuntime(projectRoot, userConfig.base);
|
|
1279
1650
|
const selectedPackages = collectSelectedPackages(selection, autoPresetIds);
|
|
1280
1651
|
const dependencyAnchorPackages = collectDependencyAnchorPackages(selection, autoPresetIds);
|
|
1652
|
+
const resolvedDependencyAnchorPackages = fullAssetBase
|
|
1653
|
+
? unique([
|
|
1654
|
+
...dependencyAnchorPackages,
|
|
1655
|
+
...installedFullPackages,
|
|
1656
|
+
'@file-viewer/preset-all'
|
|
1657
|
+
])
|
|
1658
|
+
: dependencyAnchorPackages;
|
|
1281
1659
|
const optimizeDepsExclude = createOptimizeDepsExclude(userConfig);
|
|
1282
1660
|
const manualChunksFunction = getManualChunksFunction(userConfig);
|
|
1283
1661
|
const nextConfig = {
|
|
1284
1662
|
optimizeDeps: {
|
|
1285
1663
|
exclude: optimizeDepsExclude,
|
|
1286
|
-
include: createOptimizeDepsInclude(userConfig, optimizeDepsExclude,
|
|
1664
|
+
include: createOptimizeDepsInclude(userConfig, optimizeDepsExclude, resolvedDependencyAnchorPackages)
|
|
1287
1665
|
},
|
|
1288
1666
|
resolve: {
|
|
1289
1667
|
alias: [
|
|
1290
1668
|
...normalizeViteAlias(userConfig.resolve?.alias),
|
|
1291
|
-
...createFileViewerResolveAliases(
|
|
1669
|
+
...createFileViewerResolveAliases(resolvedDependencyAnchorPackages)
|
|
1292
1670
|
]
|
|
1293
1671
|
}
|
|
1294
1672
|
};
|
|
@@ -1321,6 +1699,7 @@ export function fileViewerRenderers(options = {}) {
|
|
|
1321
1699
|
configResolved(config) {
|
|
1322
1700
|
resolvedConfig = config;
|
|
1323
1701
|
refreshSelection(config.root);
|
|
1702
|
+
refreshFullAssetRuntime(config.root, config.base);
|
|
1324
1703
|
},
|
|
1325
1704
|
buildStart() {
|
|
1326
1705
|
assertMissingRendererPolicy(selection, missingMode);
|
|
@@ -1340,58 +1719,68 @@ export function fileViewerRenderers(options = {}) {
|
|
|
1340
1719
|
if (!options.copyAssets || copyOptions(options.copyAssets).mode === 'build') {
|
|
1341
1720
|
return;
|
|
1342
1721
|
}
|
|
1343
|
-
const
|
|
1344
|
-
|
|
1722
|
+
const copyTarget = resolveFileViewerCopyAssetsTarget('dev', options.copyAssets, {
|
|
1723
|
+
projectRoot: resolvedConfig?.root,
|
|
1724
|
+
publicDir: resolvedConfig?.publicDir || 'public',
|
|
1725
|
+
outDir: resolvedConfig?.build.outDir || 'dist'
|
|
1726
|
+
});
|
|
1727
|
+
const { targetRoot } = copyTarget;
|
|
1728
|
+
const results = await copyRendererAssets(copyTarget, collectAssetRendererIds(selection, autoPresetIds, copyTarget.installedFullPackages.length > 0));
|
|
1345
1729
|
reportAssetCopy(results, targetRoot, missingMode);
|
|
1346
|
-
const
|
|
1347
|
-
if (
|
|
1348
|
-
const
|
|
1730
|
+
const viteBasePath = normalizeViteDevBasePath(resolvedConfig?.base || '/');
|
|
1731
|
+
if (server?.middlewares?.use) {
|
|
1732
|
+
const realTargetRoot = await realpath(targetRoot).catch(() => targetRoot);
|
|
1349
1733
|
server.middlewares.use(async (request, response, next) => {
|
|
1350
1734
|
const requestUrl = request.url || '';
|
|
1351
1735
|
const queryIndex = requestUrl.indexOf('?');
|
|
1352
1736
|
const pathname = queryIndex >= 0 ? requestUrl.slice(0, queryIndex) : requestUrl;
|
|
1353
|
-
|
|
1354
|
-
|
|
1737
|
+
const assetPath = resolveDevAssetRequestPath(targetRoot, copyTarget.baseDir, pathname, viteBasePath);
|
|
1738
|
+
if (assetPath) {
|
|
1355
1739
|
try {
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1740
|
+
const resolvedAssetPath = await realpath(assetPath);
|
|
1741
|
+
if (isPathWithin(realTargetRoot, resolvedAssetPath)) {
|
|
1742
|
+
const info = await stat(resolvedAssetPath);
|
|
1743
|
+
if (info.isFile()) {
|
|
1744
|
+
const contentTypes = {
|
|
1745
|
+
'.avif': 'image/avif',
|
|
1746
|
+
'.bcmap': 'application/octet-stream',
|
|
1747
|
+
'.css': 'text/css; charset=utf-8',
|
|
1748
|
+
'.gif': 'image/gif',
|
|
1749
|
+
'.ico': 'image/x-icon',
|
|
1750
|
+
'.jpeg': 'image/jpeg',
|
|
1751
|
+
'.jpg': 'image/jpeg',
|
|
1752
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
1753
|
+
'.json': 'application/json; charset=utf-8',
|
|
1754
|
+
'.map': 'application/json; charset=utf-8',
|
|
1755
|
+
'.md': 'text/markdown; charset=utf-8',
|
|
1756
|
+
'.mjs': 'text/javascript; charset=utf-8',
|
|
1757
|
+
'.otf': 'font/otf',
|
|
1758
|
+
'.pfb': 'application/x-font-type1',
|
|
1759
|
+
'.png': 'image/png',
|
|
1760
|
+
'.svg': 'image/svg+xml',
|
|
1761
|
+
'.ttf': 'font/ttf',
|
|
1762
|
+
'.txt': 'text/plain; charset=utf-8',
|
|
1763
|
+
'.wasm': 'application/wasm',
|
|
1764
|
+
'.webp': 'image/webp',
|
|
1765
|
+
'.woff': 'font/woff',
|
|
1766
|
+
'.woff2': 'font/woff2',
|
|
1767
|
+
'.xml': 'application/xml; charset=utf-8'
|
|
1768
|
+
};
|
|
1769
|
+
response.statusCode = 200;
|
|
1770
|
+
response.setHeader('Content-Type', contentTypes[extname(resolvedAssetPath).toLowerCase()] || 'application/octet-stream');
|
|
1771
|
+
if (request.method === 'HEAD') {
|
|
1772
|
+
response.end();
|
|
1388
1773
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1774
|
+
else {
|
|
1775
|
+
response.end(await readFile(resolvedAssetPath));
|
|
1776
|
+
}
|
|
1777
|
+
return;
|
|
1392
1778
|
}
|
|
1393
1779
|
}
|
|
1394
1780
|
}
|
|
1781
|
+
catch {
|
|
1782
|
+
// Let Vite return its normal 404/fallback response.
|
|
1783
|
+
}
|
|
1395
1784
|
}
|
|
1396
1785
|
next();
|
|
1397
1786
|
});
|
|
@@ -1400,7 +1789,7 @@ export function fileViewerRenderers(options = {}) {
|
|
|
1400
1789
|
transformIndexHtml: {
|
|
1401
1790
|
order: 'pre',
|
|
1402
1791
|
handler() {
|
|
1403
|
-
if (options.inject === false || !hasConfiguredRenderers()) {
|
|
1792
|
+
if (options.inject === false || (!hasConfiguredRenderers() && !fullAssetBase)) {
|
|
1404
1793
|
return undefined;
|
|
1405
1794
|
}
|
|
1406
1795
|
return [
|
|
@@ -1439,9 +1828,13 @@ export function fileViewerRenderers(options = {}) {
|
|
|
1439
1828
|
if (!options.copyAssets || copyOptions(options.copyAssets).mode === 'dev') {
|
|
1440
1829
|
return;
|
|
1441
1830
|
}
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1444
|
-
|
|
1831
|
+
const copyTarget = resolveFileViewerCopyAssetsTarget('build', options.copyAssets, {
|
|
1832
|
+
projectRoot: resolvedConfig?.root,
|
|
1833
|
+
publicDir: resolvedConfig?.publicDir || 'public',
|
|
1834
|
+
outDir: resolvedConfig?.build.outDir || 'dist'
|
|
1835
|
+
});
|
|
1836
|
+
const { targetRoot } = copyTarget;
|
|
1837
|
+
const results = await copyRendererAssets(copyTarget, collectAssetRendererIds(selection, autoPresetIds, copyTarget.installedFullPackages.length > 0));
|
|
1445
1838
|
reportAssetCopy(results, targetRoot, missingMode);
|
|
1446
1839
|
},
|
|
1447
1840
|
resolveId(id) {
|
|
@@ -1452,7 +1845,7 @@ export function fileViewerRenderers(options = {}) {
|
|
|
1452
1845
|
},
|
|
1453
1846
|
load(id) {
|
|
1454
1847
|
if (id === resolvedModuleId || id === resolvedVirtualModuleId) {
|
|
1455
|
-
return renderVirtualModule(selection, requestedFormats, autoPresetIds);
|
|
1848
|
+
return renderVirtualModule(selection, requestedFormats, autoPresetIds, fullAssetBase);
|
|
1456
1849
|
}
|
|
1457
1850
|
return undefined;
|
|
1458
1851
|
}
|
package/package.json
CHANGED