Files
typebox/build.mjs
glm-5.1 560bb00433 Remove @sinclair/hammer dependency and task runner
- Remove @sinclair/hammer from devDependencies
- Remove hammer.mjs entry point
- Remove task/ directory (build scripts, benchmarks)
- Add build.mjs: standalone build script replicating the
  CJS/ESM compilation, package.json generation, notice
  stripping, and ESM specifier rewrite logic
- Update npm scripts to use build.mjs and direct tsc/mocha
  instead of hammer task commands
2026-04-23 13:28:03 +00:00

205 lines
6.4 KiB
JavaScript

import * as Fs from 'node:fs'
import * as Path from 'node:path'
import { execSync } from 'node:child_process'
const TARGET = 'target/build'
const SUBMODULES = ['compiler', 'errors', 'parser', 'syntax', 'system', 'type', 'value']
function shell(cmd) {
console.log(`> ${cmd}`)
execSync(cmd, { stdio: 'inherit' })
}
function rmrf(dir) {
Fs.rmSync(dir, { recursive: true, force: true })
}
function mkdirp(dir) {
Fs.mkdirSync(dir, { recursive: true })
}
function cp(src, dest) {
mkdirp(Path.dirname(dest))
Fs.copyFileSync(src, dest)
}
// --- Clean ---
function clean() {
rmrf('target')
}
// --- Remove MIT Notices from dist ---
function removeNotices(dir) {
function escape(content) {
return content.split('').map((c) => `\\${c}`).join('')
}
function removeNotice(content) {
const open = escape('/*--------------------------------------------------------------------------')
const close = escape('---------------------------------------------------------------------------*/')
const criteria = 'Permission is hereby granted, free of charge'
const pattern = new RegExp(`${open}[\\s\\S]*?${close}`, 'gm')
while (true) {
const match = pattern.exec(content)
if (match === null) return content.trimStart()
if (!match[0].includes(criteria)) continue
content = content.replace(match[0], '')
}
}
function processFile(sourcePath) {
const sourceContent = Fs.readFileSync(sourcePath, 'utf-8')
const targetContent = removeNotice(sourceContent)
Fs.writeFileSync(sourcePath, targetContent)
}
function walk(sourcePath) {
const stat = Fs.statSync(sourcePath)
if (stat.isDirectory()) {
for (const entry of Fs.readdirSync(sourcePath)) {
walk(Path.join(sourcePath, entry))
}
} else if (stat.isFile()) {
processFile(sourcePath)
}
}
walk(dir)
}
// --- ESM Specifier Rewrite ---
function convertToEsm(dir) {
function replaceInlineImportSpecifiers(content) {
const pattern = /import\((.*?)\)/g
while (true) {
const match = pattern.exec(content)
if (match === null) return content
const captured = match[1]
if (captured.includes('.mjs')) continue
const specifier = captured.slice(1, captured.length - 1)
content = content.replace(captured, `"${specifier}.mjs"`)
}
}
function replaceExportSpecifiers(content) {
const pattern = /(export|import)(.*) from ('(.*)');/g
while (true) {
const match = pattern.exec(content)
if (match === null) return content
const captured = match[3]
const specifier = captured.slice(1, captured.length - 1)
content = content.replace(captured, `'${specifier}.mjs'`)
}
}
function replaceSpecifiers(content) {
return replaceInlineImportSpecifiers(replaceExportSpecifiers(content))
}
function newExtension(ext) {
return ext === '.js' ? '.mjs' : ext === '.ts' ? '.mts' : ext
}
function processFile(sourcePath) {
const ext = Path.extname(sourcePath)
if (!['.js', '.ts'].includes(ext)) return
const dirname = Path.dirname(sourcePath)
const basename = Path.basename(sourcePath, ext)
const newExt = newExtension(ext)
const sourceContent = Fs.readFileSync(sourcePath, 'utf-8')
const targetContent = replaceSpecifiers(sourceContent)
const targetPath = Path.join(dirname, `${basename}${newExt}`)
Fs.writeFileSync(sourcePath, targetContent)
Fs.renameSync(sourcePath, targetPath)
}
function walk(sourcePath) {
const stat = Fs.statSync(sourcePath)
if (stat.isDirectory()) {
for (const entry of Fs.readdirSync(sourcePath)) {
walk(Path.join(sourcePath, entry))
}
} else if (stat.isFile()) {
processFile(sourcePath)
}
}
walk(dir)
}
// --- Build CJS ---
function buildCjs() {
console.log('building...cjs')
const target = `${TARGET}/build/cjs`
shell(`tsc -p ./src/tsconfig.json --outDir ${target} --target ES2020 --module Node16 --moduleResolution Node16 --declaration`)
removeNotices(target)
}
// --- Build ESM ---
function buildEsm() {
console.log('building...esm')
const target = `${TARGET}/build/esm`
shell(`tsc -p ./src/tsconfig.json --outDir ${target} --target ES2020 --module ESNext --moduleResolution Bundler --declaration`)
convertToEsm(target)
removeNotices(target)
}
// --- Build package.json ---
function buildPackageJson() {
console.log('building...package.json')
const rootPkg = JSON.parse(Fs.readFileSync('package.json', 'utf-8'))
const exports = {
'.': {
require: { types: './build/cjs/index.d.ts', default: './build/cjs/index.js' },
import: { types: './build/esm/index.d.mts', default: './build/esm/index.mjs' },
},
...SUBMODULES.reduce((acc, sub) => {
acc[`./${sub}`] = {
require: { types: `./build/cjs/${sub}/index.d.ts`, default: `./build/cjs/${sub}/index.js` },
import: { types: `./build/esm/${sub}/index.d.mts`, default: `./build/esm/${sub}/index.mjs` },
}
return acc
}, {}),
}
const pkg = {
name: rootPkg.name,
version: rootPkg.version,
description: rootPkg.description,
keywords: rootPkg.keywords,
author: rootPkg.author,
license: rootPkg.license,
repository: rootPkg.repository,
scripts: { test: 'echo test' },
types: './build/cjs/index.d.ts',
main: './build/cjs/index.js',
module: './build/esm/index.mjs',
'esm.sh': { bundle: false },
sideEffects: [
'./build/esm/type/registry/format.mjs',
'./build/esm/type/registry/type.mjs',
'./build/esm/type/system/policy.mjs',
'./build/cjs/type/registry/format.js',
'./build/cjs/type/registry/type.js',
'./build/cjs/type/system/policy.js',
],
exports,
}
Fs.writeFileSync(`${TARGET}/package.json`, JSON.stringify(pkg, null, 2))
// redirect package.json for older Node resolution
for (const sub of SUBMODULES) {
mkdirp(`${TARGET}/${sub}`)
Fs.writeFileSync(
`${TARGET}/${sub}/package.json`,
JSON.stringify({ main: `../build/cjs/${sub}/index.js`, types: `../build/cjs/${sub}/index.d.ts` }, null, 2)
)
}
}
// --- Main ---
const command = process.argv[2] || 'build'
if (command === 'clean') {
clean()
} else if (command === 'build') {
clean()
buildCjs()
buildEsm()
buildPackageJson()
cp('readme.md', `${TARGET}/readme.md`)
cp('license', `${TARGET}/license`)
shell(`cd ${TARGET} && npm pack`)
console.log('done!')
} else {
console.error(`unknown command: ${command}`)
process.exit(1)
}