@@ -16,7 +16,8 @@ import {
1616 grants ,
1717 regexpScripts ,
1818 regexpStyles ,
19- styleTemplate
19+ styleTemplate ,
20+ vitePluginName
2021} from './constants.js'
2122import css from './css.js'
2223import { defineGrants , removeDuplicates , transform } from './helpers.js'
@@ -27,181 +28,189 @@ export type { UserscriptPluginConfig }
2728export default function UserscriptPlugin (
2829 config : UserscriptPluginConfig
2930) : PluginOption {
30- let pluginConfig : ResolvedConfig
31- let isBuildWatch : boolean
32- let socketConnection : connection | null = null
33-
34- const workdir = dirname ( fileURLToPath ( import . meta. url ) )
35- const logger = createLogger ( 'info' , {
36- prefix : '[vite-userscript-plugin]' ,
37- allowClearScreen : true
38- } )
39-
40- const httpServer = createServer ( ( req , res ) => {
41- return serveHandler ( req , res , {
42- public : pluginConfig . build . outDir
31+ try {
32+ let pluginConfig : ResolvedConfig
33+ let isBuildWatch : boolean
34+ let socketConnection : connection | null = null
35+
36+ const workdir = dirname ( fileURLToPath ( import . meta. url ) )
37+ const logger = createLogger ( 'info' , {
38+ prefix : `[${ vitePluginName } ]` ,
39+ allowClearScreen : true
4340 } )
44- } )
45-
46- const WebSocketServer = server
47- const ws = new WebSocketServer ( { httpServer } )
48- ws . on ( 'request' , ( request ) => {
49- socketConnection = request . accept ( null , request . origin )
50- } )
51-
52- return {
53- name : 'vite-userscript-plugin' ,
54- apply : 'build' ,
55- config ( ) {
56- return userConfig ( config )
57- } ,
58- async configResolved ( userConfig ) {
59- pluginConfig = userConfig
60- isBuildWatch = ( userConfig . build . watch ?? false ) as boolean
61- config . entry = resolve ( userConfig . root , config . entry )
62- config . header . name = sanitize ( config . header . name )
63-
64- Array . from ( [
65- 'match' ,
66- 'require' ,
67- 'include' ,
68- 'exclude' ,
69- 'resource' ,
70- 'connect'
71- ] ) . forEach ( ( key ) => {
72- const value = config . header [ key ]
73- config . header [ key ] = removeDuplicates ( value )
41+
42+ const httpServer = createServer ( ( req , res ) => {
43+ return serveHandler ( req , res , {
44+ public : pluginConfig . build . outDir
7445 } )
46+ } )
7547
76- config . server = {
77- port : await getPort ( ) ,
78- open : false ,
79- ...config . server
80- }
81- } ,
82- async transform ( src : string , path : string ) {
83- let code = src
48+ const WebSocketServer = server
49+ const ws = new WebSocketServer ( { httpServer } )
50+ ws . on ( 'request' , ( request ) => {
51+ socketConnection = request . accept ( null , request . origin )
52+ } )
8453
85- if ( regexpStyles . test ( path ) ) {
86- code = await css . add ( src , path )
87- }
54+ return {
55+ name : vitePluginName ,
56+ apply : 'build' ,
57+ config ( ) {
58+ return userConfig ( config )
59+ } ,
60+ async configResolved ( userConfig ) {
61+ pluginConfig = userConfig
62+ isBuildWatch = ( userConfig . build . watch ?? false ) as boolean
63+ config . entry = resolve ( userConfig . root , config . entry )
64+ config . header . name = sanitize ( config . header . name )
65+
66+ Array . from ( [
67+ 'match' ,
68+ 'require' ,
69+ 'include' ,
70+ 'exclude' ,
71+ 'resource' ,
72+ 'connect'
73+ ] ) . forEach ( ( key ) => {
74+ const value = config . header [ key ]
75+ config . header [ key ] = removeDuplicates ( value )
76+ } )
8877
89- if ( path . includes ( config . entry ) ) {
90- code = src + styleTemplate
91- }
78+ config . server = {
79+ port : await getPort ( ) ,
80+ open : false ,
81+ ...config . server
82+ }
83+ } ,
84+ async transform ( src : string , path : string ) {
85+ let code = src
9286
93- return {
94- code,
95- map : null
96- }
97- } ,
98- generateBundle ( _ , bundle ) {
99- for ( const [ _ , file ] of Object . entries ( bundle ) ) {
100- const modules = Object . keys (
101- ( file as unknown as { modules : string [ ] } ) . modules
102- )
103-
104- const styleModules = modules . filter ( ( module ) =>
105- regexpStyles . test ( module )
106- )
107-
108- if ( styleModules . length ) {
109- css . merge ( styleModules )
87+ if ( regexpStyles . test ( path ) ) {
88+ code = await css . add ( src , path )
11089 }
111- }
112- } ,
113- async writeBundle ( _ , bundle ) {
114- const { open, port } = config . server !
115- const userFilename = `${ config . header . name } .user.js`
116- const proxyFilename = `${ config . header . name } .proxy.user.js`
117- const metaFilename = `${ config . header . name } .meta.js`
118-
119- for ( const [ fileName ] of Object . entries ( bundle ) ) {
120- if ( regexpScripts . test ( fileName ) ) {
121- const rootDir = pluginConfig . root
122- const outDir = pluginConfig . build . outDir
123-
124- const outPath = resolve ( rootDir , outDir , fileName )
125- const userFilePath = resolve ( rootDir , outDir , userFilename )
126- const proxyFilePath = resolve ( rootDir , outDir , proxyFilename )
127- const metaFilePath = resolve ( rootDir , outDir , metaFilename )
128- const wsPath = resolve ( workdir , `ws-${ config . header . name } .js` )
129-
130- try {
131- let source = readFileSync ( outPath , 'utf8' )
132- source = source . replace ( styleTemplate , `${ css . inject ( ) } ` )
133- source = await transform ( {
134- file : source ,
135- name : fileName ,
136- loader : 'js'
137- } )
138-
139- config . header . grant = removeDuplicates (
140- isBuildWatch
141- ? grants
142- : [ ...defineGrants ( source ) , ...( config . header . grant ?? [ ] ) ]
143- )
14490
145- if ( isBuildWatch ) {
146- const wsFile = readFileSync ( resolve ( workdir , 'ws.js' ) , 'utf8' )
91+ if ( path . includes ( config . entry ) ) {
92+ code = src + styleTemplate
93+ }
94+
95+ return {
96+ code,
97+ map : null
98+ }
99+ } ,
100+ generateBundle ( _ , bundle ) {
101+ for ( const outputChunk of Object . values ( bundle ) ) {
102+ if ( outputChunk . type === 'asset' ) {
103+ continue
104+ }
105+
106+ // prettier-ignore
107+ const styleModules = Object
108+ . keys ( outputChunk . modules )
109+ . filter ( ( module ) => regexpStyles . test ( module ) )
147110
148- const wsScript = await transform ( {
149- file : wsFile . replace ( '__WS__' , `ws://localhost:${ port } ` ) ,
150- name : wsPath ,
111+ if ( styleModules . length ) {
112+ css . merge ( styleModules )
113+ }
114+ }
115+ } ,
116+ async writeBundle ( _ , bundle ) {
117+ const { open, port } = config . server !
118+ const userFilename = `${ config . header . name } .user.js`
119+ const proxyFilename = `${ config . header . name } .proxy.user.js`
120+ const metaFilename = `${ config . header . name } .meta.js`
121+
122+ for ( const [ fileName ] of Object . entries ( bundle ) ) {
123+ if ( regexpScripts . test ( fileName ) ) {
124+ const rootDir = pluginConfig . root
125+ const outDir = pluginConfig . build . outDir
126+
127+ const outPath = resolve ( rootDir , outDir , fileName )
128+ const userFilePath = resolve ( rootDir , outDir , userFilename )
129+ const proxyFilePath = resolve ( rootDir , outDir , proxyFilename )
130+ const metaFilePath = resolve ( rootDir , outDir , metaFilename )
131+ const wsPath = resolve ( workdir , `ws-${ config . header . name } .js` )
132+
133+ try {
134+ let source = readFileSync ( outPath , 'utf8' )
135+ source = source . replace ( styleTemplate , `${ css . inject ( ) } ` )
136+ source = await transform ( {
137+ file : source ,
138+ name : fileName ,
151139 loader : 'js'
152140 } )
153141
154- writeFileSync ( wsPath , wsScript )
155- writeFileSync (
156- proxyFilePath ,
157- new Banner ( {
158- ...config . header ,
159- require : [
160- ...config . header . require ! ,
161- 'file://' + wsPath ,
162- 'file://' + outPath
163- ]
164- } ) . generate ( )
142+ config . header . grant = removeDuplicates (
143+ isBuildWatch
144+ ? grants
145+ : [ ...defineGrants ( source ) , ...( config . header . grant ?? [ ] ) ]
165146 )
166- }
167147
168- const banner = new Banner ( config . header ) . generate ( )
169- writeFileSync ( outPath , source )
170- writeFileSync ( metaFilePath , banner )
171- writeFileSync ( userFilePath , `${ banner } \n\n${ source } ` )
172- } catch ( err ) {
173- console . log ( err )
148+ if ( isBuildWatch ) {
149+ const wsFile = readFileSync ( resolve ( workdir , 'ws.js' ) , 'utf8' )
150+
151+ const wsScript = await transform ( {
152+ file : wsFile . replace ( '__WS__' , `ws://localhost:${ port } ` ) ,
153+ name : wsPath ,
154+ loader : 'js'
155+ } )
156+
157+ writeFileSync ( wsPath , wsScript )
158+ writeFileSync (
159+ proxyFilePath ,
160+ new Banner ( {
161+ ...config . header ,
162+ require : [
163+ ...config . header . require ! ,
164+ 'file://' + wsPath ,
165+ 'file://' + outPath
166+ ]
167+ } ) . generate ( )
168+ )
169+ }
170+
171+ const banner = new Banner ( config . header ) . generate ( )
172+ writeFileSync ( outPath , source )
173+ writeFileSync ( metaFilePath , banner )
174+ writeFileSync ( userFilePath , `${ banner } \n\n${ source } ` )
175+ } catch ( err ) {
176+ console . log ( err )
177+ }
174178 }
175179 }
176- }
177180
178- if ( isBuildWatch && ! httpServer . listening ) {
179- const link = `http://localhost:${ port } `
180- httpServer . listen ( port , ( ) => {
181- logger . clearScreen ( 'info' )
182- logger . info ( colors . blue ( `Running at: ${ colors . gray ( link ) } ` ) )
183- } )
181+ if ( isBuildWatch && ! httpServer . listening ) {
182+ const link = `http://localhost:${ port } `
183+ httpServer . listen ( port , ( ) => {
184+ logger . clearScreen ( 'info' )
185+ logger . info ( colors . blue ( `Running at: ${ colors . gray ( link ) } ` ) )
186+ } )
184187
185- if ( open ) {
186- await openLink ( `${ link } /${ proxyFilename } ` )
188+ if ( open ) {
189+ await openLink ( `${ link } /${ proxyFilename } ` )
190+ }
191+ } else if ( ! isBuildWatch ) {
192+ httpServer . close ( )
193+ process . exit ( 0 )
187194 }
188- } else if ( ! isBuildWatch ) {
189- httpServer . close ( )
190- process . exit ( 0 )
191- }
192- } ,
193- buildEnd ( ) {
194- if ( isBuildWatch ) {
195- logger . clearScreen ( 'info' )
196-
197- if ( socketConnection ) {
198- socketConnection . sendUTF (
199- JSON . stringify ( {
200- message : 'reload'
201- } )
202- )
195+ } ,
196+ buildEnd ( ) {
197+ if ( isBuildWatch ) {
198+ logger . clearScreen ( 'info' )
199+
200+ if ( socketConnection ) {
201+ socketConnection . sendUTF (
202+ JSON . stringify ( {
203+ message : 'reload'
204+ } )
205+ )
206+ }
203207 }
204208 }
205209 }
210+ } catch ( err ) {
211+ console . error ( err )
212+ return {
213+ name : vitePluginName
214+ }
206215 }
207216}
0 commit comments