-
Notifications
You must be signed in to change notification settings - Fork 7
/
htmlFixPlugin.ts
116 lines (106 loc) · 4.16 KB
/
htmlFixPlugin.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import type { Plugin, ResolvedConfig } from 'vite'
import type { InjectOptions, Options } from './types'
import fs from 'fs'
import path from 'path'
import { normalizePath } from 'vite'
import { parse } from 'node-html-parser'
const VITE_PLUGIN_NAME = 'vite-plugin-html-fix'
const SPLIT_MARK = `_${VITE_PLUGIN_NAME}_`
function slash(p: string): string {
return p.replace(/\\/g, '/')
}
function copyFile(
from: string,
to: string
): void {
const dir = path.dirname(to)
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
}
fs.copyFileSync(from, to)
}
export function processTags(tags: InjectOptions['tags']): InjectOptions['tags'] {
const _tags = tags?.map(tag => {
const { attrs } = tag
return {
...tag,
attrs: {
...attrs,
...(attrs?.href ? { href: '' } : {}),
...(attrs?.src ? { src: '' } : {}),
[VITE_PLUGIN_NAME]: `${attrs?.src || attrs?.href || ''}${SPLIT_MARK}${attrs?.src ? 'src' : 'href'}`
}
}
})
return _tags ?? []
}
export function createHtmlFixPlugin(options: Options): Plugin {
let viteConfig: ResolvedConfig
const emittedFiles: {
newFileName: string,
originalFileName: string
}[] = []
return {
name: VITE_PLUGIN_NAME,
configResolved(config) {
viteConfig = config
},
transformIndexHtml: {
order: 'post',
handler(html, ctx) {
const root = parse(html)
const nodes = root.querySelectorAll(`[${VITE_PLUGIN_NAME}]`)
nodes.forEach(node => {
const [url, type] = node.getAttribute(VITE_PLUGIN_NAME)?.split(SPLIT_MARK) || []
node.setAttribute(type, url)
})
const _html = root.toString()
const originalFileName = normalizePath(ctx.path)
Object.values(options.pages ?? {}).forEach(page => {
if (originalFileName.includes(page.template ?? '')) {
const normalPath = normalizePath(page.filename ?? '')
const newFileName = normalPath.startsWith('/') ? normalPath.replace('/', '') : normalPath
if(normalPath !== newFileName && !!page.filename) {
emittedFiles.push({
newFileName,
originalFileName,
})
}
}
})
return {
html: _html,
tags: []
}
}
},
writeBundle() {
emittedFiles.forEach(emittedFile => {
// This is really only here to make sure the new filenames appear in the build output
this.emitFile({
fileName: emittedFile.newFileName,
type: 'asset',
source: 'nothing'
})
})
},
closeBundle() {
if(viteConfig.command !== 'build') return
const root = slash(viteConfig.root || process.cwd())
const dest = slash(viteConfig.build.outDir || 'dist')
const dirsToRemove = emittedFiles.map(emittedFile => {
const { newFileName: fileName, originalFileName: removeFileName } = emittedFile
const newFileName = path.join(root, dest, fileName)
const oldFileName = path.join(root, dest, removeFileName)
copyFile(oldFileName, newFileName)
const removeDir = path.join(root, dest, removeFileName.split('/').filter(Boolean)?.[0])
return removeDir
})
// use FG to make sure dir is empty https://github.com/vbenjs/vite-plugin-html/blob/841d4ef04c3cf5ff0d4339350ae336aa83aa70ed/packages/core/src/htmlPlugin.ts#L150-L161
const uniqueDirsToRemove = [...new Set(dirsToRemove)]
uniqueDirsToRemove.forEach(removePath => {
fs.rmSync(removePath, { recursive: true, force: true })
})
}
}
}