11import { copyFile , 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" ;
@@ -13,6 +13,71 @@ const __dirname = dirname(__filename);
1313const SRC_DIR = join ( __dirname , "src" ) ;
1414const OUT_DIR = join ( __dirname , "lib" ) ;
1515
16+ /**
17+ * Name of the shared entrypoint file that imports each Action's code. By introducing a single
18+ * entrypoint for all the Actions, we avoid duplicating code across each Action's bundle.
19+ */
20+ const SHARED_ENTRYPOINT = "actions-entrypoint" ;
21+
22+ /** The names of all the Action entry points (as referenced by `action.yml`s). */
23+ function findActionNames ( ) {
24+ return globSync ( [
25+ `${ SRC_DIR } /*-action.ts` ,
26+ `${ SRC_DIR } /*-action-post.ts` ,
27+ ] )
28+ . map ( ( p ) => basename ( p , ".ts" ) )
29+ . sort ( ) ;
30+ }
31+
32+ const ACTION_NAMES = findActionNames ( ) ;
33+
34+ /**
35+ * Generate the source for the shared entry point. The generated module dispatches at runtime to the
36+ * Action selected by `CODEQL_ACTION_ENTRYPOINT`, using `require()` to incorporate each Action's
37+ * code without executing the top-level side effects.
38+ */
39+ function generateEntrypointTypescriptSource ( ) {
40+ const cases = ACTION_NAMES
41+ . map (
42+ ( name ) =>
43+ ` case ${ JSON . stringify ( name ) } :\n require("./${ name } ");\n break;` ,
44+ )
45+ . join ( "\n" ) ;
46+ return `const entrypoint = process.env.CODEQL_ACTION_ENTRYPOINT;
47+ switch (entrypoint) {
48+ ${ cases }
49+ default:
50+ throw new Error(
51+ \`Unknown CodeQL Action entrypoint: \${JSON.stringify(entrypoint)}. \` +
52+ "This file is intended to be invoked via the generated stubs in lib/.",
53+ );
54+ }
55+ ` ;
56+ }
57+
58+ /**
59+ * Resolve the virtual shared entry point and provide its generated source to esbuild without
60+ * writing it to disk.
61+ *
62+ * @type {esbuild.Plugin }
63+ */
64+ const virtualEntrypointPlugin = {
65+ name : "virtual-actions-entrypoint" ,
66+ setup ( build ) {
67+ const namespace = "actions-entrypoint" ;
68+ build . onResolve ( { filter : new RegExp ( `^${ RegExp . escape ( SHARED_ENTRYPOINT ) } $` ) } , ( ) => ( {
69+ path : SHARED_ENTRYPOINT ,
70+ namespace,
71+ } ) ) ;
72+ // Restrict using the namespace. The path filter does not need to discriminate any further.
73+ build . onLoad ( { filter : / .* / , namespace } , ( ) => ( {
74+ contents : generateEntrypointTypescriptSource ( ) ,
75+ resolveDir : SRC_DIR ,
76+ loader : "ts" ,
77+ } ) ) ;
78+ } ,
79+ } ;
80+
1681/**
1782 * Clean the output directory before building.
1883 *
@@ -62,18 +127,48 @@ const onEndPlugin = {
62127 } ,
63128} ;
64129
130+ /**
131+ * Emit a tiny stub file for each Action entrypoint. Each stub sets an environment variable
132+ * identifying which action was invoked and then `require()`s the shared bundle, which dispatches to
133+ * the correct Action's code.
134+ *
135+ * @type {esbuild.Plugin }
136+ */
137+ const emitActionStubsPlugin = {
138+ name : "emit-action-stubs" ,
139+ setup ( build ) {
140+ build . onEnd ( async ( ) => {
141+ await Promise . all (
142+ ACTION_NAMES . map ( async ( name ) => {
143+ const stub =
144+ `"use strict";\n` +
145+ `process.env.CODEQL_ACTION_ENTRYPOINT = ${ JSON . stringify ( name ) } ;\n` +
146+ `require("./${ SHARED_ENTRYPOINT } .js");\n` ;
147+ await writeFile ( join ( OUT_DIR , `${ name } .js` ) , stub ) ;
148+ } ) ,
149+ ) ;
150+ } ) ;
151+ } ,
152+ } ;
153+
65154const context = await esbuild . context ( {
66- // Include upload-lib.ts as an entry point for use in testing environments.
67- entryPoints : globSync ( [
68- ` ${ SRC_DIR } /*-action.ts` ,
69- ` ${ SRC_DIR } /*-action-post.ts` ,
70- "src/ upload-lib.ts",
71- ] ) ,
155+ // Bundle every action together via the shared entry point. We also keep
156+ // `upload-lib.ts` as a separate entry point for use in testing environments.
157+ entryPoints : [
158+ { in : SHARED_ENTRYPOINT , out : SHARED_ENTRYPOINT } ,
159+ join ( SRC_DIR , " upload-lib.ts") ,
160+ ] ,
72161 bundle : true ,
73162 format : "cjs" ,
74163 outdir : OUT_DIR ,
75164 platform : "node" ,
76- plugins : [ cleanPlugin , copyDefaultsPlugin , onEndPlugin ] ,
165+ plugins : [
166+ cleanPlugin ,
167+ copyDefaultsPlugin ,
168+ virtualEntrypointPlugin ,
169+ emitActionStubsPlugin ,
170+ onEndPlugin ,
171+ ] ,
77172 target : [ "node20" ] ,
78173 define : {
79174 __CODEQL_ACTION_VERSION__ : JSON . stringify ( pkg . version ) ,
0 commit comments