- 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
205 lines
6.4 KiB
JavaScript
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)
|
|
} |