1- // import * as url from 'node:url'
2- import * as fs from 'node:fs'
3- import * as cp from 'node:child_process'
4- // import * as path from 'node:path'
5- // import * as assert from 'node:assert/strict'
6- // import * as threads from 'node:worker_threads'
7- import * as esb from 'esbuild'
8- // import ts from 'typescript'
9-
10- function is_env_truthy ( value : string | undefined ) : boolean {
11- if ( ! value ) return false
12- value = value . toLowerCase ( ) . trim ( )
13- return value === 'true'
14- || value === '"1"'
15- || value === '1'
16- || value === 'yes'
17- || value === 'y'
18- }
1+ import * as url from 'node:url'
2+ import * as fs from 'node:fs'
3+ import * as cp from 'node:child_process'
4+ import * as path from 'node:path'
5+ import * as esb from 'esbuild'
196
20- export const CI = is_env_truthy ( process . env [ 'CI' ] )
21- || is_env_truthy ( process . env [ 'GITHUB_ACTIONS' ] )
22- || ! ! process . env [ 'TURBO_HASH' ]
23-
24- // type Worker_Data = {
25- // is_dev: boolean,
26- // base_path: string,
27- // dist_path: string,
28- // ts_entries: string[],
29- // }
30-
31- // const filename = url.fileURLToPath(import.meta.url)
32- // const dirname = path.dirname(filename)
33-
34- export const DEFAULT_EXTERNAL_DEPS : string [ ] = [
35- 'solid-js' ,
36- 'solid-js/*' ,
37- '@solid-devtools/shared/*' ,
38- ]
39-
40- export function get_external_deps_from_pkg ( pkg_filename : string ) : string [ ] {
41- let pkg = JSON . parse ( fs . readFileSync ( pkg_filename ) as any ) as any
42- let deps = Object . keys ( { ...pkg ?. peerDependencies , ...pkg ?. dependencies } )
43- deps . push ( ...DEFAULT_EXTERNAL_DEPS )
44- return deps
45- }
7+ import {
8+ CI ,
9+ get_is_dev_from_args ,
10+ type Package_Json ,
11+ } from './build_shared.ts'
4612
47- export function get_is_dev_from_args ( ) : boolean {
48- return process . argv . includes ( '--watch' )
49- }
50-
51- export function get_common_esbuild_options ( is_dev : boolean , dist_dirname : string ) : esb . BuildOptions {
52- return {
53- platform : 'browser' ,
54- format : 'esm' ,
55- target : 'esnext' ,
56- sourcemap : is_dev ,
57- outdir : dist_dirname ,
58- bundle : true ,
59- splitting : true ,
60- treeShaking : ! is_dev ,
61- logLevel : is_dev ? 'debug' : 'warning' ,
62- color : true ,
63- dropLabels : [ is_dev ? 'PROD' : 'DEV' ] ,
64- }
65- }
6613
67- export async function build (
68- options : esb . BuildOptions [ ] ,
69- is_dev : boolean ,
70- base_path : string ,
71- dist_path : string ,
72- ) : Promise < void > {
14+ const is_dev = get_is_dev_from_args ( )
7315
74- /* Clear dist when building to prod */
75- if ( ! is_dev ) {
76- fs . rmSync ( dist_path , { recursive : true , force : true } )
77- }
16+ const filename = url . fileURLToPath ( import . meta. url )
17+ const dirname = path . dirname ( filename )
7818
79- let tsc_args = [ ]
8019
20+ /*
21+ Spawn separate tsc process
22+ */
23+ {
24+ let tsc_args = [ 'pnpm' , 'build:types' ]
25+
8126 if ( CI ) {
8227 tsc_args . push ( '--noEmitOnError' )
8328 }
8429 if ( is_dev ) {
8530 tsc_args . push ( '--watch' , '--preserveWatchOutput' )
8631 }
87-
88- let tsc_process = cp . spawn ( 'tsc' , tsc_args , {
89- cwd : base_path ,
90- stdio : 'inherit' ,
91- } )
92-
32+
33+ let tsc_process = cp . spawn ( tsc_args [ 0 ] ! , tsc_args . slice ( 1 ) , { stdio : 'inherit' } )
34+
9335 tsc_process . on ( 'error' , error => {
9436 // eslint-disable-next-line no-console
9537 console . error ( 'TSC process error:' , error )
9638 if ( ! is_dev ) process . exit ( 1 )
9739 } )
40+ }
9841
99- // const worker = new threads.Worker(filename, {
100- // workerData: {is_dev, dist_path, base_path, ts_entries} satisfies Worker_Data,
101- // argv: process.argv,
102- // env: process.env,
103- // })
10442
105- // worker.on('error', (error) => {
106- // // eslint-disable-next-line no-console
107- // console.error(`Worker error:`, error)
108- // })
43+ /*
44+ Build all packages with esbuild
45+ using options exported from /packages/ * /build.ts
46+ */
10947
110- /* Watch - never terminates */
111- if ( is_dev ) {
112- for ( const option of options ) {
113- esb . context ( option )
114- . then ( ctx => ctx . watch ( ) )
115- }
116- }
117- /* Build once - wait for all to finish */
118- else {
119- let begin = performance . now ( )
120- await Promise . all ( options . map ( option => esb . build ( option ) ) )
121- // eslint-disable-next-line no-console
122- console . log ( `JS built in ${ ( performance . now ( ) - begin ) . toFixed ( 2 ) } ms` )
48+ const packages_dirname = path . join ( dirname , 'packages' )
49+
50+ const packages_dirents = fs . readdirSync ( packages_dirname , { withFileTypes : true } )
51+ const packages_names = [ ]
52+ for ( let dirent of packages_dirents ) {
53+ if ( dirent . isDirectory ( ) && dirent . name !== 'types' && dirent . name !== 'dist' ) {
54+ packages_names . push ( dirent . name )
12355 }
12456}
12557
126- // function main() {
127-
128- // if (threads.isMainThread)
129- // return
130-
131- // /* Worker - runs the ts program */
132-
133- // const data = threads.workerData as Worker_Data
134-
135- // const port = threads.parentPort
136- // assert.ok(port != null)
137-
138- // const options = get_tsc_options(data.base_path)
139-
140- // /* Watch - never terminates */
141- // if (data.is_dev) {
142- // const host = ts.createWatchCompilerHost(
143- // data.ts_entries,
144- // options,
145- // ts.sys,
146- // undefined,
147- // report_diagnostic,
148- // report_watch_status_changed,
149- // )
150- // ts.createWatchProgram(host)
151- // }
152- // /* Emit once and exit */
153- // else {
154- // let begin = performance.now()
155- // ts.createProgram(data.ts_entries, options).emit()
156- // // eslint-disable-next-line no-console
157- // console.log(`DTS complete in ${(performance.now()-begin).toFixed(2)}ms`)
158- // process.exit(0)
159- // }
160- // }
161-
162- // const format_host: ts.FormatDiagnosticsHost = {
163- // getCurrentDirectory: () => process.cwd(),
164- // getCanonicalFileName: filename => filename,
165- // getNewLine: () => ts.sys.newLine
166- // }
167-
168- // function report_diagnostic(diagnostic: ts.Diagnostic) {
169- // // eslint-disable-next-line no-console
170- // console.error(ts.formatDiagnosticsWithColorAndContext([diagnostic], format_host))
171- // }
172- // function report_diagnostics(diagnostics: ts.Diagnostic[]) {
173- // // eslint-disable-next-line no-console
174- // console.error(ts.formatDiagnosticsWithColorAndContext(diagnostics, format_host))
175- // }
176- // function report_watch_status_changed(diagnostic: ts.Diagnostic) {
177- // // eslint-disable-next-line no-console
178- // console.info(ts.formatDiagnosticsWithColorAndContext([diagnostic], format_host))
179- // }
180-
181- // export function get_tsc_options(base_path: string): ts.CompilerOptions {
182-
183- // let ts_config_file = ts.findConfigFile(base_path, ts.sys.fileExists)
184- // if (!ts_config_file) throw Error('tsconfig.json not found')
185-
186- // let {config, error} = ts.readConfigFile(ts_config_file, ts.sys.readFile)
187- // if (error) {
188- // report_diagnostic(error)
189- // }
58+ type Build_Task = {
59+ promise : Promise < void >
60+ resolve : ( ) => void
61+ done : boolean
62+ }
63+ function make_build_task ( ) : Build_Task {
64+ let task : Build_Task = { } as any
65+ task . promise = new Promise ( resolve => { task . resolve = ( ) => { task . done = true ; resolve ( ) } } )
66+ return task
67+ }
68+
69+ type Build_Config = {
70+ pkg_json : Package_Json
71+ options : esb . BuildOptions
72+ task : Build_Task
73+ }
74+ let configs : Build_Config [ ] = [ ]
75+
76+ for ( let name of packages_names ) {
77+
78+ let pkg_path = path . join ( packages_dirname , name )
79+
80+ let pkg_json_path = path . join ( pkg_path , 'package.json' )
81+ let pkg_json = JSON . parse ( fs . readFileSync ( pkg_json_path ) as any ) as Package_Json
19082
191- // let {options, errors} = ts.parseJsonConfigFileContent(config, ts.sys, base_path)
192- // if (errors.length > 0) {
193- // report_diagnostics(errors)
194- // }
195-
196- // return {
197- // ...options,
198- // emitDeclarationOnly: true,
199- // noEmit: false,
200- // noEmitOnError: CI,
201- // declaration: true,
202- // declarationMap: true,
203- // }
204- // }
205-
206-
207- // main()
83+ let build_path = path . join ( pkg_path , 'build.ts' )
84+ let options_list = ( await import ( build_path ) ) . default ( ) as esb . BuildOptions [ ]
85+
86+ for ( let options of options_list ) {
87+ configs . push ( { pkg_json, options, task : make_build_task ( ) } )
88+ }
89+ }
90+
91+ for ( let config of configs ) {
92+
93+ let deps_pkg = config . pkg_json . dependencies || { }
94+ let deps_external = config . options . external || [ ]
95+ let deps_configs = new Map < string , Build_Config > ( )
96+
97+ /*
98+ Find which internal dependencies build needs to wait on
99+ Only necessary when the dependency is not parked as "external"
100+ and bundled in with the package
101+ */
102+ for ( let dep_name of Object . keys ( deps_pkg ) ) {
103+ if ( deps_external . includes ( dep_name ) ) continue
104+
105+ let dep_config = configs . find ( c => c . pkg_json . name === dep_name )
106+ if ( dep_config ) deps_configs . set ( dep_name , dep_config )
107+ }
108+
109+ let deps_plugin : esb . Plugin = {
110+ name : 'wait-for-deps' ,
111+ setup ( build ) {
112+
113+ let begin = performance . now ( )
114+
115+ build . onStart ( ( ) => {
116+ if ( config . task . done ) {
117+ config . task = make_build_task ( )
118+ }
119+ begin = performance . now ( )
120+ } )
121+
122+ build . onEnd ( ( ) => {
123+ // eslint-disable-next-line no-console
124+ console . log ( `\x1b[36m${ config . pkg_json . name } \x1b[0m built in \x1b[33m${ ( performance . now ( ) - begin ) . toFixed ( ) } ms\x1b[0m` )
125+ config . task . resolve ( )
126+ } )
127+
128+ /* Wait for each dependency to be done when requested */
129+ for ( let dep of deps_configs . values ( ) ) {
130+ build . onResolve ( { filter : new RegExp ( '^' + dep . pkg_json . name ) } , async args => {
131+ if ( ! dep . task . done ) {
132+ // eslint-disable-next-line no-console
133+ console . log ( `\x1b[36m${ config . pkg_json . name } \x1b[0m waits on \x1b[36m${ args . path } \x1b[0m` )
134+ await dep . task . promise
135+ }
136+ return null
137+ } )
138+ }
139+ } ,
140+ }
141+
142+ config . options . plugins = [
143+ ...( config . options . plugins || [ ] ) ,
144+ deps_plugin ,
145+ ]
146+ }
147+
148+ /* Watch - never terminates */
149+ if ( is_dev ) {
150+ for ( let c of configs ) {
151+ esb . context ( c . options )
152+ . then ( ctx => ctx . watch ( ) )
153+ }
154+ }
155+ /* Build once - wait for all to finish */
156+ else {
157+ await Promise . all ( configs . map ( c => esb . build ( c . options ) ) )
158+ }
0 commit comments