1
- import path from "node:path" ;
2
1
import { parseArgs } from "node:util" ;
3
2
import builtins from "builtin-modules" ;
4
- import { $ , type BunPlugin } from "bun" ;
5
- import { ResultAsync , okAsync } from "neverthrow" ;
3
+ import {
4
+ $ ,
5
+ type BuildConfig ,
6
+ type BuildOutput ,
7
+ type BunFile ,
8
+ type BunPlugin ,
9
+ } from "bun" ;
10
+ import { ResultAsync , errAsync , okAsync } from "neverthrow" ;
6
11
import type { IPackageJson } from "package-json-type" ;
7
12
import { resolveTsPaths } from "resolve-tspaths" ;
8
13
import { targetStylesFile } from "./common" ;
9
- import { BuildError , FileSystemError } from "./errors" ;
14
+ import { Code , ScriptError } from "./errors" ;
10
15
import {
16
+ createFolder ,
17
+ isUndefined ,
11
18
readJsonFile ,
12
19
readTextFile ,
13
20
setupTestVault ,
14
21
writeFile ,
15
22
} from "./helpers" ;
23
+ import { InlineGraphQlBunPlugin } from "./plugins/graphql" ;
24
+ import { CompiledHandlebarsTemplateBunPlugin } from "./plugins/handlebars" ;
25
+ import { InlineSqlBunPlugin } from "./plugins/sql" ;
26
+ import { InlineWasmBunPlugin } from "./plugins/wasm" ;
16
27
17
28
const { values : args } = parseArgs ( {
18
29
args : Bun . argv ,
@@ -37,112 +48,6 @@ const { values: args } = parseArgs({
37
48
38
49
const outputFolder = "dist" ;
39
50
40
- export const InlineWasmBunPlugin : BunPlugin = {
41
- name : "inline-wasm" ,
42
- setup ( builder ) {
43
- // Hook into the "resolve" phase to intercept .wasm imports
44
- builder . onResolve ( { filter : / \. w a s m $ / } , async ( args ) => {
45
- // Resolve the .wasm file path relative to the directory of the importing file
46
- const resolvedPath = Bun . resolveSync (
47
- args . path ,
48
- path . dirname ( args . importer ) ,
49
- ) ;
50
- return { path : resolvedPath , namespace : "wasm" } ;
51
- } ) ;
52
-
53
- // Handle the .wasm file loading
54
- builder . onLoad (
55
- { filter : / \. w a s m $ / , namespace : "wasm" } ,
56
- async ( args ) => {
57
- const wasmFile = await Bun . file ( args . path ) . bytes ( ) ;
58
- const wasm = Buffer . from ( wasmFile ) . toString ( "base64" ) ;
59
-
60
- // Create the inline WebAssembly module
61
- const contents = `
62
- const wasmBinary = Uint8Array.from(atob("${ wasm } "), c => c.charCodeAt(0));
63
- export default wasmBinary;
64
- ` ;
65
- return { contents, loader : "js" } ;
66
- } ,
67
- ) ;
68
- } ,
69
- } ;
70
-
71
- export const InlineSqlBunPlugin : BunPlugin = {
72
- name : "inline-sql" ,
73
- setup ( builder ) {
74
- // Hook into the "resolve" phase to intercept .sql imports
75
- builder . onResolve ( { filter : / \. s q l $ / } , async ( args ) => {
76
- // Resolve the .sql file path relative to the directory of the importing file
77
- const resolvedPath = Bun . resolveSync (
78
- args . path ,
79
- path . dirname ( args . importer ) ,
80
- ) ;
81
- return { path : resolvedPath , namespace : "sql" } ;
82
- } ) ;
83
-
84
- // Handle the .sql file loading
85
- builder . onLoad ( { filter : / \. s q l $ / , namespace : "sql" } , async ( args ) => {
86
- const sqlFileContent = await Bun . file ( args . path ) . text ( ) ;
87
- const contents = `const sqlQuery = \`${ sqlFileContent } \`;
88
- export default sqlQuery;` ;
89
- return { contents, loader : "js" } ;
90
- } ) ;
91
- } ,
92
- } ;
93
-
94
- export const InlineGraphQlBunPlugin : BunPlugin = {
95
- name : "inline-graphql" ,
96
- setup ( builder ) {
97
- // Hook into the "resolve" phase to intercept .gql imports
98
- builder . onResolve ( { filter : / \. g q l $ / } , async ( args ) => {
99
- // Resolve the .gql file path relative to the directory of the importing file
100
- const resolvedPath = Bun . resolveSync (
101
- args . path ,
102
- path . dirname ( args . importer ) ,
103
- ) ;
104
- return { path : resolvedPath , namespace : "graphql" } ;
105
- } ) ;
106
-
107
- // Handle the .gql file loading
108
- builder . onLoad (
109
- { filter : / \. g q l $ / , namespace : "graphql" } ,
110
- async ( args ) => {
111
- const graphQlQueryFileContent = await Bun . file (
112
- args . path ,
113
- ) . text ( ) ;
114
- const contents = `const graphQlQuery = \`${ graphQlQueryFileContent } \`;
115
- export default graphQlQuery;` ;
116
- return { contents, loader : "js" } ;
117
- } ,
118
- ) ;
119
- } ,
120
- } ;
121
-
122
- export const CompiledHandlebarsTemplateBunPlugin : BunPlugin = {
123
- name : "compile-handlebars" ,
124
- setup ( builder ) {
125
- builder . onResolve ( { filter : / \. h b s $ / } , async ( args ) => {
126
- const resolvedPath = Bun . resolveSync (
127
- args . path ,
128
- path . dirname ( args . importer ) ,
129
- ) ;
130
- return { path : resolvedPath , namespace : "handlebars" } ;
131
- } ) ;
132
-
133
- builder . onLoad (
134
- { filter : / \. h b s $ / , namespace : "handlebars" } ,
135
- async ( args ) => {
136
- const handlebarsFileContent = await Bun . file ( args . path ) . text ( ) ;
137
- const contents = `import Handlebars from "handlebars";
138
- const graphQlQuery = Handlebars.compile(\`${ handlebarsFileContent } \`, { strict: true });
139
- export default graphQlQuery;` ;
140
- return { contents, loader : "js" } ;
141
- } ,
142
- ) ;
143
- } ,
144
- } ;
145
-
146
51
export interface WasmBuildConfig {
147
52
target : "bundler" | "nodejs" | "web" | "no-modules" | "deno" ;
148
53
path : string ;
@@ -153,14 +58,14 @@ export function buildWasm(
153
58
target : "web" ,
154
59
path : "./pkg" ,
155
60
} ,
156
- ) {
61
+ ) : ResultAsync < number , ScriptError < Code . Build | Code . FileSystem > > {
157
62
console . log ( "Building Rust WASM" ) ;
158
- const wasmPackBuild = ResultAsync . fromPromise (
159
- $ `wasm-pack build --target ${ config . target } ` ,
160
- ( ) => BuildError . WasmPackBuildFailed ,
63
+ const wasmPackBuild = ResultAsync . fromThrowable (
64
+ ( target : string ) => $ `wasm-pack build --target ${ target } ` ,
65
+ ( ) => new ScriptError ( Code . Build . WasmPackBuildFailed ) ,
161
66
) ;
162
67
return (
163
- wasmPackBuild
68
+ wasmPackBuild ( config . target )
164
69
. andThen ( ( ) =>
165
70
readJsonFile < IPackageJson > ( `${ config . path } /package.json` ) ,
166
71
)
@@ -180,7 +85,7 @@ export function buildWasm(
180
85
) ;
181
86
}
182
87
183
- export interface BuildConfig {
88
+ export interface PluginBuildConfig {
184
89
sourceFolder : string ;
185
90
entrypoints : {
186
91
main : string ;
@@ -202,95 +107,110 @@ const defaultBunPlugins = [
202
107
CompiledHandlebarsTemplateBunPlugin ,
203
108
] ;
204
109
205
- export function build ( config : BuildConfig ) {
206
- const createOutputFolder = ResultAsync . fromPromise (
207
- $ `mkdir -p ${ config . outputFolder } ` ,
208
- ( error ) => {
209
- console . error ( `ERROR. ${ error } ` ) ;
210
- return FileSystemError . FileSystemError ;
211
- } ,
110
+ export function buildStyles (
111
+ source : BunFile ,
112
+ destination : BunFile ,
113
+ ) : ResultAsync < string , ScriptError < Code . Build > > {
114
+ console . log ( "Building styles" ) ;
115
+ return ResultAsync . fromThrowable (
116
+ ( s : BunFile , d : BunFile ) => $ `grass ${ s } --style compressed > ${ d } ` ,
117
+ ( ) => new ScriptError ( Code . Build . UnableToBuildStylesFiles ) ,
118
+ ) ( source , destination ) . map ( ( shellOutput ) => shellOutput . text ( ) ) ;
119
+ }
120
+
121
+ export function buildJavaScript (
122
+ config : Partial < PluginBuildConfig > ,
123
+ ) : ResultAsync < BuildOutput , ScriptError < Code . Build > > {
124
+ if ( isUndefined ( config . entrypoints ) ) {
125
+ return errAsync (
126
+ new ScriptError ( Code . Build . UnableToBuildJavaScriptFiles ) ,
127
+ ) ;
128
+ }
129
+
130
+ console . log (
131
+ `Building JavaScript: ${ config . sourceFolder } /${ config . entrypoints . main } (output: ${ config . outputFolder } )` ,
132
+ ) ;
133
+
134
+ const buildConfig : BuildConfig = {
135
+ entrypoints : [ `${ config . sourceFolder } /${ config . entrypoints . main } ` ] ,
136
+ outdir : config . outputFolder ,
137
+ minify : config . minify ,
138
+ target : "browser" ,
139
+ format : config . format ,
140
+ plugins : config . useWasm
141
+ ? [ InlineWasmBunPlugin , ...defaultBunPlugins ]
142
+ : defaultBunPlugins ,
143
+ drop : config . drop ,
144
+ sourcemap : config . sourcemap ? "inline" : "none" ,
145
+ external : [
146
+ "obsidian" ,
147
+ "electron" ,
148
+ "@electron/remote" ,
149
+ "@codemirror/autocomplete" ,
150
+ "@codemirror/collab" ,
151
+ "@codemirror/commands" ,
152
+ "@codemirror/language" ,
153
+ "@codemirror/lint" ,
154
+ "@codemirror/search" ,
155
+ "@codemirror/state" ,
156
+ "@codemirror/view" ,
157
+ "@lezer/common" ,
158
+ "@lezer/highlight" ,
159
+ "@lezer/lr" ,
160
+ ...builtins ,
161
+ ] ,
162
+ } ;
163
+
164
+ return ResultAsync . fromThrowable (
165
+ ( c : BuildConfig ) => Bun . build ( c ) ,
166
+ ( error ) => new ScriptError ( Code . Build . UnableToBuildJavaScriptFiles ) ,
167
+ ) ( buildConfig ) ;
168
+ }
169
+
170
+ export function buildTypeScriptDeclarations (
171
+ sourceFolder : string ,
172
+ outputFolder : string ,
173
+ ) : ResultAsync < string , ScriptError < Code . Build > > {
174
+ console . log ( "Building types" ) ;
175
+ const buildDeclarations = ResultAsync . fromThrowable (
176
+ ( folder : string ) =>
177
+ $ `bun tsc --noEmit false --emitDeclarationOnly --declaration --outDir ${ folder } /types` ,
178
+ ( ) => new ScriptError ( Code . Build . UnableToBuildTypesDeclarations ) ,
212
179
) ;
180
+ const resolvePaths = ResultAsync . fromThrowable (
181
+ ( src : string , out : string ) =>
182
+ resolveTsPaths ( {
183
+ src,
184
+ out : `${ out } /types` ,
185
+ } ) ,
186
+ ( error ) => new ScriptError ( Code . Build . UnableToResolveTypeScriptPaths ) ,
187
+ ) ;
188
+ return buildDeclarations ( outputFolder )
189
+ . andThrough ( ( ) => resolvePaths ( sourceFolder , outputFolder ) )
190
+ . map ( ( shellOutput ) => shellOutput . text ( ) ) ;
191
+ }
213
192
214
- return createOutputFolder
193
+ export function build ( config : PluginBuildConfig ) {
194
+ return createFolder ( config . outputFolder )
215
195
. andThrough ( ( ) => {
216
196
if ( config . wasmBuildConfig ) {
217
197
return buildWasm ( config . wasmBuildConfig ) ;
218
198
}
219
199
return okAsync ( ) ;
220
200
} )
221
- . andThrough ( ( ) => {
222
- console . log ( "Building styles" ) ;
223
- return ResultAsync . fromPromise (
224
- $ `grass ${ Bun . file ( `${ config . sourceFolder } /${ config . entrypoints . styles } ` ) } --style compressed > ${ Bun . file ( `${ config . outputFolder } /${ targetStylesFile } ` ) } ` ,
225
- ( error ) => {
226
- console . error ( `ERROR. ${ error } ` ) ;
227
- return BuildError . UnableToBuildStylesFiles ;
228
- } ,
229
- ) ;
230
- } )
231
- . andThen ( ( ) => {
232
- console . log (
233
- `Building main: ${ config . sourceFolder } /${ config . entrypoints . main } (output: ${ config . outputFolder } )` ,
234
- ) ;
235
- return ResultAsync . fromPromise (
236
- Bun . build ( {
237
- entrypoints : [
238
- `${ config . sourceFolder } /${ config . entrypoints . main } ` ,
239
- ] ,
240
- outdir : config . outputFolder ,
241
- minify : config . minify ,
242
- target : "browser" ,
243
- format : config . format ,
244
- plugins : config . useWasm
245
- ? [ InlineWasmBunPlugin , ...defaultBunPlugins ]
246
- : defaultBunPlugins ,
247
- drop : config . drop ,
248
- sourcemap : config . sourcemap ? "inline" : "none" ,
249
- external : [
250
- "obsidian" ,
251
- "electron" ,
252
- "@electron/remote" ,
253
- "@codemirror/autocomplete" ,
254
- "@codemirror/collab" ,
255
- "@codemirror/commands" ,
256
- "@codemirror/language" ,
257
- "@codemirror/lint" ,
258
- "@codemirror/search" ,
259
- "@codemirror/state" ,
260
- "@codemirror/view" ,
261
- "@lezer/common" ,
262
- "@lezer/highlight" ,
263
- "@lezer/lr" ,
264
- ...builtins ,
265
- ] ,
266
- } ) ,
267
- ( error ) => {
268
- console . error ( `ERROR. ${ error } ` ) ;
269
- return BuildError . UnableToBuildJavaScriptFiles ;
270
- } ,
271
- ) ;
272
- } )
201
+ . andThrough ( ( ) =>
202
+ buildStyles (
203
+ Bun . file ( `${ config . sourceFolder } /${ config . entrypoints . styles } ` ) ,
204
+ Bun . file ( `${ config . outputFolder } /${ targetStylesFile } ` ) ,
205
+ ) ,
206
+ )
207
+ . andThen ( ( ) => buildJavaScript ( config ) )
273
208
. andThrough ( ( ) => {
274
209
if ( config . generateTypes ) {
275
210
// Build typescript declaration files
276
- console . log ( "Building types" ) ;
277
- return ResultAsync . fromPromise (
278
- $ `bun tsc --noEmit false --emitDeclarationOnly --declaration --outDir ${ config . outputFolder } /types` ,
279
- ( error ) => {
280
- console . error ( `ERROR. ${ error } ` ) ;
281
- return BuildError . UnableToBuildTypesDeclarations ;
282
- } ,
283
- ) . andThrough ( ( ) =>
284
- ResultAsync . fromPromise (
285
- resolveTsPaths ( {
286
- src : config . sourceFolder ,
287
- out : `${ config . outputFolder } /types` ,
288
- } ) ,
289
- ( error ) => {
290
- console . error ( `ERROR. ${ error } ` ) ;
291
- return BuildError . UnableToResolveTypeScriptPaths ;
292
- } ,
293
- ) ,
211
+ return buildTypeScriptDeclarations (
212
+ config . sourceFolder ,
213
+ config . outputFolder ,
294
214
) ;
295
215
}
296
216
return okAsync ( ) ;
@@ -302,7 +222,8 @@ await build({
302
222
entrypoints : { main : "main.ts" , styles : "styles/index.scss" } ,
303
223
outputFolder,
304
224
format : "cjs" ,
305
- drop : args . dev ? [ ] : [ "console" ] ,
225
+ // drop: args.dev ? [] : ["console"],
226
+ drop : [ ] ,
306
227
generateTypes : false ,
307
228
useWasm : true ,
308
229
minify : ! args [ "no-minify" ] ,
@@ -320,6 +241,6 @@ await build({
320
241
} )
321
242
. andTee ( ( ) => console . log ( "Done!" ) )
322
243
. orElse ( ( error ) => {
323
- console . error ( `Build failed. Reason: ${ error } ` ) ;
244
+ error . log ( ) ;
324
245
process . exit ( 1 ) ;
325
246
} ) ;
0 commit comments