From f1387416153c9c1b101ca6838aa2aafcc78e88ae Mon Sep 17 00:00:00 2001 From: George Fu Date: Fri, 29 Aug 2025 12:11:35 -0400 Subject: [PATCH] chore(scripts): support multi-commands in cli dispatcher --- scripts/cli-dispatcher/index.js | 90 ++++++++++++-------------------- tests/bundlers/Makefile | 7 ++- tests/bundlers/test.spec.mjs | 60 ++++++++++++++++++--- tests/bundlers/vite.config.ts | 2 + tests/bundlers/webpack.config.js | 8 +++ 5 files changed, 102 insertions(+), 65 deletions(-) diff --git a/scripts/cli-dispatcher/index.js b/scripts/cli-dispatcher/index.js index 801fb7d91dff..6d2aaf97859e 100644 --- a/scripts/cli-dispatcher/index.js +++ b/scripts/cli-dispatcher/index.js @@ -1,6 +1,5 @@ #!/usr/bin/env node -const fs = require("fs"); const path = require("path"); const readline = require("readline"); @@ -37,31 +36,21 @@ async function main() { const [node, dispatcher, ...rest] = argv; const flags = rest.filter((f) => f.startsWith("--")); const options = { - dry: flags.includes("--dry"), help: flags.includes("--help") || rest.length === 0, - confirm: flags.includes("--c"), }; if (options.help) { console.info(` Usage: b [package query words] - [command query words] - b c s3 c - b t + b c s3 c - b t, b cjs matches to: - (cd clients/client-s3-control && yarn build:types) + (cd clients/client-s3-control && yarn build:types && yarn build:cjs) Query words are substrings that match against the package name and npm scripts. The substrings must appear in order for a match. Match priority goes to whole-word matching and initial matching. - - Options: - --dry - dry run with no command execution. - --help - show this message. - --c - ask for confirmation before executing command. `); return 0; } @@ -70,6 +59,10 @@ async function main() { const separatorIndex = rest.indexOf("-") !== -1 ? rest.indexOf("-") : rest.length; const query = nonFlags.slice(0, separatorIndex); const commands = nonFlags.slice(separatorIndex + 1); + const multiCommands = commands + .join(" ") + .split(/,\s?/) + .map((c) => c.split(" ")); const matchedPackages = findFolders(allPackages, ...query); @@ -85,59 +78,42 @@ async function main() { ); const [target] = matchedPackages; - const targetPkgJson = require(path.join(target.location, "package.json")); - const matchedScripts = findScripts(Object.keys(targetPkgJson.scripts || {}), ...commands); - const [script] = matchedScripts; - - if (commands.length === 0) { - console.info("No commands entered"); - return 0; - } - if (matchedScripts.length === 0) { - console.error("No matching scripts for command query:", commands); - return 0; - } + for (const commands of multiCommands) { + const matchedScripts = findScripts(Object.keys(targetPkgJson.scripts || {}), ...commands); - console.log("commands:", ...commands); - console.log("matched commands:", matchedScripts); + if (commands.length === 0) { + console.info("No commands entered"); + return 0; + } - const command = `yarn ${script} in ${target.location}`; + if (matchedScripts.length === 0) { + console.error("No matching scripts for command query:", commands); + return 0; + } - if (options.dry) { - console.log("DRYRUN:", command); - return 0; + console.log("commands:", ...commands); + console.log("matched commands:", matchedScripts); } - const execute = async () => { - const { spawnProcess } = require("../utils/spawn-process"); - console.info("Running:", "yarn", script); - console.info("Location:", target.location); - await spawnProcess("yarn", [script], { - cwd: target.location, - stdio: "inherit", - }); - return; - }; - - if (options.confirm) { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - rl.question(`run script "${script}" in ${target.location} (y)/n?:`, async (confirm) => { - if (confirm.toLowerCase().trim() === "y" || confirm === "") { - await execute(); - } - rl.close(); - }); - return 0; + for (const commands of multiCommands) { + const matchedScripts = findScripts(Object.keys(targetPkgJson.scripts || {}), ...commands); + const [script] = matchedScripts; + + const execute = async () => { + const { spawnProcess } = require("../utils/spawn-process"); + console.info("Running:", "yarn", script); + console.info("Location:", target.location); + await spawnProcess("yarn", [script], { + cwd: target.location, + stdio: "inherit", + }); + }; + + await execute(); } - await execute(); - return 0; } diff --git a/tests/bundlers/Makefile b/tests/bundlers/Makefile index ca388f62a003..7734bc621a10 100644 --- a/tests/bundlers/Makefile +++ b/tests/bundlers/Makefile @@ -6,10 +6,13 @@ test: # create bundles build: - make vite webpack + make vite webpack esbuild vite: npx vite build webpack: - npx webpack \ No newline at end of file + npx webpack + +esbuild: + npx esbuild ./source.ts --bundle --outfile=./dist/esbuild-dist.js --format=esm --tree-shaking=true \ No newline at end of file diff --git a/tests/bundlers/test.spec.mjs b/tests/bundlers/test.spec.mjs index c6bf61f047f7..185de139bc63 100644 --- a/tests/bundlers/test.spec.mjs +++ b/tests/bundlers/test.spec.mjs @@ -14,17 +14,25 @@ const viteDist = { name: "vite", content: fs.readFileSync(path.join(__dirname, "dist", "vite-dist.js"), "utf-8"), }; +const esbuildDist = { + name: "esbuild", + content: fs.readFileSync(path.join(__dirname, "dist", "esbuild-dist.js"), "utf-8"), +}; -for (const { content: fileContents, name } of [webpackDist, viteDist]) { - console.log(name); +for (const { content: fileContents, name } of [webpackDist, viteDist, esbuildDist]) { + console.log("================", name, "================"); const contentSize = fileContents.replaceAll(/\s+/g, "").length; - const callsToClassBuilder = fileContents.match(/\.classBuilder\(\)/g); - const runtimeConfig = fileContents.match(/runtime: "browser"/); + const callsToClassBuilder = fileContents.match(/\.classBuilder\(\)/g) || []; + const runtimeConfig = fileContents.match(/runtime: "browser"/) || []; + + const serializers = fileContents.match(/(var|const) se_/g) || []; + const operationSchemas = fileContents.match(/ op\(/g) || []; + const structSchemas = fileContents.match(/ struct\(/g) || []; try { assert(contentSize < 1_000_000); - console.info(`✅ content size is under 1M char.`); + console.info(`✅ content size is under 1M char. ${contentSize.toLocaleString()}`); } catch (e) { throw new Error("Content size should be less than 1M characters."); } @@ -33,7 +41,9 @@ for (const { content: fileContents, name } of [webpackDist, viteDist]) { assert(callsToClassBuilder.length <= 2); // only GetObject and CreateSession should be present. console.info(`✅ two commands bundled (tree shaken).`); } catch (e) { - throw new Error("there should only be 2 calls to the Command classBuilder. Tree-shaking failure?"); + throw new Error( + `there should only be 2 calls to the Command classBuilder, got ${callsToClassBuilder.length}. Tree-shaking failure?` + ); } try { @@ -42,4 +52,42 @@ for (const { content: fileContents, name } of [webpackDist, viteDist]) { } catch (e) { throw new Error("the browser runtimeConfig should be present in the bundle."); } + + console.log("serializers", serializers.length); + console.log("operationSchemas", operationSchemas.length); + console.log("structSchemas", structSchemas.length); } + +// Model-ignorant codegen expected output: +// problems: webpack fails to tree shake serde functions. +/* +================ webpack ================ +✅ content size is under 1M char. 628,148 +✅ two commands bundled (tree shaken). +✅ runtimeConfig is browser. +serializers 250 +operationSchemas 11 +================ vite ================ +✅ content size is under 1M char. 341,346 +✅ two commands bundled (tree shaken). +✅ runtimeConfig is browser. +serializers 2 +operationSchemas 10 + */ + +// Schema serde expected output: +// problems: both bundlers fail to tree-shake schemas (fix WIP). +/* +================ webpack ================ +✅ content size is under 1M char. 557,102 +✅ two commands bundled (tree shaken). +✅ runtimeConfig is browser. +serializers 0 +operationSchemas 116 +================ vite ================ +✅ content size is under 1M char. 459,392 +✅ two commands bundled (tree shaken). +✅ runtimeConfig is browser. +serializers 0 +operationSchemas 115 + */ diff --git a/tests/bundlers/vite.config.ts b/tests/bundlers/vite.config.ts index 435bdf93762b..9d329f1a2ada 100644 --- a/tests/bundlers/vite.config.ts +++ b/tests/bundlers/vite.config.ts @@ -17,6 +17,8 @@ export default defineConfig({ // Provide global variables to use in the UMD build // for externalized deps globals: {}, + // to get an easier aggregate accounting of bundle contents + inlineDynamicImports: true, }, }, minify: false, diff --git a/tests/bundlers/webpack.config.js b/tests/bundlers/webpack.config.js index 5f3882f2c3f0..143743fbb89c 100644 --- a/tests/bundlers/webpack.config.js +++ b/tests/bundlers/webpack.config.js @@ -6,6 +6,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)); export default { mode: "production", entry: "./source.ts", + target: "web", output: { path: path.resolve(__dirname, "dist"), filename: "webpack-dist.js", @@ -13,5 +14,12 @@ export default { }, optimization: { minimize: false, + splitChunks: false, + runtimeChunk: false, + sideEffects: true, + usedExports: true, + }, + stats: { + optimizationBailout: false, }, };