@@ -4,6 +4,7 @@ import fs from 'node:fs'
4
4
import { bold , dim , green , yellow } from 'kolorist'
5
5
import { normalizePath } from 'vite'
6
6
import type { PluginOption , ServerOptions } from 'vite'
7
+ import MagicString from 'magic-string'
7
8
import { compileSFCTemplate } from './compiler'
8
9
import { idToFile , parseVueRequest } from './utils'
9
10
@@ -88,6 +89,15 @@ export interface VitePluginInspectorOptions {
88
89
* @default false
89
90
*/
90
91
disableInspectorOnEditorOpen ?: boolean
92
+
93
+ /**
94
+ * Hide information in VNode and produce clean html in DevTools
95
+ *
96
+ * Currently, it only works for Vue 3
97
+ *
98
+ * @default true
99
+ */
100
+ cleanHtml ?: boolean
91
101
}
92
102
93
103
const toggleComboKeysMap = {
@@ -125,88 +135,136 @@ function VitePluginInspector(options: VitePluginInspectorOptions = DEFAULT_INSPE
125
135
let serverOptions : ServerOptions | undefined
126
136
127
137
const {
138
+ vue,
128
139
appendTo,
140
+ cleanHtml = vue === 3 , // Only enabled for Vue 3 by default
129
141
} = normalizedOptions
130
142
131
- return {
132
- name : 'vite-plugin-vue-inspector' ,
133
- enforce : 'pre' ,
134
- apply ( _ , { command } ) {
135
- // apply only on serve and not for test
136
- return command === 'serve' && process . env . NODE_ENV !== 'test'
137
- } ,
138
- async resolveId ( importee : string ) {
139
- if ( importee . startsWith ( 'virtual:vue-inspector-options' ) ) {
140
- return importee
141
- }
142
- else if ( importee . startsWith ( 'virtual:vue-inspector-path:' ) ) {
143
- const resolved = importee . replace ( 'virtual:vue-inspector-path:' , `${ inspectorPath } /` )
144
- return resolved
145
- }
146
- } ,
143
+ return [
144
+ {
145
+ name : 'vite-plugin-vue-inspector' ,
146
+ enforce : 'pre' ,
147
+ apply ( _ , { command } ) {
148
+ // apply only on serve and not for test
149
+ return command === 'serve' && process . env . NODE_ENV !== 'test'
150
+ } ,
151
+ async resolveId ( importee : string ) {
152
+ if ( importee . startsWith ( 'virtual:vue-inspector-options' ) ) {
153
+ return importee
154
+ }
155
+ else if ( importee . startsWith ( 'virtual:vue-inspector-path:' ) ) {
156
+ const resolved = importee . replace ( 'virtual:vue-inspector-path:' , `${ inspectorPath } /` )
157
+ return resolved
158
+ }
159
+ } ,
147
160
148
- async load ( id ) {
149
- if ( id === 'virtual:vue-inspector-options' ) {
150
- return `export default ${ JSON . stringify ( { ...normalizedOptions , serverOptions } ) } `
151
- }
152
- else if ( id . startsWith ( inspectorPath ) ) {
153
- const { query } = parseVueRequest ( id )
154
- if ( query . type )
155
- return
156
- // read file ourselves to avoid getting shut out by vites fs.allow check
157
- const file = idToFile ( id )
158
- if ( fs . existsSync ( file ) )
159
- return await fs . promises . readFile ( file , 'utf-8' )
160
- else
161
- console . error ( `failed to find file for vue-inspector: ${ file } , referenced by id ${ id } .` )
162
- }
163
- } ,
164
- transform ( code , id ) {
165
- const { filename, query } = parseVueRequest ( id )
161
+ async load ( id ) {
162
+ if ( id === 'virtual:vue-inspector-options' ) {
163
+ return `export default ${ JSON . stringify ( { ...normalizedOptions , serverOptions } ) } `
164
+ }
165
+ else if ( id . startsWith ( inspectorPath ) ) {
166
+ const { query } = parseVueRequest ( id )
167
+ if ( query . type )
168
+ return
169
+ // read file ourselves to avoid getting shut out by vites fs.allow check
170
+ const file = idToFile ( id )
171
+ if ( fs . existsSync ( file ) )
172
+ return await fs . promises . readFile ( file , 'utf-8' )
173
+ else
174
+ console . error ( `failed to find file for vue-inspector: ${ file } , referenced by id ${ id } .` )
175
+ }
176
+ } ,
177
+ transform ( code , id ) {
178
+ const { filename, query } = parseVueRequest ( id )
166
179
167
- const isJsx = filename . endsWith ( '.jsx' ) || filename . endsWith ( '.tsx' ) || ( filename . endsWith ( '.vue' ) && query . isJsx )
168
- const isTpl = filename . endsWith ( '.vue' ) && query . type !== 'style' && ! query . raw
180
+ const isJsx = filename . endsWith ( '.jsx' ) || filename . endsWith ( '.tsx' ) || ( filename . endsWith ( '.vue' ) && query . isJsx )
181
+ const isTpl = filename . endsWith ( '.vue' ) && query . type !== 'style' && ! query . raw
169
182
170
- if ( isJsx || isTpl )
171
- return compileSFCTemplate ( { code, id : filename , type : isJsx ? 'jsx' : 'template' } )
183
+ if ( isJsx || isTpl )
184
+ return compileSFCTemplate ( { code, id : filename , type : isJsx ? 'jsx' : 'template' } )
172
185
173
- if ( ! appendTo )
174
- return
186
+ if ( ! appendTo )
187
+ return
175
188
176
- if ( ( typeof appendTo === 'string' && filename . endsWith ( appendTo ) )
189
+ if ( ( typeof appendTo === 'string' && filename . endsWith ( appendTo ) )
177
190
|| ( appendTo instanceof RegExp && appendTo . test ( filename ) ) )
178
- return { code : `${ code } \nimport 'virtual:vue-inspector-path:load.js'` }
179
- } ,
180
- configureServer ( server ) {
181
- const _printUrls = server . printUrls
182
- const { toggleComboKey } = normalizedOptions
183
-
184
- toggleComboKey && ( server . printUrls = ( ) => {
185
- const keys = normalizeComboKeyPrint ( toggleComboKey )
186
- _printUrls ( )
187
- console . log ( ` ${ green ( '➜' ) } ${ bold ( 'Vue Inspector' ) } : ${ green ( `Press ${ yellow ( keys ) } in App to toggle the Inspector` ) } \n` )
188
- } )
189
- } ,
190
- transformIndexHtml ( html ) {
191
- if ( appendTo )
192
- return
193
- return {
194
- html,
195
- tags : [
196
- {
197
- tag : 'script' ,
198
- injectTo : 'head' ,
199
- attrs : {
200
- type : 'module' ,
201
- src : '/@id/virtual:vue-inspector-path:load.js' ,
191
+ return { code : `${ code } \nimport 'virtual:vue-inspector-path:load.js'` }
192
+ } ,
193
+ configureServer ( server ) {
194
+ const _printUrls = server . printUrls
195
+ const { toggleComboKey } = normalizedOptions
196
+
197
+ toggleComboKey && ( server . printUrls = ( ) => {
198
+ const keys = normalizeComboKeyPrint ( toggleComboKey )
199
+ _printUrls ( )
200
+ console . log ( ` ${ green ( '➜' ) } ${ bold ( 'Vue Inspector' ) } : ${ green ( `Press ${ yellow ( keys ) } in App to toggle the Inspector` ) } \n` )
201
+ } )
202
+ } ,
203
+ transformIndexHtml ( html ) {
204
+ if ( appendTo )
205
+ return
206
+ return {
207
+ html,
208
+ tags : [
209
+ {
210
+ tag : 'script' ,
211
+ injectTo : 'head' ,
212
+ attrs : {
213
+ type : 'module' ,
214
+ src : '/@id/virtual:vue-inspector-path:load.js' ,
215
+ } ,
202
216
} ,
203
- } ,
204
- ] ,
205
- }
206
- } ,
207
- configResolved ( resolvedConfig ) {
208
- serverOptions = resolvedConfig . server
217
+ ] ,
218
+ }
219
+ } ,
220
+ configResolved ( resolvedConfig ) {
221
+ serverOptions = resolvedConfig . server
222
+ } ,
209
223
} ,
224
+ {
225
+ name : 'vite-plugin-vue-inspector:post' ,
226
+ enforce : 'post' ,
227
+ apply ( _ , { command } ) {
228
+ // apply only on serve and not for test
229
+ return cleanHtml && vue === 3 && command === 'serve' && process . env . NODE_ENV !== 'test'
230
+ } ,
231
+ transform ( code ) {
232
+ if ( code . includes ( '_interopVNode' ) )
233
+ return
234
+ if ( ! code . includes ( 'data-v-inspector' ) )
235
+ return
236
+
237
+ const fn = new Set < string > ( )
238
+ const s = new MagicString ( code )
239
+
240
+ s . replace ( / ( c r e a t e E l e m e n t V N o d e | c r e a t e V N o d e | c r e a t e E l e m e n t B l o c k ) a s _ \1, ? / g, ( _ , name ) => {
241
+ fn . add ( name )
242
+ return ''
243
+ } )
244
+
245
+ if ( ! fn . size )
246
+ return
247
+
248
+ s . appendLeft ( 0 , `/* Injection by vite-plugin-vue-inspector Start */
249
+ import { ${ Array . from ( fn . values ( ) ) . map ( i => `${ i } as __${ i } ` ) . join ( ',' ) } } from 'vue'
250
+ function _interopVNode(vnode) {
251
+ if (vnode && vnode.props && 'data-v-inspector' in vnode.props) {
252
+ const data = vnode.props['data-v-inspector']
253
+ delete vnode.props['data-v-inspector']
254
+ Object.defineProperty(vnode.props, '__v_inspector', { value: data, enumerable: false })
210
255
}
256
+ return vnode
257
+ }
258
+ ${ Array . from ( fn . values ( ) ) . map ( i => `function _${ i } (...args) { return _interopVNode(__${ i } (...args)) }` ) . join ( '\n' ) }
259
+ /* Injection by vite-plugin-vue-inspector End */
260
+ ` )
261
+
262
+ return {
263
+ code : s . toString ( ) ,
264
+ map : s . generateMap ( { hires : 'boundary' } ) ,
265
+ }
266
+ } ,
267
+ } ,
268
+ ]
211
269
}
212
270
export default VitePluginInspector
0 commit comments