packages/bundles/scripts/build.ts (162 lines of code) (raw):
import * as path from 'path';
import { createRequire } from 'module';
import { fileURLToPath } from 'url';
import fs from 'fs-extra';
import ncc from '@vercel/ncc';
import chalk from 'chalk';
import * as dts from 'dts-bundle';
import glob from 'glob';
import findUp from 'find-up';
import tasks, { taskExternals } from './tasks';
const cwd = process.cwd();
// @NOTE: Make ncc work, due to loader recognize cwd as resolving working directory.
process.cwd = () => path.join(cwd, '..');
const require = createRequire(import.meta.url);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
interface Options {
pkgName?: string;
rootDir?: string;
target?: string;
file?: string;
externals?: Record<string, string>;
minify?: boolean;
declaration?: boolean;
bundleName?: string;
emptyDir?: boolean;
skipCompile?: boolean;
matchCopyFiles?: (data: {
resolvePath: string;
resolveId: string;
}) => boolean;
patch?: () => void;
}
export async function packDependency(options: Options): Promise<void> {
const { pkgName, file, rootDir, target, externals, matchCopyFiles, patch, emptyDir = true,
bundleName = 'index.js', minify = true, declaration = true, skipCompile } = options;
console.log(chalk.green(`start pack ${pkgName || file}`));
const targetPath = path.join(rootDir, target);
if (emptyDir) {
fs.removeSync(targetPath);
}
const filesToCopy = [];
const packEntry = file ? path.join(rootDir, file) : require.resolve(pkgName, {
paths: [rootDir],
});
fs.ensureDirSync(targetPath);
if (!skipCompile) {
const { code, assets } = await ncc(packEntry, {
cache: false,
externals,
minify,
target: 'es5',
assetsBuilds: false,
customEmit(filePath: string, opts: {id: string}) {
const { id } = opts;
if (matchCopyFiles && matchCopyFiles({
resolvePath: filePath,
resolveId: id,
})) {
filesToCopy.push(require.resolve(filePath, {
paths: [path.dirname(id)],
}));
return `'./${path.basename(filePath)}'`;
}
},
});
for (const assetKey of Object.keys(assets)) {
const asset = assets[assetKey];
const data = asset.source;
const fileTarget = path.join(targetPath, assetKey);
fs.ensureDirSync(path.dirname(fileTarget));
fs.writeFileSync(fileTarget, data);
}
// copy files
for (const fileToCopy of filesToCopy) {
let content = fs.readFileSync(fileToCopy, 'utf-8');
for (const key of Object.keys(externals)) {
content = content.replace(
new RegExp(`require\\(['"]${key}['"]\\)`, 'gm'),
`require('${externals[key]}')`,
);
content = content.replace(
new RegExp(`require\\(['"]${key}/package(.json)?['"]\\)`, 'gm'),
`require('${externals[key]}/package.json')`,
);
}
const copyTarget = path.join(targetPath, path.basename(fileToCopy));
console.log(chalk.green(`fileToCopy ${copyTarget}`));
fs.writeFileSync(
copyTarget,
content,
'utf-8',
);
}
// write code to package
const outfile = path.join(targetPath, bundleName);
fs.ensureDirSync(path.dirname(outfile));
fs.writeFileSync(path.join(targetPath, bundleName), code, 'utf-8');
}
if (pkgName) {
const packageRoot = path.dirname(
findUp.sync('package.json', {
cwd: require.resolve(pkgName, { paths: [rootDir] }),
}));
// create license file
const licensePath = path.join(packageRoot, 'LICENSE');
if (fs.existsSync(licensePath)) {
console.log(chalk.green(`create LICENSE for ${pkgName || file}`));
fs.copyFileSync(licensePath, path.join(targetPath, 'LICENSE'));
}
// create package.json
const pkgJson = fs.readJSONSync(path.join(packageRoot, 'package.json'));
let dtsName = '';
if (declaration) {
if (pkgJson.types) {
dtsName = 'index.d.ts';
console.log(chalk.green(`bundle dts file for ${pkgName || file}`));
dts.bundle({
name: pkgJson.name,
outputAsModuleFolder: true,
out: path.join(targetPath, dtsName),
main: path.join(packageRoot, pkgJson.types),
headerPath: '',
headerText: '',
});
} else {
try {
const typesRoot = path.dirname(require.resolve(`@types/${pkgName}/package.json`, {
paths: [rootDir],
}));
const typeJson = fs.readJSONSync(path.join(typesRoot, 'package.json'));
dtsName = typeJson.types.endsWith('.d.ts') ? typeJson.types : `${typeJson.types}.d.ts`;
// copy dts to package root
const files = glob.sync('**/*.d.ts', { cwd: typesRoot, nodir: true });
console.log(chalk.green(`copy dts file for ${pkgName || file}`));
for (const file of files) {
const from = path.join(typesRoot, file);
const to = path.join(targetPath, file);
await fs.mkdirp(path.dirname(to));
await fs.copyFile(from, to);
}
} catch (err) {
console.log(chalk.yellow(`can not find dts file of ${pkgName}`));
}
}
}
console.log(chalk.green(`create package.json for ${pkgName || file}`));
fs.writeJSONSync(path.join(targetPath, 'package.json'), {
name: pkgJson.name,
main: 'index.js',
...(dtsName ? { types: dtsName } : {}),
...(pkgJson.author ? { author: pkgJson.author } : {}),
...(pkgJson.license ? { license: pkgJson.license } : {}),
});
}
if (patch) {
patch();
}
}
(async () => {
for (let task of tasks) {
await packDependency({
rootDir: path.join(__dirname, '..'),
externals: taskExternals,
target: `compiled/${task.pkgName}`,
...task,
});
}
})();