packages/bundles/scripts/tasks.ts (289 lines of code) (raw):
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs-extra';
import { globbySync } from 'globby';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export const taskExternals = {
// don't bundle caniuse-lite data so users can
// update it manually
'caniuse-lite': 'caniuse-lite',
'/caniuse-lite(/.*)/': 'caniuse-lite$1',
chokidar: 'chokidar',
fibers: 'fibers',
sass: 'sass',
less: 'less',
eslint: 'eslint',
typescript: 'typescript',
postcss: 'postcss',
'@swc/core': '@swc/core',
'jest-worker': 'jest-worker',
esbuild: 'esbuild',
};
const commonDeps = ['terser', 'tapable', 'cssnano', 'terser-webpack-plugin', 'webpack', 'schema-utils',
'lodash', 'postcss-preset-env', 'loader-utils', 'find-up', 'common-path-prefix', 'magic-string'];
const webpackDevServerDeps = ['bonjour-service', 'colorette', 'compression', 'connect-history-api-fallback',
'default-gateway', 'express', 'graceful-fs', 'http-proxy-middleware',
'ipaddr.js', 'open', 'p-retry', 'portfinder', 'rimraf', 'selfsigned', 'serve-index',
'sockjs', 'spdy', 'webpack-dev-middleware', 'ws'];
commonDeps.concat(webpackDevServerDeps).forEach(dep => taskExternals[dep] = `@ice/bundles/compiled/${dep}`);
function replaceDeps(code: string, deps: string[]) {
return deps.reduce((acc, curr) => {
return acc
// cjs
.replace(new RegExp(`require\\(["']${curr}["']\\)`, 'g'), `require("${`@ice/bundles/compiled/${curr}`}")`)
// esm
.replace(new RegExp(`from ["']${curr}["']`, 'g'), `from "${`@ice/bundles/compiled/${curr}`}"`);
}, code);
}
export function filterExternals(externals: Record<string, string>, keys: string[]) {
const filterExternals = {};
Object.keys(externals).forEach((externalKey) => {
if (!keys.includes(externalKey)) {
filterExternals[externalKey] = externals[externalKey];
}
});
return filterExternals;
}
const tasks = [
// simple task
...['cssnano', 'tapable', 'schema-utils', 'lodash',
'less-loader', 'postcss-loader', 'sass-loader', 'css-loader',
'postcss-preset-env', 'postcss-nested', 'postcss-modules', 'postcss-plugin-rpx2vw',
'webpack-bundle-analyzer', 'es-module-lexer', 'terser', 'trusted-cert', 'magic-string',
'eslint-webpack-plugin', 'copy-webpack-plugin', 'cacache', 'ora', 'unplugin',
// Dependencies of react-refresh-webpack-plugin.
'loader-utils', 'source-map', 'find-up', 'common-path-prefix',
// Dependencies of webpack-dev-server.
...webpackDevServerDeps,
].map((pkgName) => ({ pkgName, externals: taskExternals })),
{
pkgName: 'esbuild-register',
file: 'node_modules/esbuild-register/dist/node.js',
externals: taskExternals,
bundleName: 'node.js',
},
{
pkgName: 'unplugin',
declaration: false,
emptyDir: false,
externals: taskExternals,
file: 'node_modules/unplugin/dist/webpack/loaders/transform.js',
bundleName: 'webpack/loaders/transform.js',
},
{
pkgName: 'unplugin',
declaration: false,
emptyDir: false,
externals: taskExternals,
file: 'node_modules/unplugin/dist/webpack/loaders/load.js',
bundleName: 'webpack/loaders/load.js',
},
{
pkgName: 'unplugin',
declaration: false,
emptyDir: false,
externals: taskExternals,
file: 'node_modules/unplugin/dist/rspack/loaders/transform.js',
bundleName: 'rspack/loaders/transform.js',
},
{
pkgName: 'unplugin',
declaration: false,
emptyDir: false,
externals: taskExternals,
file: 'node_modules/unplugin/dist/rspack/loaders/load.js',
bundleName: 'rspack/loaders/load.js',
},
{
// pack main package
pkgName: 'fork-ts-checker-webpack-plugin',
externals: taskExternals,
patch: () => {
// Hack: ncc will prebundle typescript because of require.resolve('typescript'), overwrite to make it externaled.
const targetPath = path.join(__dirname, '../compiled/fork-ts-checker-webpack-plugin/typescript.js');
fs.writeFileSync(targetPath, 'module.exports = require(\'typescript\');');
},
},
{
// pack worker file
pkgName: 'fork-ts-checker-webpack-plugin',
externals: taskExternals,
declaration: false,
emptyDir: false,
file: 'node_modules/fork-ts-checker-webpack-plugin/lib/typescript/worker/get-issues-worker.js',
bundleName: 'typescript/worker/get-issues-worker.js',
},
{
// pack worker file
pkgName: 'fork-ts-checker-webpack-plugin',
externals: taskExternals,
declaration: false,
emptyDir: false,
file: path.join('node_modules', 'fork-ts-checker-webpack-plugin/lib/typescript/worker/get-dependencies-worker.js'),
bundleName: 'typescript/worker/get-dependencies-worker.js',
},
{
pkgName: 'css-minimizer-webpack-plugin',
externals: taskExternals,
matchCopyFiles: (data: { resolvePath: string; resolveId: string }): boolean => {
const { resolvePath, resolveId } = data;
return resolvePath.includes('./utils') && resolveId.includes('css-minimizer-webpack-plugin/dist');
},
},
{
pkgName: 'mini-css-extract-plugin',
skipCompile: true,
patch: () => {
// copy packages
const pkgPath = path.join(__dirname, '../node_modules/mini-css-extract-plugin');
const targetPath = path.join(__dirname, '../compiled/mini-css-extract-plugin');
const entryPath = path.join(targetPath, 'dist/index.js');
fs.copySync(path.join(pkgPath, 'dist'), path.join(targetPath, 'dist'));
fs.copyFileSync(path.join(targetPath, 'index.d.ts'), path.join(targetPath, 'dist/index.d.ts'));
fs.writeFileSync(entryPath, fs.readFileSync(entryPath, 'utf-8').replace('schema-utils', '@ice/bundles/compiled/schema-utils/index.js'));
},
},
{
pkgName: 'terser-webpack-plugin',
externals: taskExternals,
matchCopyFiles: (data: { resolvePath: string; resolveId: string }): boolean => {
const { resolvePath } = data;
return resolvePath.endsWith('./utils') || resolvePath.endsWith('.json');
},
},
{
pkgName: 'webpack-dev-server',
skipCompile: true,
patch: () => {
// Copy webpack-dev-server while all dependencies has been packed.
const pkgPath = path.join(__dirname, '../node_modules/webpack-dev-server');
const filePaths = globbySync(['**/*'], { cwd: pkgPath, ignore: ['node_modules', 'types', 'bin'] });
filePaths.forEach((filePath) => {
fs.ensureDirSync(path.join(__dirname, `../compiled/webpack-dev-server/${path.dirname(filePath)}`));
const sourcePath = path.join(pkgPath, filePath);
const targetPath = path.join(__dirname, `../compiled/webpack-dev-server/${filePath}`);
if (path.extname(filePath) === '.js') {
const fileContent = fs.readFileSync(sourcePath, 'utf8');
fs.writeFileSync(targetPath, replaceDeps(fileContent, webpackDevServerDeps.concat(commonDeps)));
} else {
fs.copyFileSync(sourcePath, targetPath);
}
});
},
},
{
pkgName: '@pmmmwh/react-refresh-webpack-plugin',
skipCompile: true,
patch: () => {
// Copy @pmmmwh/react-refresh-webpack-plugin while all dependencies has been packed.
const pkgPath = path.join(__dirname, '../node_modules/@pmmmwh/react-refresh-webpack-plugin');
const filePaths = globbySync(['**/*'], { cwd: pkgPath, ignore: ['node_modules', 'types'] });
filePaths.forEach((filePath) => {
fs.ensureDirSync(path.join(__dirname, `../compiled/@pmmmwh/react-refresh-webpack-plugin/${path.dirname(filePath)}`));
const sourcePath = path.join(pkgPath, filePath);
const targetPath = path.join(__dirname, `../compiled/@pmmmwh/react-refresh-webpack-plugin/${filePath}`);
if (path.extname(filePath) === '.js') {
const fileContent = fs.readFileSync(sourcePath, 'utf8');
// Add source-map for react-refresh-webpack-plugin, while other dependencies should pack it.
fs.writeFileSync(targetPath, replaceDeps(fileContent, commonDeps.concat('source-map')));
} else {
fs.copyFileSync(sourcePath, targetPath);
}
});
// Overwrite RefreshUtils.js which is customized for ice.js.
fs.copyFileSync(path.join(__dirname, '../override/RefreshUtils.js'), path.join(pkgPath, 'lib/runtime/RefreshUtils.js'));
},
},
{
file: './webpack/bundle',
pkgName: 'webpack',
bundleName: 'bundle.js',
externals: filterExternals(taskExternals, ['webpack']),
minify: false,
matchCopyFiles: (data: { resolvePath: string }): boolean => {
const { resolvePath } = data;
return resolvePath.endsWith('.runtime.js');
},
patch: () => {
// copy packages
const pkgPath = path.join(__dirname, '../node_modules/webpack');
const targetPath = path.join(__dirname, '../compiled/webpack');
fs.copySync(path.join(pkgPath, 'hot'), path.join(targetPath, 'hot'));
fs.copySync(path.join(__dirname, '../webpack/packages'), targetPath);
},
},
{
pkgName: '@rspack/core',
skipCompile: true,
declaration: false,
patch: () => {
const pkgPath = path.join(__dirname, '../node_modules/@rspack/core');
const targetPath = path.join(__dirname, '../compiled/@rspack/core');
// Copy the entire directory.
// filter out js files and replace with compiled files.
const filePaths = globbySync(['**/*'], { cwd: pkgPath, ignore: ['node_modules'] });
const filesAddOverwrite = [
'dist/util/bindingVersionCheck.js',
];
filePaths.forEach((filePath) => {
const sourcePath = path.join(pkgPath, filePath);
const targetFilePath = path.join(targetPath, filePath);
fs.ensureDirSync(path.dirname(targetFilePath));
if (path.extname(filePath) === '.js') {
const matched = filesAddOverwrite.some(filePath => {
const matched = sourcePath.split(path.sep).join('/').includes(filePath);
if (matched) {
fs.copyFileSync(path.join(__dirname, `../override/rspack/${path.basename(filePath)}`), targetFilePath);
}
return matched;
});
if (!matched) {
const fileContent = fs.readFileSync(sourcePath, 'utf8');
fs.writeFileSync(
targetFilePath,
replaceDeps(fileContent, ['tapable', 'schema-utils', 'graceful-fs'])
.replace(new RegExp('require\\(["\']@rspack/binding["\']\\)', 'g'), 'require("@ice/pack-binding")'),
);
}
} else {
fs.copyFileSync(sourcePath, targetFilePath);
}
});
},
},
{
pkgName: '@rspack/dev-server',
skipCompile: true,
patch: () => {
// Copy webpack-dev-server while all dependencies has been packed.
const pkgPath = path.join(__dirname, '../node_modules/@rspack/dev-server');
const filePaths = globbySync(['**/*'], { cwd: pkgPath, ignore: ['node_modules', 'types', 'bin'] });
filePaths.forEach((filePath) => {
fs.ensureDirSync(path.join(__dirname, `../compiled/@rspack/dev-server/${path.dirname(filePath)}`));
const sourcePath = path.join(pkgPath, filePath);
const targetPath = path.join(__dirname, `../compiled/@rspack/dev-server/${filePath}`);
if (path.extname(filePath) === '.js') {
const fileContent = fs.readFileSync(sourcePath, 'utf8');
fs.writeFileSync(targetPath,
replaceDeps(fileContent, webpackDevServerDeps.concat([...commonDeps, '@rspack/core', 'webpack-dev-server']))
.replace(/webpack-dev-server\//g, '@ice/bundles/compiled/webpack-dev-server/')
.replace(/@rspack\/core\//g, '@ice/bundles/compiled/@rspack/core/')
.replace(/@rspack\/dev-server\//g, '@ice/bundles/compiled/@rspack/dev-server/'),
);
} else {
fs.copyFileSync(sourcePath, targetPath);
}
});
},
},
{
pkgName: '@rspack/plugin-react-refresh',
skipCompile: true,
patch: () => {
const pkgPath = path.join(__dirname, '../node_modules/@rspack/plugin-react-refresh');
const filePaths = globbySync(['**/*'], { cwd: pkgPath, ignore: ['node_modules'] });
filePaths.forEach((filePath) => {
fs.ensureDirSync(path.join(__dirname, `../compiled/@rspack/plugin-react-refresh/${path.dirname(filePath)}`));
const sourcePath = path.join(pkgPath, filePath);
const targetPath = path.join(__dirname, `../compiled/@rspack/plugin-react-refresh/${filePath}`);
if (path.extname(filePath) === '.js') {
const fileContent = fs.readFileSync(sourcePath, 'utf8');
fs.writeFileSync(targetPath,
replaceDeps(fileContent, webpackDevServerDeps.concat([
...commonDeps,
'@rspack/core',
])),
);
} else {
fs.copyFileSync(sourcePath, targetPath);
}
});
// Overwrite RefreshUtils.js which is customized for ice.js.
fs.copyFileSync(
path.join(__dirname, '../override/RefreshUtils.js'),
path.join(__dirname, '../compiled/@rspack/plugin-react-refresh/client/refreshUtils.js'),
);
},
},
];
export default tasks;