-
-
Notifications
You must be signed in to change notification settings - Fork 164
/
Copy pathvite.ts
225 lines (200 loc) · 7.62 KB
/
vite.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
import { fileURLToPath } from 'node:url'
import path from 'node:path'
import fs from 'node:fs'
import { normalizePath } from 'vite'
import type { PluginOption, ResolvedConfig, ViteDevServer } from 'vite'
import sirv from 'sirv'
import Inspect from 'vite-plugin-inspect'
import { setViteServerContext } from '@vue/devtools-kit'
import VueInspector from 'vite-plugin-vue-inspector'
import { createViteServerRpc } from '@vue/devtools-core'
import { bold, cyan, dim, green, yellow } from 'kolorist'
import type { VitePluginInspectorOptions } from 'vite-plugin-vue-inspector'
import { DIR_CLIENT } from './dir'
import { getRpcFunctions } from './rpc'
import { removeUrlQuery } from './utils'
import { guessEditor } from './guess-editor'
function getVueDevtoolsPath() {
const pluginPath = normalizePath(path.dirname(fileURLToPath(import.meta.url)))
return pluginPath.replace(/\/dist$/, '/\/src')
}
const toggleComboKeysMap = {
option: process.platform === 'darwin' ? 'Option(⌥)' : 'Alt(⌥)',
meta: 'Command(⌘)',
shift: 'Shift(⇧)',
}
function normalizeComboKeyPrint(toggleComboKey: string) {
return toggleComboKey.split('-').map(key => toggleComboKeysMap[key] || key[0].toUpperCase() + key.slice(1)).join(dim('+'))
}
const devtoolsNextResourceSymbol = '?__vue-devtools-next-resource'
export interface VitePluginVueDevToolsOptions {
/**
* append an import to the module id ending with `appendTo` instead of adding a script into body
* useful for projects that do not use html file as an entry
*
* WARNING: only set this if you know exactly what it does.
* @default ''
*/
appendTo?: string | RegExp
/**
* Enable vue component inspector
*
* @default true
*/
componentInspector?: boolean | VitePluginInspectorOptions
/**
* Target editor when open in editor (v7.2.0+)
*
* @default
* It will be [predicted](https://github.com/yyx990803/launch-editor/blob/master/packages/launch-editor/guess.js) based on the IDEs you've launched.
* And its backup is code (Visual Studio Code).
*/
launchEditor?: 'appcode' | 'atom' | 'atom-beta' | 'brackets' | 'clion' | 'code' | 'code-insiders' | 'codium' | 'emacs' | 'idea' | 'notepad++' | 'pycharm' | 'phpstorm' | 'rubymine' | 'sublime' | 'vim' | 'visualstudio' | 'webstorm' | 'rider' | string
/**
* Customize openInEditor host
* @default false
* @deprecated This option is deprecated and removed in 7.1.0. The plugin now automatically detects the correct host.
*/
openInEditorHost?: string | false
/**
* DevTools client host
* useful for projects that use a reverse proxy
* @default false
* @deprecated This option is deprecated and removed in 7.1.0. The plugin now automatically detects the correct host.
*/
clientHost?: string | false
}
const defaultOptions: VitePluginVueDevToolsOptions = {
appendTo: '',
componentInspector: true,
launchEditor: guessEditor()[0] ?? process.env.LAUNCH_EDITOR ?? 'code',
}
function mergeOptions(options: VitePluginVueDevToolsOptions): VitePluginVueDevToolsOptions {
return Object.assign({}, defaultOptions, options)
}
export default function VitePluginVueDevTools(options?: VitePluginVueDevToolsOptions): PluginOption {
const vueDevtoolsPath = getVueDevtoolsPath()
const inspect = Inspect({
silent: true,
})
const pluginOptions = mergeOptions(options ?? {})
let config: ResolvedConfig
function configureServer(server: ViteDevServer) {
const base = (server.config.base) || '/'
server.middlewares.use(`${base}__devtools__`, sirv(DIR_CLIENT, {
single: true,
dev: true,
}))
// vite client <-> server messaging
setViteServerContext(server)
const rpcFunctions = getRpcFunctions({
rpc: inspect.api.rpc,
server,
config,
})
createViteServerRpc(rpcFunctions)
const _printUrls = server.printUrls
const colorUrl = (url: string) =>
cyan(url.replace(/:(\d+)\//, (_, port) => `:${bold(port)}/`))
server.printUrls = () => {
const urls = server.resolvedUrls!
const keys = normalizeComboKeyPrint('option-shift-d')
_printUrls()
for (const url of urls.local) {
const devtoolsUrl = url.endsWith('/') ? `${url}__devtools__/` : `${url}/__devtools__/`
console.log(` ${green('➜')} ${bold('Vue DevTools')}: ${green(`Open ${colorUrl(`${devtoolsUrl}`)} as a separate window`)}`)
}
console.log(` ${green('➜')} ${bold('Vue DevTools')}: ${green(`Press ${yellow(keys)} in App to toggle the Vue DevTools`)}\n`)
}
}
const devtoolsOptionsImportee = 'virtual:vue-devtools-options'
const resolvedDevtoolsOptions = `\0${devtoolsOptionsImportee}`
const plugin = <PluginOption>{
name: 'vite-plugin-vue-devtools',
enforce: 'pre',
apply: 'serve',
configResolved(resolvedConfig) {
config = resolvedConfig
},
configureServer(server) {
configureServer(server)
},
async resolveId(importee: string) {
if (importee === devtoolsOptionsImportee) {
return resolvedDevtoolsOptions
}
// Why use query instead of vite virtual module on devtools resource?
// Devtools resource will import `@vue/devtools-core` and other packages, which vite cannot analysis correctly on virtual module.
// So we should use absolute path + `query` to mark the resource as devtools resource.
else if (importee.startsWith('virtual:vue-devtools-path:')) {
const resolved = importee.replace('virtual:vue-devtools-path:', `${vueDevtoolsPath}/`)
return `${resolved}${devtoolsNextResourceSymbol}`
}
},
async load(id) {
if (id === resolvedDevtoolsOptions) {
return `export default ${JSON.stringify({ base: config.base, componentInspector: pluginOptions.componentInspector })}`
}
else if (id.endsWith(devtoolsNextResourceSymbol)) {
const filename = removeUrlQuery(id)
// read file ourselves to avoid getting shut out by vite's fs.allow check
return await fs.promises.readFile(filename, 'utf-8')
}
},
transform(code, id, options) {
if (options?.ssr)
return
const { appendTo } = pluginOptions
const [filename] = id.split('?', 2)
if (appendTo
&& (
(typeof appendTo === 'string' && filename.endsWith(appendTo))
|| (appendTo instanceof RegExp && appendTo.test(filename)))) {
code = `import 'virtual:vue-devtools-path:overlay.js';\n${code}`
}
return code
},
transformIndexHtml(html) {
if (pluginOptions.appendTo)
return
return {
html,
tags: [
{
tag: 'script',
injectTo: 'head-prepend',
attrs: {
type: 'module',
src: `${config.base || '/'}@id/virtual:vue-devtools-path:overlay.js`,
},
},
// inject inspector script manually to ensure it's loaded after vue-devtools
pluginOptions.componentInspector && {
tag: 'script',
injectTo: 'head-prepend',
launchEditor: pluginOptions.launchEditor,
attrs: {
type: 'module',
src: `${config.base || '/'}@id/virtual:vue-inspector-path:load.js`,
},
},
].filter(Boolean),
}
},
async buildEnd() {
},
}
return [
inspect as PluginOption,
pluginOptions.componentInspector && VueInspector({
toggleComboKey: '',
toggleButtonVisibility: 'never',
launchEditor: pluginOptions.launchEditor,
...typeof pluginOptions.componentInspector === 'boolean'
? {}
: pluginOptions.componentInspector,
appendTo: pluginOptions.appendTo || 'manually',
}) as PluginOption,
plugin,
].filter(Boolean)
}