From 6f398978852c05deb72a3bd1a21ff35abc490588 Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Wed, 24 Apr 2024 17:50:49 -0700 Subject: [PATCH 1/6] delete unused file --- .../scripts/retain-dynamic-import-plugin.js | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 packages/lage/scripts/retain-dynamic-import-plugin.js diff --git a/packages/lage/scripts/retain-dynamic-import-plugin.js b/packages/lage/scripts/retain-dynamic-import-plugin.js deleted file mode 100644 index 13dd0ef78..000000000 --- a/packages/lage/scripts/retain-dynamic-import-plugin.js +++ /dev/null @@ -1,19 +0,0 @@ -export function retainDynamicImport() { - return { - name: "retain-dynamic-import", - resolveDynamicImport(specifier) { - if (typeof specifier === "string") { - return null; - } - return false; - }, - renderDynamicImport(entry) { - if (!entry.targetModuleId) { - return { - left: "import(", - right: ")", - }; - } - }, - }; -} From 7e959d1d309e5c3011472627280f8d8c09814469 Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Wed, 24 Apr 2024 17:55:40 -0700 Subject: [PATCH 2/6] use execa and fix bundling --- packages/cli/src/cli.ts | 1 + packages/lage/package.json | 12 +- packages/lage/scripts/bundle.mjs | 62 ++++++--- packages/lage/scripts/prebuild.js | 26 ---- packages/scheduler/package.json | 3 +- .../scheduler/src/runners/NpmScriptRunner.ts | 8 +- yarn.lock | 122 +++++++++++++++++- 7 files changed, 175 insertions(+), 59 deletions(-) delete mode 100644 packages/lage/scripts/prebuild.js diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index c23b25112..d7dfe0390 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -1,3 +1,4 @@ +#!/usr/bin/env node import { Command } from "commander"; import { runCommand } from "./commands/run/index.js"; diff --git a/packages/lage/package.json b/packages/lage/package.json index 66aba6c7b..f3d7f9aaf 100644 --- a/packages/lage/package.json +++ b/packages/lage/package.json @@ -12,8 +12,7 @@ "lage": "dist/lage.js" }, "scripts": { - "prebundle": "node scripts/prebuild.js", - "bundle": "yarn dts-bundle && node scripts/bundle.mjs", + "bundle": "rimraf dist && yarn dts-bundle && node scripts/bundle.mjs", "dts-bundle": "dts-bundle-generator --config ./dts-bundle.config.js && node ./scripts/update-dts-bundle.js" }, "dependencies": { @@ -28,14 +27,11 @@ "backfill-config": "6.4.1", "dts-bundle-generator": "^7.2.0", "workspace-tools": "0.36.4", - "esbuild": "^0.17.18" + "esbuild": "^0.17.18", + "rimraf": "^5.0.5" }, "files": [ - "dist/*.d.ts", - "dist/lage.js", - "dist/lage.js.map", - "dist/runners/**", - "dist/workers/**" + "dist" ], "publishConfig": { "access": "public" diff --git a/packages/lage/scripts/bundle.mjs b/packages/lage/scripts/bundle.mjs index e8f2be234..c80ba00ce 100644 --- a/packages/lage/scripts/bundle.mjs +++ b/packages/lage/scripts/bundle.mjs @@ -1,28 +1,56 @@ +// @ts-check import * as esbuild from "esbuild"; +import fs from "fs"; +import { createRequire } from "module"; +import path from "path"; + +const localRequire = createRequire(import.meta.url); + +async function bundle() { + console.log("\nBundling lage with esbuild..."); + + // Mapping from output path (relative to dist, no extension) to input path (relative to package root) + const entryPoints = { + lage: "@lage-run/cli/lib/cli.js", + main: "./index.js", + "workers/targetWorker": "@lage-run/scheduler/lib/workers/targetWorker.js", + }; + // List of external modules that should not be bundled + const external = ["fsevents", "glob-hasher", "./workers/targetWorker"]; + + // Due to the fact that workers require the runner to be in the same directory, + // add the runners to the entry points, as well as the externals (so the file is preserved). + const pkgToRunnerDir = { + "@lage-run/scheduler": "lib/runners", + "@lage-run/cli": "lib/commands/cache/runners", + }; + for (const [pkg, runnerDir] of Object.entries(pkgToRunnerDir)) { + const pkgPath = path.dirname(localRequire.resolve(`${pkg}/package.json`)); + for (const runner of fs.readdirSync(path.join(pkgPath, runnerDir))) { + // By convention, only include things that end with "Runner.js" + if (runner.endsWith("Runner.js")) { + const runnerInput = `${pkgPath}/${runnerDir}/${runner}`; + const runnerOutput = `runners/${runner}`; + entryPoints[runnerOutput.replace(/\.js$/, "")] = runnerInput; + external.push(`./${runnerOutput}`); + } + } + } -async function bundle(entry, outfile, addBanner = false) { await esbuild.build({ - entryPoints: [entry], + entryPoints, bundle: true, platform: "node", target: ["node16"], - outfile, + outdir: "dist", sourcemap: true, - external: [ - "fsevents", - "glob-hasher", - "./runners/NpmScriptRunner.js", - "./workers/targetWorker", - "./runners/NoOpRunner.js", - "./runners/WorkerRunner.js", - ], - ...(addBanner && { banner: { js: "#!/usr/bin/env node" } }), + logLevel: "info", + external, minify: true, }); } -await Promise.all([ - bundle("@lage-run/cli/lib/cli.js", "dist/lage.js", true), - bundle("./index.js", "dist/main.js"), - bundle("@lage-run/scheduler/lib/workers/targetWorker.js", "dist/workers/targetWorker.js"), -]); +await bundle().catch((err) => { + console.error(err.stack || err); + process.exit(1); +}); diff --git a/packages/lage/scripts/prebuild.js b/packages/lage/scripts/prebuild.js deleted file mode 100644 index 84d4eda5f..000000000 --- a/packages/lage/scripts/prebuild.js +++ /dev/null @@ -1,26 +0,0 @@ -const fs = require("fs"); -const path = require("path"); - -const cwd = process.cwd(); -const schedulerPath = path.dirname(require.resolve("@lage-run/scheduler/package.json")); -const cliPath = path.dirname(require.resolve("@lage-run/cli/package.json")); - -const runnerDirs = [path.join(schedulerPath, "lib", "runners"), path.join(cliPath, "lib", "commands", "cache", "runners")]; - -function prebuild() { - // Due to the fact that workers require the runner to be in the same directory, we need to copy the runners to the dist folder - - for (const runnerDir of runnerDirs) { - for (const runner of fs.readdirSync(runnerDir)) { - // By convention, only copy things that end with "Runner.js" - if (runner.endsWith("Runner.js")) { - const src = path.join(runnerDir, runner); - const dest = path.join(cwd, "dist", "runners", runner); - fs.mkdirSync(path.dirname(dest), { recursive: true }); - fs.copyFileSync(src, dest); - } - } - } -} - -prebuild(); diff --git a/packages/scheduler/package.json b/packages/scheduler/package.json index 34cb0faab..c83d93144 100644 --- a/packages/scheduler/package.json +++ b/packages/scheduler/package.json @@ -22,7 +22,8 @@ "@lage-run/cache": "^1.1.5", "@lage-run/config": "^0.3.5", "@lage-run/hasher": "^1.1.0", - "@lage-run/worker-threads-pool": "^0.8.0" + "@lage-run/worker-threads-pool": "^0.8.0", + "execa": "5.1.1" }, "devDependencies": { "@lage-run/scheduler-types": "^0.3.13", diff --git a/packages/scheduler/src/runners/NpmScriptRunner.ts b/packages/scheduler/src/runners/NpmScriptRunner.ts index 59eda6c63..a99a010bf 100644 --- a/packages/scheduler/src/runners/NpmScriptRunner.ts +++ b/packages/scheduler/src/runners/NpmScriptRunner.ts @@ -1,7 +1,6 @@ -import { existsSync } from "fs"; import { join } from "path"; import { readFile } from "fs/promises"; -import { spawn, type ChildProcess } from "child_process"; +import execa from "execa"; import type { TargetRunner, TargetRunnerOptions } from "@lage-run/scheduler-types"; import type { Target } from "@lage-run/target-graph"; @@ -56,7 +55,7 @@ export class NpmScriptRunner implements TargetRunner { const { nodeOptions, npmCmd, taskArgs } = this.options; const task = target.options?.script ?? target.task; - let childProcess: ChildProcess | undefined; + let childProcess: execa.ExecaChildProcess | undefined; /** * Handling abort signal from the abort controller. Gracefully kills the process, @@ -100,11 +99,12 @@ export class NpmScriptRunner implements TargetRunner { const npmRunNodeOptions = [nodeOptions, target.options?.nodeOptions].filter((str) => str).join(" "); await new Promise((resolve, reject) => { - childProcess = spawn(npmCmd, npmRunArgs, { + childProcess = execa(npmCmd, npmRunArgs, { cwd: target.cwd, stdio: ["inherit", "pipe", "pipe"], // This is required for Windows due to https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2 shell: true, + reject: false, // don't reject the promise on exit non-zero (we handle that differently) env: { ...(process.stdout.isTTY && { FORCE_COLOR: "1" }), // allow user env to override this ...process.env, diff --git a/yarn.lock b/yarn.lock index ad8ef922d..dedc3d3ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -621,6 +621,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -915,6 +927,11 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.0.tgz#2c91791a9ba6ca0a0f4aaac5e45d58df13639ac8" integrity sha512-IgMK9i3sFGNUqPMbjABm0G26g0QCKCUBfglhQ7rQq6WcxbKfEHRcmwsoER4hZcuYqJgkYn2OeuoJIv7Jsftp7g== +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -1391,7 +1408,7 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.0.0: +ansi-styles@^6.0.0, ansi-styles@^6.1.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== @@ -1905,7 +1922,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -2408,6 +2425,14 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + form-data@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" @@ -2601,6 +2626,17 @@ glob@8.1.0: minimatch "^5.0.1" once "^1.3.0" +glob@^10.3.7: + version "10.3.12" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b" + integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.6" + minimatch "^9.0.1" + minipass "^7.0.4" + path-scurry "^1.10.2" + glob@^7.0.3, glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -2913,6 +2949,15 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jackspeak@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jest-changed-files@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" @@ -3468,6 +3513,11 @@ loose-envify@^1.0.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -3576,11 +3626,23 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.1: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -3835,6 +3897,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7" + integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -4090,6 +4160,13 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.5.tgz#9be65d2d6e683447d2e9013da2bf451139a61ccf" + integrity sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A== + dependencies: + glob "^10.3.7" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -4182,6 +4259,11 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -4271,6 +4353,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -4280,7 +4371,7 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^5.0.0: +string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== @@ -4301,6 +4392,13 @@ string_decoder@~0.10.x: resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -4691,6 +4789,15 @@ workspace-tools@^0.35.0: js-yaml "^4.1.0" micromatch "^4.0.0" +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -4709,6 +4816,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" From f22b49dc82466f9f924214d7da727a9d59306dba Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Wed, 24 Apr 2024 18:11:32 -0700 Subject: [PATCH 3/6] use execa promises and add a debug bundle --- ...-8ab7af26-370a-4496-87d7-b41eb0117e71.json | 25 +++++++ packages/lage/package.json | 3 +- packages/lage/scripts/bundle.mjs | 11 +++ .../scheduler/src/runners/NpmScriptRunner.ts | 68 ++++++------------- 4 files changed, 59 insertions(+), 48 deletions(-) create mode 100644 change/change-8ab7af26-370a-4496-87d7-b41eb0117e71.json diff --git a/change/change-8ab7af26-370a-4496-87d7-b41eb0117e71.json b/change/change-8ab7af26-370a-4496-87d7-b41eb0117e71.json new file mode 100644 index 000000000..c23f5face --- /dev/null +++ b/change/change-8ab7af26-370a-4496-87d7-b41eb0117e71.json @@ -0,0 +1,25 @@ +{ + "changes": [ + { + "type": "patch", + "comment": "Add shebang to CLI entry point", + "packageName": "@lage-run/cli", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" + }, + { + "type": "minor", + "comment": "Use execa to run scripts, and add a lage-debug command which runs with a non-minified build", + "packageName": "lage", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" + }, + { + "type": "minor", + "comment": "Use execa to run scripts", + "packageName": "@lage-run/scheduler", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" + } + ] +} \ No newline at end of file diff --git a/packages/lage/package.json b/packages/lage/package.json index f3d7f9aaf..bdaf687ef 100644 --- a/packages/lage/package.json +++ b/packages/lage/package.json @@ -9,7 +9,8 @@ "main": "dist/main.js", "types": "dist/index.d.ts", "bin": { - "lage": "dist/lage.js" + "lage": "dist/lage.js", + "lage-debug": "dist/debug/lage.js" }, "scripts": { "bundle": "rimraf dist && yarn dts-bundle && node scripts/bundle.mjs", diff --git a/packages/lage/scripts/bundle.mjs b/packages/lage/scripts/bundle.mjs index c80ba00ce..93d907bab 100644 --- a/packages/lage/scripts/bundle.mjs +++ b/packages/lage/scripts/bundle.mjs @@ -48,6 +48,17 @@ async function bundle() { external, minify: true, }); + + await esbuild.build({ + entryPoints, + bundle: true, + platform: "node", + target: ["node16"], + outdir: "dist/debug", + logLevel: "info", + external, + minify: false, + }); } await bundle().catch((err) => { diff --git a/packages/scheduler/src/runners/NpmScriptRunner.ts b/packages/scheduler/src/runners/NpmScriptRunner.ts index a99a010bf..5c9db6ae1 100644 --- a/packages/scheduler/src/runners/NpmScriptRunner.ts +++ b/packages/scheduler/src/runners/NpmScriptRunner.ts @@ -57,10 +57,7 @@ export class NpmScriptRunner implements TargetRunner { let childProcess: execa.ExecaChildProcess | undefined; - /** - * Handling abort signal from the abort controller. Gracefully kills the process, - * will be handled by exit handler separately to resolve the promise. - */ + // Handling abort signal from the abort controller. This gracefully kills the process. if (abortSignal) { if (abortSignal.aborted) { return; @@ -69,9 +66,7 @@ export class NpmScriptRunner implements TargetRunner { const abortSignalHandler = () => { abortSignal.removeEventListener("abort", abortSignalHandler); if (childProcess && !childProcess.killed) { - const pid = childProcess.pid; - - process.stdout.write(`Abort signal detected, attempting to killing process id ${pid}\n`); + process.stdout.write(`Abort signal detected, attempting to killing process id ${childProcess.pid}\n`); childProcess.kill("SIGTERM"); @@ -83,28 +78,23 @@ export class NpmScriptRunner implements TargetRunner { }, NpmScriptRunner.gracefulKillTimeout); // Remember that even this timeout needs to be unref'ed, otherwise the process will hang due to this timeout - if (t.unref) { - t.unref(); - } + t.unref?.(); } }; abortSignal.addEventListener("abort", abortSignalHandler); } - /** - * Actually spawn the npm client to run the task - */ + // Actually spawn the npm client to run the task const npmRunArgs = this.getNpmArgs(task, taskArgs); const npmRunNodeOptions = [nodeOptions, target.options?.nodeOptions].filter((str) => str).join(" "); - await new Promise((resolve, reject) => { + try { childProcess = execa(npmCmd, npmRunArgs, { cwd: target.cwd, stdio: ["inherit", "pipe", "pipe"], // This is required for Windows due to https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2 shell: true, - reject: false, // don't reject the promise on exit non-zero (we handle that differently) env: { ...(process.stdout.isTTY && { FORCE_COLOR: "1" }), // allow user env to override this ...process.env, @@ -115,41 +105,25 @@ export class NpmScriptRunner implements TargetRunner { }, }); - let exitHandled = false; - - const handleChildProcessExit = (code: number) => { - childProcess?.off("exit", handleChildProcessExit); - childProcess?.off("error", handleChildProcessExit); - - if (exitHandled) { - return; - } + process.stdout.write(`Running ${[npmCmd, ...npmRunArgs].join(" ")}, pid: ${childProcess.pid}\n`); - exitHandled = true; + childProcess.stdout?.pipe(process.stdout); + childProcess.stderr?.pipe(process.stderr); + await childProcess; + } catch (err) { + const execaError = err as execa.ExecaError; + throw new Error( + `NPM Script Runner: ${npmCmd} ${npmRunArgs.join(" ")} exited with ${execaError.exitCode ? `code ${execaError.exitCode}` : "error"}` + ); + } finally { + try { childProcess?.stdout?.destroy(); childProcess?.stderr?.destroy(); - childProcess?.stdin?.destroy(); - - if (code === 0) { - return resolve(); - } - - reject(new Error(`NPM Script Runner: ${npmCmd} ${npmRunArgs.join(" ")} exited with code ${code}`)); - }; - - const { pid } = childProcess; - - process.stdout.write(`Running ${[npmCmd, ...npmRunArgs].join(" ")}, pid: ${pid}\n`); - - const stdout = childProcess.stdout!; - const stderr = childProcess.stderr!; - - stdout.pipe(process.stdout); - stderr.pipe(process.stderr); - - childProcess.on("exit", handleChildProcessExit); - childProcess.on("error", () => handleChildProcessExit(1)); - }); + childProcess = undefined; + } catch { + // ignore + } + } } } From 540dfc56c299441ea449427f210f7116ae03b806 Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Wed, 24 Apr 2024 18:25:44 -0700 Subject: [PATCH 4/6] bundle --- packages/lage/scripts/bundle.mjs | 35 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/lage/scripts/bundle.mjs b/packages/lage/scripts/bundle.mjs index 93d907bab..4714772bf 100644 --- a/packages/lage/scripts/bundle.mjs +++ b/packages/lage/scripts/bundle.mjs @@ -3,12 +3,16 @@ import * as esbuild from "esbuild"; import fs from "fs"; import { createRequire } from "module"; import path from "path"; +import { findPackageRoot } from "workspace-tools"; const localRequire = createRequire(import.meta.url); async function bundle() { console.log("\nBundling lage with esbuild..."); + const packageRoot = findPackageRoot(process.cwd()) || process.cwd(); + const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf-8")); + // Mapping from output path (relative to dist, no extension) to input path (relative to package root) const entryPoints = { lage: "@lage-run/cli/lib/cli.js", @@ -16,7 +20,13 @@ async function bundle() { "workers/targetWorker": "@lage-run/scheduler/lib/workers/targetWorker.js", }; // List of external modules that should not be bundled - const external = ["fsevents", "glob-hasher", "./workers/targetWorker"]; + const external = [ + // Externalize deps and optional deps, which are native packages + ...Object.keys(packageJson.dependencies), + ...Object.keys(packageJson.optionalDependencies), + // Also don't re-bundle targetWorker from the other entry points + "./workers/targetWorker", + ]; // Due to the fact that workers require the runner to be in the same directory, // add the runners to the entry points, as well as the externals (so the file is preserved). @@ -37,26 +47,31 @@ async function bundle() { } } - await esbuild.build({ + /** @type {import('esbuild').BuildOptions} */ + const esbuildOptions = { + absWorkingDir: packageRoot, entryPoints, bundle: true, platform: "node", target: ["node16"], - outdir: "dist", - sourcemap: true, logLevel: "info", external, + }; + + // Minified build + console.log("\nCreating minified bundles under dist"); + await esbuild.build({ + ...esbuildOptions, + outdir: "dist", + sourcemap: true, minify: true, }); + // Debug build + console.log("\nCreating debug bundles under dist/debug"); await esbuild.build({ - entryPoints, - bundle: true, - platform: "node", - target: ["node16"], + ...esbuildOptions, outdir: "dist/debug", - logLevel: "info", - external, minify: false, }); } From 43681e43b8d179f1cab01f88b0caffe1307ce881 Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Wed, 24 Apr 2024 18:57:23 -0700 Subject: [PATCH 5/6] depcheck --- scripts/worker/depcheck.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/scripts/worker/depcheck.js b/scripts/worker/depcheck.js index 6aeec20fc..a357f25b5 100644 --- a/scripts/worker/depcheck.js +++ b/scripts/worker/depcheck.js @@ -4,35 +4,41 @@ const path = require("path"); const depcheck = require("depcheck"); -module.exports = async function depcheckWorker({ target }) { - const ignored = ["@lage-run/monorepo-scripts", "@lage-run/docs"]; +const ignoredPackages = ["@lage-run/monorepo-scripts", "@lage-run/docs"]; +/** @type {Record} */ +const ignoreUnused = { + // used in ways that aren't detected + lage: { dependencies: ["glob-hasher"], devDependencies: ["@lage-run/scheduler"] }, +}; +module.exports = async function depcheckWorker({ target: { packageName, cwd } }) { // ignore the tooling package: monorepo-scripts - if (ignored.includes(target.packageName)) { + if (ignoredPackages.includes(packageName)) { return; } - const results = await depcheck(target.cwd, { + const results = await depcheck(cwd, { ignoreBinPackage: true, ignorePatterns: ["node_modules", "dist", "lib", "build"], - ignoreMatches: ["yoga-layout-prebuilt", "glob-hasher"], }); let hasErrors = false; - let formattedError = `Depcheck errors detected in ${target.packageName}\n\n`; + let formattedError = `Depcheck errors detected in ${packageName}\n\n`; - if (results.dependencies.length > 0) { + const unusedDeps = results.dependencies.filter((dep) => ignoreUnused[packageName]?.dependencies?.includes(dep)); + if (unusedDeps.length > 0) { hasErrors = true; formattedError += `Unused dependency: \n`; - for (const dep of results.dependencies) { + for (const dep of unusedDeps) { formattedError += ` ${dep}\n`; } } - if (results.devDependencies.length > 0) { + const unusedDevDeps = results.devDependencies.filter((dep) => ignoreUnused[packageName]?.devDependencies?.includes(dep)); + if (unusedDevDeps.length > 0) { hasErrors = true; formattedError += `Unused devDependency: \n`; - for (const dep of results.devDependencies) { + for (const dep of unusedDevDeps) { formattedError += ` ${dep}\n`; } } From 97477a3ab4db75b0d32f707e77fc709b4fbba0f4 Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Wed, 24 Apr 2024 19:00:36 -0700 Subject: [PATCH 6/6] depcheck --- .../change-3938e9f7-033d-42a7-b3cb-d40316a9382f.json | 11 +++++++++++ packages/cache/package.json | 3 +-- scripts/worker/depcheck.js | 5 ++--- 3 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 change/change-3938e9f7-033d-42a7-b3cb-d40316a9382f.json diff --git a/change/change-3938e9f7-033d-42a7-b3cb-d40316a9382f.json b/change/change-3938e9f7-033d-42a7-b3cb-d40316a9382f.json new file mode 100644 index 000000000..ce81ffd30 --- /dev/null +++ b/change/change-3938e9f7-033d-42a7-b3cb-d40316a9382f.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "type": "patch", + "comment": "Remove unused glob-hasher dep", + "packageName": "@lage-run/cache", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" + } + ] +} \ No newline at end of file diff --git a/packages/cache/package.json b/packages/cache/package.json index 8ce847e41..b3f20c19b 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -21,8 +21,7 @@ "@lage-run/logger": "^1.3.0", "backfill-cache": "5.7.1", "backfill-config": "6.4.1", - "backfill-logger": "5.2.1", - "glob-hasher": "^1.3.0" + "backfill-logger": "5.2.1" }, "devDependencies": { "@lage-run/monorepo-fixture": "*", diff --git a/scripts/worker/depcheck.js b/scripts/worker/depcheck.js index a357f25b5..3a72e2372 100644 --- a/scripts/worker/depcheck.js +++ b/scripts/worker/depcheck.js @@ -12,7 +12,6 @@ const ignoreUnused = { }; module.exports = async function depcheckWorker({ target: { packageName, cwd } }) { - // ignore the tooling package: monorepo-scripts if (ignoredPackages.includes(packageName)) { return; } @@ -25,7 +24,7 @@ module.exports = async function depcheckWorker({ target: { packageName, cwd } }) let hasErrors = false; let formattedError = `Depcheck errors detected in ${packageName}\n\n`; - const unusedDeps = results.dependencies.filter((dep) => ignoreUnused[packageName]?.dependencies?.includes(dep)); + const unusedDeps = results.dependencies.filter((dep) => !ignoreUnused[packageName]?.dependencies?.includes(dep)); if (unusedDeps.length > 0) { hasErrors = true; formattedError += `Unused dependency: \n`; @@ -34,7 +33,7 @@ module.exports = async function depcheckWorker({ target: { packageName, cwd } }) } } - const unusedDevDeps = results.devDependencies.filter((dep) => ignoreUnused[packageName]?.devDependencies?.includes(dep)); + const unusedDevDeps = results.devDependencies.filter((dep) => !ignoreUnused[packageName]?.devDependencies?.includes(dep)); if (unusedDevDeps.length > 0) { hasErrors = true; formattedError += `Unused devDependency: \n`;