| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- import { existsSync } from 'node:fs'
- import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
- import { tmpdir } from 'node:os'
- import { dirname, join, resolve } from 'node:path'
- import { fileURLToPath, pathToFileURL } from 'node:url'
- const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
- const pluginDist = resolve(root, 'packages/presets/vite-plugin/dist/index.js')
- function assert(condition, message) {
- if (!condition) {
- throw new Error(message)
- }
- }
- assert(
- existsSync(pluginDist),
- 'packages/presets/vite-plugin/dist/index.js is missing; run pnpm --filter @file-viewer/vite-plugin build first.'
- )
- const plugin = await import(`${pathToFileURL(pluginDist).href}?t=${Date.now()}`)
- const {
- collectFileViewerRendererScanTokens,
- extractFileViewerRendererHintTokens,
- fileViewerRenderers,
- resolveFileViewerRendererSelection
- } = plugin
- const inlineTokens = extractFileViewerRendererHintTokens(`
- export const fileViewerFormats = ['pdf', 'docx', 'dwg']
- const upload = '<input accept=".xlsx,image/*,application/vnd.ms-powerpoint">'
- // file-viewer-formats: typst zip xmind
- <div data-file-viewer-renderers="geo,eda"></div>
- `)
- for (const token of ['pdf', 'docx', 'dwg', 'xlsx', 'image', 'ppt', 'typst', 'zip', 'xmind', 'geo', 'eda']) {
- assert(inlineTokens.includes(token), `inline hint extraction missed ${token}`)
- }
- const tempRoot = await mkdtemp(join(tmpdir(), 'file-viewer-vite-scan-'))
- try {
- await mkdir(join(tempRoot, 'src'), { recursive: true })
- await writeFile(
- join(tempRoot, 'src/App.vue'),
- [
- '<template>',
- ' <input data-file-viewer-formats="pdf,docx,dwf,step" accept=".xlsx,.xmind,.brep" />',
- '</template>',
- '<script setup lang="ts">',
- "const fileViewerRenderers = ['cad', 'typst']",
- '</script>',
- ''
- ].join('\n')
- )
- await writeFile(
- join(tempRoot, 'src/upload.ts'),
- [
- "export const fileViewerFormats = ['eml', 'epub', 'geojson']",
- '// file-viewer-formats: gds oas parquet',
- ''
- ].join('\n')
- )
- const scanTokens = collectFileViewerRendererScanTokens(tempRoot, true)
- for (const token of ['pdf', 'docx', 'dwf', 'step', 'xlsx', 'xmind', 'brep', 'cad', 'typst', 'eml', 'epub', 'geojson', 'gds', 'oas', 'parquet']) {
- assert(scanTokens.includes(token), `source scan missed ${token}`)
- }
- const selection = resolveFileViewerRendererSelection({ scan: true }, tempRoot)
- const packages = new Set(selection.renderers.map(renderer => renderer.packageName))
- for (const packageName of [
- '@file-viewer/renderer-pdf',
- '@file-viewer/renderer-word',
- '@file-viewer/renderer-spreadsheet',
- '@file-viewer/renderer-cad',
- '@file-viewer/renderer-3d',
- '@file-viewer/renderer-typst',
- '@file-viewer/renderer-mindmap',
- '@file-viewer/renderer-email',
- '@file-viewer/renderer-epub',
- '@file-viewer/renderer-geo',
- '@file-viewer/renderer-eda',
- '@file-viewer/renderer-data'
- ]) {
- assert(packages.has(packageName), `auto scan did not select ${packageName}`)
- }
- const officePresetSelection = resolveFileViewerRendererSelection(
- { preset: 'office', formats: ['dwg'] },
- tempRoot
- )
- assert(
- officePresetSelection.presetPackage === '@file-viewer/preset-office',
- 'office preset did not resolve to @file-viewer/preset-office'
- )
- assert(
- officePresetSelection.packages.includes('@file-viewer/preset-office'),
- 'office preset package is missing from the renderer plan'
- )
- assert(
- officePresetSelection.packages.includes('@file-viewer/renderer-cad'),
- 'office preset should allow additional explicit renderer packages'
- )
- for (const rendererId of ['pdf', 'office-word-openxml', 'spreadsheet-openxml', 'cad']) {
- assert(
- officePresetSelection.rendererIds.includes(rendererId),
- `office preset selection missed renderer id ${rendererId}`
- )
- }
- const officePlugin = fileViewerRenderers({ preset: 'office', formats: ['dwg'] })
- await officePlugin.buildStart?.()
- const officeModuleId = officePlugin.resolveId?.('virtual:file-viewer-renderers')
- assert(typeof officeModuleId === 'string', 'office preset virtual module did not resolve')
- const officeVirtualCode = await officePlugin.load?.(officeModuleId)
- assert(
- officeVirtualCode.includes("@file-viewer/preset-office"),
- 'office preset virtual module must import @file-viewer/preset-office'
- )
- assert(
- officeVirtualCode.includes("@file-viewer/renderer-cad"),
- 'office preset virtual module must import explicitly added CAD renderer'
- )
- assert(
- !officeVirtualCode.includes("import { pdfRenderer"),
- 'office preset virtual module must not re-import renderer packages covered by the preset'
- )
- assert(
- officeVirtualCode.includes('registerFileViewerAutoRendererPreset'),
- 'virtual module must register selected renderers for framework auto consumption'
- )
- const officeHtmlTags = officePlugin.transformIndexHtml?.()
- assert(
- Array.isArray(officeHtmlTags) &&
- officeHtmlTags.some(tag => tag?.children?.includes('virtual:file-viewer-renderers')),
- 'configured plugin should inject the virtual renderer module into Vite HTML'
- )
- const manualOnlyPlugin = fileViewerRenderers({ preset: 'office', inject: false })
- assert(
- manualOnlyPlugin.transformIndexHtml?.() === undefined,
- 'inject:false should keep the virtual renderer module manual-only'
- )
- const strictRoot = await mkdtemp(join(tmpdir(), 'file-viewer-vite-strict-'))
- const previousCwd = process.cwd()
- try {
- const fakePdfRoot = join(
- strictRoot,
- 'node_modules/@file-viewer/preset-office/node_modules/@file-viewer/renderer-pdf/node_modules/pdfjs-dist'
- )
- await mkdir(join(strictRoot, 'node_modules/@file-viewer/preset-office'), { recursive: true })
- await mkdir(join(strictRoot, 'node_modules/@file-viewer/preset-office/node_modules/@file-viewer/renderer-pdf'), { recursive: true })
- await mkdir(join(fakePdfRoot, 'legacy/build'), { recursive: true })
- await mkdir(join(fakePdfRoot, 'cmaps'), { recursive: true })
- await mkdir(join(fakePdfRoot, 'wasm'), { recursive: true })
- await mkdir(join(fakePdfRoot, 'standard_fonts'), { recursive: true })
- await writeFile(join(strictRoot, 'package.json'), '{"name":"strict-react-app","type":"module"}\n')
- await writeFile(
- join(strictRoot, 'node_modules/@file-viewer/preset-office/package.json'),
- '{"name":"@file-viewer/preset-office","version":"0.0.0","dependencies":{"@file-viewer/renderer-pdf":"0.0.0"}}\n'
- )
- await writeFile(
- join(strictRoot, 'node_modules/@file-viewer/preset-office/node_modules/@file-viewer/renderer-pdf/package.json'),
- '{"name":"@file-viewer/renderer-pdf","version":"0.0.0","dependencies":{"pdfjs-dist":"0.0.0"}}\n'
- )
- await writeFile(join(fakePdfRoot, 'package.json'), '{"name":"pdfjs-dist","version":"0.0.0"}\n')
- await writeFile(join(fakePdfRoot, 'legacy/build/pdf.worker.mjs'), 'export default null\n')
- await writeFile(join(fakePdfRoot, 'cmaps/LICENSE'), 'fake cmap\n')
- await writeFile(join(fakePdfRoot, 'wasm/pdf.wasm'), 'fake wasm\n')
- await writeFile(join(fakePdfRoot, 'standard_fonts/LICENSE'), 'fake font\n')
- process.chdir(strictRoot)
- const strictPlugin = fileViewerRenderers({
- preset: 'office',
- copyAssets: { publicDir: 'public', mode: 'dev' }
- })
- strictPlugin.config?.({ root: '.' })
- strictPlugin.configResolved?.({
- root: strictRoot,
- publicDir: join(strictRoot, 'public'),
- build: { outDir: join(strictRoot, 'dist') }
- })
- await strictPlugin.buildStart?.()
- await strictPlugin.configureServer?.({})
- for (const assetPath of [
- 'vendor/pdf/pdf.worker.mjs',
- 'vendor/pdf/cmaps/LICENSE',
- 'vendor/pdf/wasm/pdf.wasm',
- 'vendor/pdf/standard_fonts/LICENSE'
- ]) {
- assert(
- existsSync(join(strictRoot, 'public', assetPath)),
- `strict preset dependency asset copy missed ${assetPath}`
- )
- }
- } finally {
- process.chdir(previousCwd)
- await rm(strictRoot, { recursive: true, force: true })
- }
- const autoPresetSelection = resolveFileViewerRendererSelection({ preset: 'auto' }, tempRoot)
- assert(
- autoPresetSelection.autoPresetPackages.length > 0,
- 'preset:auto should discover installed @file-viewer/preset-* packages'
- )
- const autoPresetPlugin = fileViewerRenderers({ preset: 'auto' })
- await autoPresetPlugin.buildStart?.()
- const autoPresetModuleId = autoPresetPlugin.resolveId?.('virtual:file-viewer-renderers')
- assert(typeof autoPresetModuleId === 'string', 'preset:auto virtual module did not resolve')
- const autoPresetVirtualCode = await autoPresetPlugin.load?.(autoPresetModuleId)
- assert(typeof autoPresetVirtualCode === 'string', 'preset:auto virtual module did not load')
- assert(
- autoPresetSelection.autoPresetPackages.some(packageName =>
- autoPresetVirtualCode.includes(packageName)
- ),
- 'preset:auto virtual module should import discovered preset packages'
- )
- } finally {
- await rm(tempRoot, { recursive: true, force: true })
- }
- console.log('[vite-plugin-auto-scan] Verified source hint scanning and renderer selection.')
|