const babel = require("@babel/core"); const path = require("path"); const fs = require("fs"); const resolver = require("enhanced-resolve"); const root = path.resolve(__dirname, "../ant-design"); const entryDir = path.resolve(root, "./components"); const entryFile = "index.tsx"; // copy from `/ant-design/node_modules/@ant-design/tools/lib/` const resolve = resolver.create.sync({ extensions: [ ".web.tsx", ".web.ts", ".web.jsx", ".web.js", ".ts", ".tsx", ".js", ".jsx", ".json", ], fileSystem: fs, alias: { "@ant-design/tools": path.resolve(__dirname, "../ant-design"), }, }); /** * * @param {string} p */ function getDirFromAbsolutePath(p) { return path.dirname(p); } /** * * @param {string} p */ function getFileFromAbsolutePath(p) { return p.split("/").pop(); } function isNotJsFile(file) { return !( file.endsWith(".js") || file.endsWith(".ts") || file.endsWith(".jsx") || file.endsWith(".tsx") ); } /** * * @param {string} dir * @param {string} file * @param {Set} set * @param {(dir: string, file: string) => void} callback */ function dfs(dir, file, set, callback) { if (isNotJsFile(file)) { return; } const target = path.resolve(dir, file); if (set.has(target)) { // avoid self-reference, // such as https://github.com/ant-design/ant-design/blob/master/components/version/index.tsx return; } else { set.add(target); } if (set.size % 100 == 0) { console.log( `Already processed ${set.size} files, now is deal with ${target}` ); } const code = fs.readFileSync(target).toString("utf-8"); const nodeHadRemoveTS = babel.transformSync(code, { presets: ["@babel/preset-typescript"], plugins: [ [ "@babel/plugin-proposal-decorators", { version: "2021-12", }, ], ], ast: true, filename: target, configFile: false, sourceMaps: false, code: false, highlightCode: false, comments: false, }); if (!nodeHadRemoveTS.ast) { return; } babel.traverse(nodeHadRemoveTS.ast, { enter(p) { let value = ""; if (p.isImportDeclaration()) { value = p.node.source.value; } else if (p.isExportNamedDeclaration()) { if (!p.node.source) { return; } value = p.node.source.value; } else if (p.isCallExpression() && p.node.callee.name === "require") { if (typeof p.node.arguments?.[0].value === "string") { value = p.node.arguments[0].value; } else { console.log(target); } } else if (p.isCallExpression() && p.node.callee?.type === "Import") { if (typeof p.node.arguments?.[0].value === "string") { value = p.node.arguments[0].value; } else { console.log(target); } } else { return; } if (value === "") { return; } try { const next = resolve(dir, value); callback(dir, value); dfs( getDirFromAbsolutePath(next), getFileFromAbsolutePath(next), set, callback ); } catch (err) { console.log( `[IGNORED FAILED] resolve ${value} in ${dir} failed, and ignored it.` ); } }, }); } /** * * @param {(dir: string, file: string) => void} callback */ function run(callback) { dfs(entryDir, entryFile, new Set(), callback); } // ------------------------ const HEADER = `// DO NOT EDIT THIS FILE. // It is auto-generated by /bench/scripts/generator-rs-benchmark.js `; function generatorRsBenchmark() { let content = HEADER + ` #![feature(test)] extern crate test; #[cfg(test)] mod bench_test { use nodejs_resolver::{Resolver, Options, ResolveResult, Resource, RResult}; use std::path::PathBuf; use test::Bencher; // use std::time::Instant; fn is_ok(result: RResult>) { assert!(result.is_ok()) } #[bench] fn ant_design_bench(b: &mut Bencher) { b.iter(|| { let resolver = Resolver::new(Options { extensions: vec![ ".web.tsx", ".web.ts", ".web.jsx", ".web.js", ".ts", ".tsx", ".js", ".jsx", ".json", ].into_iter().map(String::from).collect(), ..Default::default() }); // let start = Instant::now(); `; run(function (dir, file) { content += ` is_ok(resolver.resolve( &PathBuf::from("${dir}"), "${file}", )); `; }); content += ` // println!("time cost: {:?} ms", start.elapsed().as_millis());// ms }); } }\n`; console.log("length", content.length); const rsFileStoredPath = path.resolve(__dirname, "../../tests/bench.rs"); fs.writeFileSync(rsFileStoredPath, content); } function generatorEnhanceResolveBenchmark() { let content = HEADER + ` console.time('bench'); const path = require('path'); const { ResolverFactory } = require("enhanced-resolve"); const Benchmark = require('benchmark'); const root = path.resolve(__dirname, "./ant-design"); const resolver = ResolverFactory.createResolver({ extensions: [ '.web.tsx', '.web.ts', '.web.jsx', '.web.js', '.ts', '.tsx', '.js', '.jsx', '.json', ], fileSystem: require('fs'), alias: { "@ant-design/tools" : path.resolve(__dirname, '../ant-design'), }, }) async function resolve(dir, file) { return new Promise(res => { resolver.resolve({}, dir, file, {}, (_error, _result) => { res(undefined) }) }) } async function run() { const tasks = []; `; run(function (dir, file) { content += `tasks.push(resolve('${dir}', '${file}'));\n`; }); content += ` await Promise.all(tasks); }; // const suite = new Benchmark.Suite(); // suite // .add("EnhancedResolve", run) // .on('cycle', function(event) { // console.log(String(event.target)); // }) // .run(); run().then(() => { console.timeEnd('bench'); }); `; console.log("length", content.length); const jsFileStoredPath = path.resolve(__dirname, "../enhanceResolve.js"); fs.writeFileSync(jsFileStoredPath, content); } function generatorESBuildResolveBenchMark() { let content = HEADER + ` const path = require('path'); const { build } = require('esbuild'); const Benchmark = require('benchmark'); const suite = new Benchmark.Suite(); async function run() { await build({ stdin: { contents: '', }, write: false, bundle: true, treeShaking: false, ignoreAnnotations: true, platform: 'node', plugins: [{ name: 'resolve', setup(build) { build.onStart(async () => { console.time('bench') const tasks = []; `; run(function (dir, file) { content += `tasks.push(build.resolve('${file}', { resolveDir: '${dir}', kind: 'import-statement' } ));\n`; }); content += ` await Promise.all(tasks); console.timeEnd('bench') }) }, }], }) }; run(); // suite // .add("ESBuildResolve", run) // .on('cycle', function(event) { // console.log(String(event.target)); // }) // .run({ 'async': true }); `; console.log("length", content.length); const jsFileStoredPath = path.resolve(__dirname, "../esbuildResolve.js"); fs.writeFileSync(jsFileStoredPath, content); } function generatorESBuildNativeResolveBenchMark() { let content = HEADER + ` package main import ( "github.com/evanw/esbuild/pkg/api" ) func main() { api.Build(api.BuildOptions{ Stdin: &api.StdinOptions{ Contents: "", }, Write: false, TreeShaking: api.TreeShakingFalse, Bundle: true, IgnoreAnnotations: true, Platform: api.PlatformNode, Plugins: []api.Plugin{{ Name: "resolve", Setup: func(build api.PluginBuild) { build.OnStart(func() (api.OnStartResult, error) { `; run(function (dir, file) { content += `build.Resolve("${file}", api.ResolveOptions{ ResolveDir: "${dir}"})\n`; }); content += ` return api.OnStartResult{}, nil }) }, }, }, }) } `; console.log("length", content.length); const goFileStoredPath = path.resolve( __dirname, "../esbuildResolve_native.go" ); fs.writeFileSync(goFileStoredPath, content); } // ------------------------------------------------- if (process.argv[2] === "rs") { generatorRsBenchmark(); } else if (process.argv[2] === "esbuild") { generatorESBuildResolveBenchMark(); } else if (process.argv[2] === "enhanced") { generatorEnhanceResolveBenchmark(); } else if (process.argv[2] === "esbuild_native") { generatorESBuildNativeResolveBenchMark(); const cp = require("child_process"); cp.spawnSync("go mod init esbuildResolve_native"); cp.spawnSync("go get github.com/evanw/esbuild/pkg/api"); } else { throw Error("Input the correct argument"); }