#!/usr/bin/env tsx import { promises as fs } from 'node:fs'; import { isBuiltin } from 'node:module'; import { relative, resolve } from 'node:path'; import esbuild from 'esbuild'; import type { Plugin } from 'esbuild'; import { memoize } from 'lodash-es'; import c from 'picocolors'; import { minify_sync } from 'terser'; import { dependencies } from '../dist/package.json'; import { dts } from './dts'; import { babelPlugin } from './modifier'; const ENV = (process.env.NODE_ENV ??= 'production'); const PROD = ENV === 'production'; const { gray, green } = c; declare global { interface Array { filter( predicate: BooleanConstructor, ): Exclude[]; } } const log = memoize(console.log); const plugins: Plugin[] = [ babelPlugin, { name: 'alias', setup(build) { build.onResolve({ filter: /^jsx-ast-utils$/ }, () => ({ path: resolve('./packages/jsx-ast-utils/src/index.js'), })); build.onResolve({ filter: /^jsx-ast-utils\/.+$/ }, ({ path }) => ({ path: resolve('./packages/jsx-ast-utils/', path.slice('jsx-ast-utils/'.length)) + '.js', })); }, }, ]; if (process.env.DEBUG) { plugins.push({ name: 'deps-check', setup(build) { const declared = new Set(Object.keys(dependencies)); build.onResolve({ filter: /^.*$/ }, ({ path, importer }) => { if ( !path.startsWith('./') && !path.startsWith('../') && !isBuiltin(path) && path !== 'eslint' && !path.startsWith('eslint/') && !path.startsWith('eslint-module-utils/') && !declared.has(path) ) { log(green(path), gray('from'), './' + relative(process.cwd(), importer)); } return null; }); }, }); } async function bundle( entry: string, outfile: string, options?: esbuild.BuildOptions & { treeShaking?: boolean }, ) { const output = await esbuild.build({ entryPoints: [entry], outfile, bundle: true, minify: PROD, platform: 'node', packages: 'external', sourcemap: 'linked', plugins, define: {}, alias: {}, format: 'esm', banner: { js: '/* eslint-disable */', }, ...options, }); if (options?.treeShaking) { const [text, setText] = await useText(outfile); const minified = minify_sync(text, { module: true, compress: { conditionals: true, dead_code: true, defaults: false, evaluate: true, passes: 3, pure_new: true, side_effects: true, unused: true, }, mangle: false, format: { comments: true, }, }); await setText(minified.code!); } return output; } async function editPackageJson() { const [state, setState] = await useText('./dist/package.json'); const distPackageJson = JSON.parse(state); const overrideList = await fs.readdir('dist/overrides'); const npmOverrides = Object.fromEntries( overrideList.map(name => [name, `file:./overrides/${name}`]), ); Object.assign(distPackageJson, { overrides: npmOverrides, resolutions: Object.fromEntries( overrideList.map(name => [`**/${name}`, `file:./overrides/${name}`]), ), pnpm: { overrides: npmOverrides }, }); await setState(JSON.stringify(distPackageJson, null, 2) + '\n'); } async function useText(path: string) { const state = await fs.readFile(path, 'utf8'); const setState = (text: string) => fs.writeFile(path, text); return [state, setState] as const; } function bundleType(source: string, output: string) { try { return dts({ source, dist: output, project: './tsconfig.build.json', }); } catch { // noop } } async function main() { console.log('Building type definitions…'); try { await fs.rm('dist/config', { recursive: true }); } catch { // noop } bundleType('./src/prettier.ts', './dist/prettier.d.ts'); bundleType('./src/types.ts', './dist/types.d.ts'); const unminify = { minify: false }; console.log('Building packages…'); await Promise.all([ bundle('./src/index.ts', undefined!, { format: 'esm', splitting: true, outdir: './dist/config', ...unminify, }), bundle('./src/types.ts', './dist/types.js', unminify), bundle('./src/prettier.ts', './dist/prettier.js', unminify), bundle('./src/install.ts', './dist/install.js', { treeShaking: true, minify: false, banner: { js: '#!/usr/bin/env node\n/* eslint-disable */', }, }), editPackageJson(), ]); // bundleType('./src/index.ts', './dist/config/index.d.ts'); await fs.copyFile('./src/config.d.ts', './dist/config/index.d.ts'); } void main();