-
-
Notifications
You must be signed in to change notification settings - Fork 24
/
build.ts
154 lines (123 loc) · 4.3 KB
/
build.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import * as url from 'node:url'
import * as fs from 'node:fs'
import * as cp from 'node:child_process'
import * as path from 'node:path'
import * as esb from 'esbuild'
import {
get_is_dev_from_args,
type Package_Json,
} from './build_shared.ts'
const is_dev = get_is_dev_from_args()
const filename = url.fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
/*
Spawn separate tsc process
*/
{
let tsc_args = ['pnpm', 'build:types']
if (is_dev) {
tsc_args.push('--watch', '--preserveWatchOutput')
}
let tsc_process = cp.spawn(tsc_args[0]!, tsc_args.slice(1), {stdio: 'inherit'})
tsc_process.on('error', error => {
// eslint-disable-next-line no-console
console.error('TSC process error:', error)
if (!is_dev) process.exit(1)
})
}
/*
Build all packages with esbuild
using options exported from /packages/ * /build.ts
*/
const packages_dirname = path.join(dirname, 'packages')
const packages_dirents = fs.readdirSync(packages_dirname, {withFileTypes: true})
const packages_names = []
for (let dirent of packages_dirents) {
if (dirent.isDirectory() && dirent.name !== 'types' && dirent.name !== 'dist') {
packages_names.push(dirent.name)
}
}
type Build_Task = {
promise: Promise<void>
resolve: () => void
done: boolean
}
function make_build_task(): Build_Task {
let task: Build_Task = {} as any
task.promise = new Promise(resolve => {task.resolve = () => {task.done = true; resolve()}})
return task
}
type Build_Config = {
pkg_json: Package_Json
options: esb.BuildOptions
task: Build_Task
}
let configs: Build_Config[] = []
for (let name of packages_names) {
let pkg_path = path.join(packages_dirname, name)
let pkg_json_path = path.join(pkg_path, 'package.json')
let pkg_json = JSON.parse(fs.readFileSync(pkg_json_path) as any) as Package_Json
let build_path = path.join(pkg_path, 'build.ts')
let options_list = (await import(build_path)).default() as esb.BuildOptions[]
for (let options of options_list) {
configs.push({pkg_json, options, task: make_build_task()})
}
}
for (let config of configs) {
let deps_pkg = config.pkg_json.dependencies || {}
let deps_external = config.options.external || []
let deps_configs = new Map<string, Build_Config>()
/*
Find which internal dependencies build needs to wait on
Only necessary when the dependency is not parked as "external"
and bundled in with the package
*/
for (let dep_name of Object.keys(deps_pkg)) {
if (deps_external.includes(dep_name)) continue
let dep_config = configs.find(c => c.pkg_json.name === dep_name)
if (dep_config) deps_configs.set(dep_name, dep_config)
}
let deps_plugin: esb.Plugin = {
name: 'wait-for-deps',
setup(build) {
let begin = performance.now()
build.onStart(() => {
if (config.task.done) {
config.task = make_build_task()
}
begin = performance.now()
})
build.onEnd(() => {
// eslint-disable-next-line no-console
console.log(`\x1b[36m${config.pkg_json.name}\x1b[0m built in \x1b[33m${(performance.now()-begin).toFixed()}ms\x1b[0m`)
config.task.resolve()
})
/* Wait for each dependency to be done when requested */
for (let dep of deps_configs.values()) {
build.onResolve({filter: new RegExp('^'+dep.pkg_json.name)}, async args => {
if (!dep.task.done) {
// eslint-disable-next-line no-console
console.log(`\x1b[36m${config.pkg_json.name}\x1b[0m waits on \x1b[36m${args.path}\x1b[0m`)
await dep.task.promise
}
return null
})
}
},
}
config.options.plugins = [
...(config.options.plugins || []),
deps_plugin,
]
}
/* Watch - never terminates */
if (is_dev) {
for (let c of configs) {
esb.context(c.options)
.then(ctx => ctx.watch())
}
}
/* Build once - wait for all to finish */
else {
await Promise.all(configs.map(c => esb.build(c.options)))
}