11import fs from 'fs/promises' ;
22import { parse } from 'acorn' ;
33import { generate } from 'astring' ;
4- import type { BuildOptions as ESBuildOptions , OutputFile } from 'esbuild' ;
4+ import chalk from 'chalk' ;
5+ import type { BuildOptions as ESBuildOptions , OutputFile , Plugin as ESBuildPlugin } from 'esbuild' ;
56import type es from 'estree' ;
67import type { BuildResult , InputAsset } from '../../types.js' ;
78
@@ -26,12 +27,15 @@ export const commonEsbuildOptions = {
2627} satisfies ESBuildOptions ;
2728// #endregion esbuildOptions
2829
29- /**
30- * Write the compiled output from ESBuild to the file system after performing AST transformation
31- */
32- export async function outputBundleOrTab ( { text } : OutputFile , input : InputAsset , outDir : string ) : Promise < BuildResult > {
33- const parsed = parse ( text , { ecmaVersion : 6 } ) as es . Program ;
30+ type ConvertAstResult = {
31+ severity : 'error' ,
32+ error : string
33+ } | {
34+ severity : 'success'
35+ output : es . Node
36+ } ;
3437
38+ function convertAst ( parsed : es . Program ) : ConvertAstResult {
3539 // Account for 'use strict'; directives
3640 let declStatement : es . VariableDeclaration ;
3741 if ( parsed . body [ 0 ] . type === 'VariableDeclaration' ) {
@@ -43,22 +47,18 @@ export async function outputBundleOrTab({ text }: OutputFile, input: InputAsset,
4347 const { init : callExpression } = declStatement . declarations [ 0 ] ;
4448 if ( callExpression ?. type !== 'CallExpression' ) {
4549 return {
46- type : input . type ,
4750 severity : 'error' ,
48- input,
49- errors : [ `parse failure: Expected a CallExpression, got ${ callExpression ?. type ?? callExpression } ` ]
50- } as BuildResult ;
51+ error : `parse failure: Expected a CallExpression, got ${ callExpression ?. type ?? callExpression } `
52+ } ;
5153 }
5254
5355 const moduleCode = callExpression . callee ;
5456
5557 if ( moduleCode . type !== 'FunctionExpression' && moduleCode . type !== 'ArrowFunctionExpression' ) {
5658 return {
57- type : input . type ,
5859 severity : 'error' ,
59- input,
60- errors : [ `${ input . type } ${ input . name } parse failure: Expected a function, got ${ moduleCode . type } ` ]
61- } as BuildResult ;
60+ error : `parse failure: Expected a function, got ${ moduleCode . type } ` ,
61+ } ;
6262 }
6363
6464 const output : es . ExportDefaultDeclaration = {
@@ -72,6 +72,29 @@ export async function outputBundleOrTab({ text }: OutputFile, input: InputAsset,
7272 }
7373 } ;
7474
75+ return {
76+ severity : 'success' ,
77+ output
78+ } ;
79+ }
80+
81+ /**
82+ * Write the compiled output from ESBuild to the file system after performing AST transformation
83+ */
84+ export async function outputBundleOrTab ( { text } : OutputFile , input : InputAsset , outDir : string ) : Promise < BuildResult > {
85+ const parsed = parse ( text , { ecmaVersion : 6 } ) as es . Program ;
86+
87+ const astResult = convertAst ( parsed ) ;
88+ if ( astResult . severity === 'error' ) {
89+ return {
90+ type : input . type ,
91+ severity : 'error' ,
92+ input,
93+ errors : [ `${ input . type } ${ input . name } ${ astResult . error } ` ]
94+ } as BuildResult ;
95+ }
96+
97+ const { output } = astResult ;
7598 const outputDirectory = `${ outDir } /${ input . type } s` ;
7699 await fs . mkdir ( outputDirectory , { recursive : true } ) ;
77100
@@ -93,3 +116,30 @@ export async function outputBundleOrTab({ text }: OutputFile, input: InputAsset,
93116 await file ?. close ( ) ;
94117 }
95118}
119+
120+ export function builderPlugin ( input : InputAsset , outDir : string ) : ESBuildPlugin {
121+ return {
122+ name : 'Builder Plugin' ,
123+ async setup ( { initialOptions, onEnd } ) {
124+ if ( initialOptions . write !== false ) {
125+ throw new Error ( 'Plugin must be used with write: false' ) ;
126+ }
127+
128+ const outpath = `${ outDir } /${ input . name } .js` ;
129+ const file = await fs . open ( outpath , 'w' ) ;
130+ const writeStream = file . createWriteStream ( ) ;
131+
132+ onEnd ( result => {
133+ const [ { text } ] = result . outputFiles ! ;
134+ const parsed = parse ( text , { ecmaVersion : 6 } ) as es . Program ;
135+ const astResult = convertAst ( parsed ) ;
136+ if ( astResult . severity === 'success' ) {
137+ generate ( astResult . output , { output : writeStream } ) ;
138+ console . log ( chalk . greenBright ( `Output written to ${ outpath } ` ) ) ;
139+ } else {
140+ console . error ( chalk . redBright ( astResult . error ) ) ;
141+ }
142+ } ) ;
143+ }
144+ } ;
145+ }
0 commit comments