Files
browser-scripts-builder/src/build.ts
2024-06-19 09:47:25 -05:00

154 lines
3.9 KiB
TypeScript

import { BuildFailure, BuildOptions, BuildResult, build } from 'esbuild'
import { UserScriptMetaFull } from './types'
import readMeta from './readmeta'
import { format, resolveConfig } from 'prettier'
import { AllPaths, CLIArgs } from './main'
import { writeFile, stat, unlink } from 'fs/promises'
export interface runBuildResult {
meta: UserScriptMetaFull
error: string | null
}
export default async function runBuild(name: string) {
//read meta file
let { meta, metaString } = readMeta(name)
let paths = AllPaths.script(name)
let result = await runEsbuild({
entryPoints: [paths.main],
outfile: paths.dist,
target: 'esnext',
platform: 'node',
format: 'esm',
bundle: true,
minify: CLIArgs.minify,
define: {
UserScriptName: `'${meta.name}'`,
UserScriptNamespace: `'${meta.namespace}'`,
UserScriptVersion: `'${meta.version}'`,
UserScriptDownloadURL: `'${meta.downloadURL}'`,
UserScriptSupportURL: `'${meta.supportURL}'`,
UserScriptHomepageURL: `'${meta.homepageURL}'`,
},
})
let error = await postBuild(name, result, metaString)
return {
meta,
error,
}
}
interface RunEsbuildResult {
content: string | null
error: string | null
errorRaw?: BuildFailure
}
async function runEsbuild(opts: BuildOptions): Promise<RunEsbuildResult> {
opts.write = false
try {
let res = await build(opts)
return getResult(null, res)
} catch (err) {
return getResult(err as BuildFailure, null)
}
}
function getResult(error: BuildFailure | null, result: BuildResult | null) {
if (error) {
return {
content: null,
error: (error as BuildFailure).message,
errorRaw: error,
}
} else if (result) {
let content = ''
if (result.outputFiles && result.outputFiles.length > 0) {
content = result.outputFiles[0].text
if (!CLIArgs.srccomment) content = clearFilenameComments(content)
}
if (content === '') {
return {
content: null,
error: 'No output',
}
}
return {
content,
error: null,
}
} else {
return {
content: null,
error: 'No result',
}
}
}
function clearFilenameComments(content: string): string {
let regexp = new RegExp(`//\\s*${AllPaths.base.in}/.*(?:\\n|$)`, 'g')
return content.replace(regexp, '')
}
async function postBuild(
name: string,
result: RunEsbuildResult,
metaString: string
) {
let error: string | null = null
let paths = AllPaths.script(name)
let PrettierConfig = (await resolveConfig(paths.dir)) ?? {}
if (result.error) {
console.error(name, result.errorRaw || result.error)
error = result.error
} else if (result.content) {
let content = metaString + result.content
if (CLIArgs.prettier) {
content = await format(content, {
...PrettierConfig,
parser: 'babel',
})
}
await writeFile(paths.dist, content)
} else {
console.error(name, 'No output')
}
await doErrorFile(name, error)
return error
}
async function doErrorFile(name: string, error: string | null) {
let paths = AllPaths.script(name)
let content = `${new Date().toISOString()}\n\n${error}`
if (error !== null) {
await writeFile(paths.error, content)
if (await existsFile(paths.dist)) {
await unlink(paths.dist)
}
} else if (await existsFile(paths.error)) {
await unlink(paths.error)
}
}
function existsFile(path: string): Promise<boolean> {
return new Promise(resolve => {
stat(path)
.then(() => resolve(true))
.catch(() => resolve(false))
})
}