build.mjs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises'
  2. import { dirname, join, resolve } from 'node:path'
  3. import { fileURLToPath } from 'node:url'
  4. const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), '..')
  5. const distDir = resolve(packageDir, 'dist')
  6. const args = new Set(process.argv.slice(2))
  7. async function collectFiles(dir, files = []) {
  8. const entries = await readdir(dir)
  9. for (const entry of entries) {
  10. const filePath = join(dir, entry)
  11. const fileStat = await stat(filePath)
  12. if (fileStat.isDirectory()) {
  13. await collectFiles(filePath, files)
  14. } else {
  15. files.push(filePath)
  16. }
  17. }
  18. return files
  19. }
  20. async function inlineCssAssetUrls() {
  21. const files = await collectFiles(distDir)
  22. const cssAssets = []
  23. for (const filePath of files) {
  24. if (!filePath.endsWith('.css')) {
  25. continue
  26. }
  27. const source = await readFile(filePath, 'utf8')
  28. if (source.trim()) {
  29. cssAssets.push({ filePath, source })
  30. }
  31. }
  32. if (!cssAssets.length) {
  33. return
  34. }
  35. cssAssets.sort((a, b) => b.source.length - a.source.length)
  36. const cssDataUrl = `data:text/css;base64,${Buffer.from(cssAssets[0].source).toString('base64')}`
  37. const cssHref = JSON.stringify(cssDataUrl)
  38. for (const filePath of files) {
  39. if (!/\.(mjs|js)$/.test(filePath)) {
  40. continue
  41. }
  42. const source = await readFile(filePath, 'utf8')
  43. let rewritten = source
  44. rewritten = rewritten.replace(
  45. /const\s+([A-Za-z_$][\w$]*)\s*=\s*"data:text\/css;base64,[^"]*";/,
  46. (_match, varName) => `const ${varName} = ${cssHref};`
  47. )
  48. rewritten = rewritten.replace(/new URL\("style\.css", import\.meta\.url\)\.href/g, cssHref)
  49. rewritten = rewritten.replace(/new URL\("\.\/file-viewer3\.css", import\.meta\.url\)\.href/g, cssHref)
  50. if (rewritten !== source) {
  51. await writeFile(filePath, rewritten, 'utf8')
  52. }
  53. }
  54. }
  55. async function fixInlineWorkerBlobs() {
  56. const inlineWorkerBlobRE = /new Blob\(\[atob\(([^)]+)\)\],/g
  57. const files = await collectFiles(distDir)
  58. for (const filePath of files) {
  59. if (!/\.(mjs|js)$/.test(filePath)) {
  60. continue
  61. }
  62. const source = await readFile(filePath, 'utf8')
  63. let replacementCount = 0
  64. const fixed = source.replace(inlineWorkerBlobRE, (_match, base64Source) => {
  65. replacementCount += 1
  66. return `new Blob([Uint8Array.from(atob(${base64Source}), byte => byte.charCodeAt(0))],`
  67. })
  68. if (replacementCount) {
  69. await writeFile(filePath, fixed, 'utf8')
  70. console.log(`fixed ${replacementCount} inline worker blob(s) in ${filePath.replace(`${packageDir}/`, '')}`)
  71. }
  72. }
  73. }
  74. async function writeCompatibilityCss() {
  75. await mkdir(distDir, { recursive: true })
  76. await writeFile(resolve(distDir, 'file-viewer3.css'), [
  77. "@import './vue3.css';",
  78. '',
  79. '.ff-file-viewer-vue3 {',
  80. ' width: 100%;',
  81. ' height: 100%;',
  82. ' min-height: 0;',
  83. '}',
  84. ''
  85. ].join('\n'))
  86. }
  87. async function assertFile(relativePath) {
  88. const fullPath = join(packageDir, relativePath)
  89. const info = await stat(fullPath).catch(() => null)
  90. if (!info?.isFile()) {
  91. throw new Error(`[file-viewer-vue3] Missing required package file: ${relativePath}`)
  92. }
  93. }
  94. async function assertMissing(relativePath) {
  95. const info = await stat(join(packageDir, relativePath)).catch(() => null)
  96. if (info) {
  97. throw new Error(`[file-viewer-vue3] Unexpected demo artifact in library package output: ${relativePath}`)
  98. }
  99. }
  100. async function verifyPackageOutput() {
  101. await assertFile('dist/index.mjs')
  102. await assertFile('dist/src/package/index.d.ts')
  103. await assertFile('dist/style.css')
  104. await assertFile('dist/file-viewer3.css')
  105. await assertMissing('dist/index.html')
  106. await assertMissing('dist/compare.html')
  107. const packageJson = JSON.parse(await readFile(join(packageDir, 'package.json'), 'utf8'))
  108. const typeEntry = packageJson.exports?.['.']?.types || packageJson.types
  109. if (!typeEntry) {
  110. throw new Error('[file-viewer-vue3] package.json does not expose a type entry.')
  111. }
  112. const normalizedTypeEntry = typeEntry.replace(/^\.\//, '')
  113. await assertFile(normalizedTypeEntry)
  114. console.log(`[file-viewer-vue3] Verified library package output in ${distDir}`)
  115. }
  116. if (!args.has('--verify-only')) {
  117. await inlineCssAssetUrls()
  118. await fixInlineWorkerBlobs()
  119. await writeCompatibilityCss()
  120. }
  121. await verifyPackageOutput()