Skip to content

Commit f18ce57

Browse files
committed
Refactor building packages to not use turbo
1 parent df9fe29 commit f18ce57

31 files changed

+323
-303
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ node_modules
22
dist
33
gitignore
44

5+
# tsc build artifacts
6+
tsconfig.tsbuildinfo
7+
58
# debug
69
npm-debug.log*
710
yarn-debug.log*

build.ts

Lines changed: 134 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -1,207 +1,158 @@
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

Comments
 (0)