11import { copyFile , readFile , rm , writeFile } from "node:fs/promises" ;
2- import { dirname , join } from "node:path" ;
2+ import { basename , dirname , join } from "node:path" ;
33import { fileURLToPath } from "node:url" ;
44
55import * as esbuild from "esbuild" ;
@@ -62,15 +62,22 @@ const onEndPlugin = {
6262 } ,
6363} ;
6464
65+ /** The name of the virtual `entry-points` module. */
66+ const SHARED_ENTRYPOINT = "entry-points" ;
67+
6568/**
66- * Emit a tiny stub file for each Action entrypoint. Each stub imports the shared bundle
69+ * This plugin finds all source files that contain action entry points.
70+ * It then generates the virtual `entry-points` module which imports all identifies files,
71+ * and re-exports their `runWrapper` functions with suitable aliases.
72+ * A tiny stub file is emitted for each Action entrypoint. Each stub imports the shared bundle
6773 * and calls the respective entry point.
6874 *
6975 * @type {esbuild.Plugin }
7076 */
7177const entryPointsPlugin = {
7278 name : "entry-points" ,
7379 setup ( build ) {
80+ const namespace = "actions" ;
7481 const actions = [ ] ;
7582
7683 const toPascal = ( s ) =>
@@ -93,6 +100,44 @@ const entryPointsPlugin = {
93100 }
94101 } ) ;
95102
103+ // Resolve the virtual `entry-points` file and set the corresponding namespace.
104+ // Ideally, we'd `RegExp.escape` the entrypoint here, but that API isn't supported in Node 20.
105+ // Since we're dealing with a hardcoded string, this isn't too much of a problem.
106+ build . onResolve ( { filter : new RegExp ( `^${ SHARED_ENTRYPOINT } $` ) } , ( ) => {
107+ return { path : SHARED_ENTRYPOINT , namespace } ;
108+ } ) ;
109+
110+ // Generate the virtual `entry-points` file based on the actions we discovered.
111+ // Restrict using the namespace. The path filter does not need to discriminate any further.
112+ build . onLoad ( { filter : / .* / , namespace } , async ( ) => {
113+ const wrapperTemplatePath = "entry-wrapper.js.tpl" ;
114+ const wrapperTemplate = await readFile (
115+ join ( SRC_DIR , wrapperTemplatePath ) ,
116+ "utf-8" ,
117+ ) ;
118+
119+ const actionsSorted = actions . sort ( ( a , b ) =>
120+ a . name . localeCompare ( b . name ) ,
121+ ) ;
122+ const imports = actionsSorted
123+ . map (
124+ ( action ) =>
125+ `import * as ${ action . pascalCaseName } from "./src/${ basename ( action . path ) } "` ,
126+ )
127+ . join ( "\n" ) ;
128+ const wrappers = actionsSorted
129+ . map ( ( action ) =>
130+ wrapperTemplate . replaceAll ( "__ACTION__" , action . pascalCaseName ) ,
131+ )
132+ . join ( "\n\n" ) ;
133+
134+ return {
135+ contents : `"use strict";\n${ imports } \n\n${ wrappers } \n` ,
136+ resolveDir : "." ,
137+ loader : "ts" ,
138+ } ;
139+ } ) ;
140+
96141 // Emit entry point stubs for each action using the entry template.
97142 build . onEnd ( async ( result ) => {
98143 // Read the entry point template.
@@ -119,7 +164,10 @@ const entryPointsPlugin = {
119164
120165const context = await esbuild . context ( {
121166 // Include upload-lib.ts as an entry point for use in testing environments.
122- entryPoints : globSync ( [ "src/entry-points.ts" , "src/upload-lib.ts" ] ) ,
167+ entryPoints : [
168+ { in : SHARED_ENTRYPOINT , out : SHARED_ENTRYPOINT } ,
169+ join ( SRC_DIR , "upload-lib.ts" ) ,
170+ ] ,
123171 bundle : true ,
124172 format : "cjs" ,
125173 outdir : OUT_DIR ,
0 commit comments