1+ // From https://github.com/pnd280/complexity/blob/alpha/vite-plugins/vite-plugin-run-command-on-demand.ts
2+
3+ import chalk from "chalk" ;
4+ import { exec } from "child_process" ;
5+
6+
7+ const pluginName = "vite-plugin-run-command-on-demand" ;
8+
9+ /**
10+ * Logs a message with the plugin name.
11+ * @param {string } message - The message to log.
12+ */
13+ const log = ( message ) => console . log ( chalk . blue ( `\n[${ pluginName } ]` ) , message ) ;
14+
15+ /**
16+ * Logs an error message with the plugin name.
17+ * @param {string } message - The error message to log.
18+ */
19+ const logError = ( message ) =>
20+ console . error ( chalk . blue ( `\n[${ pluginName } ]` ) , chalk . red ( message ) , "\n" ) ;
21+
22+ /**
23+ * Runs a shell command.
24+ * @param {string } command - The command to run.
25+ * @returns {Promise<void> } A promise that resolves when the command completes.
26+ */
27+ const runCommand = ( command ) =>
28+ new Promise ( ( resolve , reject ) => {
29+ exec ( command , ( error , stdout , stderr ) => {
30+ if ( error ) {
31+ logError ( `Error executing command: ${ command } \n${ stderr } ` ) ;
32+ reject ( error ) ;
33+ } else {
34+ log ( `Command executed successfully: ${ command } \n${ stdout } ` ) ;
35+ resolve ( ) ;
36+ }
37+ } ) ;
38+ } ) ;
39+
40+ /**
41+ * @typedef {Object } CustomCommandsPluginOptions
42+ * @property {string } [beforeServerStart] - Command to run before the server starts.
43+ * @property {string } [afterServerStart] - Command to run after the server starts.
44+ * @property {string } [onHotUpdate] - Command to run on hot update.
45+ * @property {string } [beforeBuild] - Command to run before the build starts.
46+ * @property {string } [afterBuild] - Command to run after the build ends.
47+ * @property {string } [closeBundle] - Command to run when the bundle is closed.
48+ */
49+
50+ /**
51+ * Executes a command if it is defined.
52+ * @param {string|undefined } command - The command to execute.
53+ * @param {string } errorMessage - The error message to log if the command fails.
54+ */
55+ const executeCommand = async ( command , errorMessage ) => {
56+ if ( command ) {
57+ try {
58+ await runCommand ( command ) ;
59+ } catch {
60+ logError ( errorMessage ) ;
61+ }
62+ }
63+ } ;
64+
65+ /**
66+ * Checks if the current environment is allowed.
67+ * @returns {boolean } True if the environment is "development" or "production".
68+ */
69+ const isAllowedEnvironment = ( ) => {
70+ const env = process . env . NODE_ENV ;
71+ return env === "development" || env === "production" ;
72+ } ;
73+
74+ /**
75+ * Vite plugin to run custom commands on demand.
76+ * @param {CustomCommandsPluginOptions } [options={}] - The plugin options.
77+
78+ */
79+ export default function customCommandsPlugin ( options = { } ) {
80+ return {
81+ name : pluginName ,
82+ configureServer ( server ) {
83+ if ( ! isAllowedEnvironment ( ) ) return ;
84+ server . httpServer ?. once ( "listening" , async ( ) => {
85+ await executeCommand (
86+ options . beforeServerStart ,
87+ `Error running beforeServerStart command: ${ options . beforeServerStart } `
88+ ) ;
89+ await executeCommand (
90+ options . afterServerStart ,
91+ `Error running afterServerStart command: ${ options . afterServerStart } `
92+ ) ;
93+ } ) ;
94+ } ,
95+ async handleHotUpdate ( ctx ) {
96+ if ( ! isAllowedEnvironment ( ) ) return ctx . modules ;
97+ const isPageReload = ctx . modules . some ( ( module ) => ! module . isSelfAccepting ) ;
98+ if ( ! isPageReload ) {
99+ await executeCommand (
100+ options . onHotUpdate ,
101+ `Error running onHotUpdate command: ${ options . onHotUpdate } `
102+ ) ;
103+ }
104+ return ctx . modules ;
105+ } ,
106+ async buildStart ( ) {
107+ if ( ! isAllowedEnvironment ( ) ) return ;
108+ await executeCommand (
109+ options . beforeBuild ,
110+ `Error running beforeBuild command: ${ options . beforeBuild } `
111+ ) ;
112+ } ,
113+ async buildEnd ( ) {
114+ if ( ! isAllowedEnvironment ( ) ) return ;
115+ await executeCommand (
116+ options . afterBuild ,
117+ `Error running afterBuild command: ${ options . afterBuild } `
118+ ) ;
119+ } ,
120+ async closeBundle ( ) {
121+ if ( ! isAllowedEnvironment ( ) ) return ;
122+ await executeCommand (
123+ options . closeBundle ,
124+ `Error running closeBundle command: ${ options . closeBundle } `
125+ ) ;
126+ } ,
127+ } ;
128+ }
0 commit comments