diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c7ffb11454f7..a85dc4dd39dbc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -263,15 +263,15 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: '>=22.16.0' + node-version: '>=22.18.0' - uses: ./.github/actions/setup-go - run: npm ci - run: npx hereby generate - run: npx hereby generate:enums - run: npx hereby generate:ast - run: npx hereby generate:vendor - - run: node --experimental-strip-types ./tsc/internal/lsp/lsproto/_generate/fetchModel.mts - - run: node --experimental-strip-types ./tsc/internal/lsp/lsproto/_generate/generate.mts + - run: node ./tsc/internal/lsp/lsproto/_generate/fetchModel.mts + - run: node ./tsc/internal/lsp/lsproto/_generate/generate.mts - run: npx hereby generate:extension - run: npx hereby check:scripts - run: git add . @@ -310,6 +310,7 @@ jobs: - uses: ./.github/actions/setup-go - run: npm ci - run: go -C ./tools run ./cmd/checkmodpaths "$PWD" + - run: npx hereby check:herebyfile - run: npx hereby check:scripts - run: npx hereby typescript:check-platforms diff --git a/Herebyfile.mjs b/Herebyfile.mjs index 361b819ba9240..0f312902df2e8 100644 --- a/Herebyfile.mjs +++ b/Herebyfile.mjs @@ -2,8 +2,6 @@ import AdmZip from "adm-zip"; import chokidar from "chokidar"; -import { $ as _$ } from "execa"; -import { glob } from "glob"; import { task } from "hereby"; import assert from "node:assert"; import crypto from "node:crypto"; @@ -11,12 +9,15 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import url from "node:url"; -import { parseArgs } from "node:util"; -import pLimit from "p-limit"; -import pc from "picocolors"; +import { + parseArgs, + styleText, +} from "node:util"; import * as tar from "tar"; -import tmp from "tmp"; -import which from "which"; +import { + x, + xSync, +} from "tinyexec"; if (process.platform === "win32") { process.chdir(fs.realpathSync.native(process.cwd())); @@ -27,8 +28,48 @@ const __dirname = path.dirname(__filename); const isCI = !!process.env.CI || !!process.env.TF_BUILD; -const $pipe = _$({ verbose: "short" }); -const $ = _$({ verbose: "short", stdio: "inherit" }); +/** + * @typedef {{ + * captureOutput?: boolean; + * cwd?: string; + * env?: NodeJS.ProcessEnv; + * signal?: AbortSignal; + * }} RunOptions + */ + +/** + * @param {string} arg + */ +function formatCommandArg(arg) { + return arg && /^[\w@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg); +} + +/** + * @param {string} command + * @param {readonly string[]} [args] + * @param {RunOptions} [options] + */ +function run(command, args = [], options = {}) { + console.log("$ " + [command, ...args].map(formatCommandArg).join(" ")); + return x(command, args, { + throwOnError: true, + ...(options.signal ? { signal: options.signal } : {}), + nodeOptions: { + cwd: options.cwd, + env: options.env ? { ...process.env, ...options.env } : undefined, + stdio: options.captureOutput ? "pipe" : "inherit", + }, + }); +} + +/** + * @param {string} command + * @param {readonly string[]} [args] + * @param {Omit} [options] + */ +function runOutput(command, args, options) { + return run(command, args, { ...options, captureOutput: true }); +} /** * @param {string} name @@ -142,17 +183,64 @@ function memoize(fn) { }; } -const tools = new Map([ - ["gotest.tools/gotestsum", "latest"], -]); +/** + * @param {string} pattern + * @param {string[]} [exclude] + */ +async function globFiles(pattern, exclude) { + const files = []; + const absolute = path.isAbsolute(pattern); + for await (const entry of fs.promises.glob(pattern, { exclude, withFileTypes: true })) { + if (entry.isFile()) { + const file = path.join(entry.parentPath, entry.name); + files.push(absolute ? file : path.relative(process.cwd(), file)); + } + } + return files; +} /** - * @param {string} tool + * @param {(() => Promise)[]} tasks + * @param {number} concurrency */ -function isInstalled(tool) { - return !!which.sync(tool, { nothrow: true }); +async function runWithConcurrencyLimit(tasks, concurrency) { + const queue = tasks.values(); + /** @type {unknown[]} */ + const errors = []; + const workers = Array.from({ length: Math.min(concurrency, tasks.length) }, async () => { + for (const task of queue) { + try { + await task(); + } + catch (error) { + errors.push(error); + } + } + }); + await Promise.all(workers); + if (errors.length === 1) { + throw errors[0]; + } + if (errors.length > 1) { + throw new AggregateError(errors, `${errors.length} concurrent tasks failed`); + } } +const tools = new Map([ + ["gotest.tools/gotestsum", "latest"], +]); + +const hasGotestsum = memoize(() => { + try { + return xSync("gotestsum", ["--version"], { + nodeOptions: { stdio: "ignore" }, + }).exitCode === 0; + } + catch { + return false; + } +}); + const builtLocal = "./built/local"; const libsDir = "./tsc/internal/bundled/libs"; @@ -201,7 +289,11 @@ function buildTsc(opts) { opts ||= {}; const out = opts.out ?? path.resolve("./built/local/tsc" + (process.platform === "win32" ? ".exe" : "")); const env = { ...goBuildEnv, ...opts.env }; - return $({ cancelSignal: opts.abortSignal, env, cwd: "./tsc" })`go build ${goBuildFlags} ${opts.extraFlags ?? []} ${goBuildTags("noembed")} -o ${out} ./cmd/tsc`; + return run("go", ["build", ...goBuildFlags, ...(opts.extraFlags ?? []), ...goBuildTags("noembed"), "-o", out, "./cmd/tsc"], { + signal: opts.abortSignal, + env, + cwd: "./tsc", + }); } export const tscBuild = task({ @@ -279,7 +371,7 @@ export const generate = task({ name: "generate", description: "Runs go generate on the project.", run: async () => { - await $({ cwd: "./tsc" })`go generate -v ./...`; + await run("go", ["generate", "-v", "./..."], { cwd: "./tsc" }); }, }); @@ -287,7 +379,7 @@ export const generateExtension = task({ name: "generate:extension", description: "Generates files in the extension", run: async () => { - await $`npm run -w native-preview generateLocBundle`; + await run("npm", ["run", "-w", "native-preview", "generateLocBundle"]); }, }); @@ -559,7 +651,7 @@ async function runGenerateEnums() { console.log(` ${def.name}: ${members.length} members → ${camelName}.enum.ts, ${camelName}.ts`); } - await $`dprint fmt ${generatedFiles}`; + await run("dprint", ["fmt", ...generatedFiles]); console.log("Done."); } @@ -572,15 +664,15 @@ export const generateEnums = task({ export const generateAST = task({ name: "generate:ast", description: "Generates AST and encoder files from ast.json.", - run: () => $`node --experimental-strip-types --no-warnings ./tools/scripts/tsc/generate.ts`, + run: () => run("node", ["./tools/scripts/tsc/generate.ts"]), }); export const generateAPI = task({ name: "generate:api", description: "Generates API files from internal/api/proto.go and internal/api/session.go.", run: async () => { - await $`go -C ./tools run ./gen-proto ../tsc/internal/api/proto.go ../packages/typescript/src/api/proto.generated.ts`; - await $`npx dprint fmt packages/typescript/src/api/proto.generated.ts`; + await run("go", ["-C", "./tools", "run", "./gen-proto", "../tsc/internal/api/proto.go", "../packages/typescript/src/api/proto.generated.ts"]); + await run("npx", ["dprint", "fmt", "packages/typescript/src/api/proto.generated.ts"]); }, }); @@ -701,7 +793,7 @@ async function checkUnusedBaselines(trackingDir) { return []; } - const allBaselines = await glob(`${refBaseline}/**`, { nodir: true }); + const allBaselines = await globFiles(`${refBaseline}/**`); const unusedBaselines = allBaselines .map(p => path.relative(refBaseline, p)) .filter(p => !usedBaselines.has(p)); @@ -709,13 +801,11 @@ async function checkUnusedBaselines(trackingDir) { return unusedBaselines; } -const $test = $({ env: goTestEnv, cwd: "./tsc" }); - /** * @param {string} taskName */ function gotestsum(taskName) { - const args = isInstalled("gotestsum") ? ["gotestsum", ...goTestSumFlags, "--"] : ["go", "test"]; + const args = hasGotestsum() ? ["gotestsum", ...goTestSumFlags, "--"] : ["go", "test"]; return args.concat(goTestFlags(taskName)); } @@ -735,13 +825,13 @@ async function runTests() { // Create a tmp directory for baseline tracking if enabled /** @type {string | undefined} */ let trackingDir; - /** @type {(() => void) | undefined} */ + /** @type {(() => Promise) | undefined} */ let cleanupTracking; if (baselineTrackingEnabled) { - const tmpDir = tmp.dirSync({ prefix: "tsgo-baseline-tracking-", unsafeCleanup: true }); - trackingDir = tmpDir.name; - cleanupTracking = tmpDir.removeCallback; + const tempTrackingDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-baseline-tracking-")); + trackingDir = tempTrackingDir; + cleanupTracking = () => rimraf(tempTrackingDir); } try { @@ -749,19 +839,22 @@ async function runTests() { ...goTestEnv, ...(trackingDir ? { TSGO_BASELINE_TRACKING_DIR: trackingDir } : {}), }; - const $testWithTracking = $({ env: testEnv, cwd: "./tsc" }); - await $testWithTracking`${gotestsum("tests")} ./... ${isCI ? ["--timeout=45m"] : []}`; + const command = gotestsum("tests"); + await run(command[0], [...command.slice(1), "./...", ...(isCI ? ["--timeout=45m"] : [])], { + env: testEnv, + cwd: "./tsc", + }); // Check for unused baselines after tests complete if (trackingDir) { const unusedBaselines = await checkUnusedBaselines(trackingDir); if (unusedBaselines.length > 0) { - console.error(pc.red(`\nFound ${unusedBaselines.length} unused baseline file(s):`)); + console.error(styleText("red", `\nFound ${unusedBaselines.length} unused baseline file(s):`)); for (const baseline of unusedBaselines.slice(0, 20)) { - console.error(pc.red(` ${baseline}`)); + console.error(styleText("red", ` ${baseline}`)); } if (unusedBaselines.length > 20) { - console.error(pc.red(` ... and ${unusedBaselines.length - 20} more`)); + console.error(styleText("red", ` ... and ${unusedBaselines.length - 20} more`)); } // Create .delete files for each unused baseline so baseline-accept can remove them @@ -770,7 +863,7 @@ async function runTests() { await fs.promises.mkdir(path.dirname(deleteFilePath), { recursive: true }); await fs.promises.writeFile(deleteFilePath, ""); } - console.error(pc.red(`\nRun 'hereby baseline-accept' to delete them.`)); + console.error(styleText("red", `\nRun 'hereby baseline-accept' to delete them.`)); throw new Error(`Found ${unusedBaselines.length} unused baseline file(s). Run 'hereby baseline-accept' to delete them.`); } @@ -778,13 +871,13 @@ async function runTests() { } finally { if (cleanupTracking) { - cleanupTracking(); + await cleanupTracking(); } } } async function runTestExtension() { - await $`npm test -w native-preview`; + await run("npm", ["test", "-w", "native-preview"]); } export const testTsc = task({ @@ -804,7 +897,8 @@ export const test = task({ async function runTestBenchmarks() { // Run the benchmarks once to ensure they compile and run without errors. - await $test`${goTest("benchmarks")} -run=- -bench=. -benchtime=1x ./...`; + const command = goTest("benchmarks"); + await run(command[0], [...command.slice(1), "-run=-", "-bench=.", "-benchtime=1x", "./..."], { env: goTestEnv, cwd: "./tsc" }); } export const testBenchmarks = task({ @@ -814,12 +908,13 @@ export const testBenchmarks = task({ }); async function runTestTools() { - await $test({ cwd: path.join(__dirname, "tools") })`${gotestsum("tools")} ./...`; + const command = gotestsum("tools"); + await run(command[0], [...command.slice(1), "./..."], { env: goTestEnv, cwd: path.join(__dirname, "tools") }); } async function runTestAPI() { - // await $`npm run -w @typescript/typescript test:only`; // doesn't work on windows - some path escaping isn't done correctly, test runner runs no tests - await _$({ verbose: "short", stdio: "inherit", cwd: "./packages/typescript" })`node --experimental-strip-types --no-warnings --conditions @typescript/source --test ./test/**/*.test.ts`; + // Running the package script doesn't work on Windows; some path escaping isn't done correctly and the test runner runs no tests. + await run("node", ["--conditions", "@typescript/source", "--test", "./test/**/*.test.ts"], { cwd: "./packages/typescript" }); } export const testTools = task({ @@ -838,7 +933,7 @@ export const buildAPI = task({ name: "build:api", description: "Builds @typescript/typescript JS API.", run: async () => { - await $`npm run -w @typescript/typescript build`; + await run("npm", ["run", "-w", "@typescript/typescript", "build"]); }, }); @@ -847,7 +942,7 @@ export const buildAPITests = task({ description: "Builds the @typescript/typescript JS API tests.", dependencies: [generateEnums, generateAPI], run: async () => { - await $`npm run -w @typescript/typescript build:test`; + await run("npm", ["run", "-w", "@typescript/typescript", "build:test"]); }, }); @@ -872,7 +967,7 @@ export const testAll = task({ }, }); -const customLinterPath = "./tools/custom-gcl"; +const customLinterPath = `./tools/custom-gcl${process.platform === "win32" ? ".exe" : ""}`; const customLinterHashPath = customLinterPath + ".hash"; const golangciLintPackage = memoize(() => { @@ -890,15 +985,13 @@ const golangciLintPackage = memoize(() => { }); const customlintHash = memoize(() => { - const files = glob.sync([ + const files = fs.globSync([ "./tools/go.mod", "./tools/customlint/**/*", "./.custom-gcl.yml", ], { - ignore: "**/testdata/**", - nodir: true, - absolute: true, - }); + exclude: ["**/testdata/**"], + }).filter(file => fs.statSync(file).isFile()).map(file => path.resolve(file)); files.sort(); const hash = crypto.createHash("sha256"); @@ -914,15 +1007,15 @@ const customlintHash = memoize(() => { const buildCustomLinter = memoize(async () => { const hash = customlintHash(); if ( - isInstalled(customLinterPath) + fs.existsSync(customLinterPath) && fs.existsSync(customLinterHashPath) && fs.readFileSync(customLinterHashPath, "utf8") === hash ) { return; } - await $`go run ${golangciLintPackage()} custom`; - await $`${customLinterPath} cache clean`; + await run("go", ["run", golangciLintPackage(), "custom"]); + await run(customLinterPath, ["cache", "clean"]); fs.writeFileSync(customLinterHashPath, hash); }); @@ -945,9 +1038,9 @@ async function runLint() { } const resolvedCustomLinterPath = path.resolve(customLinterPath); - await $({ cwd: "./tsc" })`${resolvedCustomLinterPath} ${lintArgs} --config ../.golangci.yml`; + await run(resolvedCustomLinterPath, [...lintArgs, "--config", "../.golangci.yml"], { cwd: "./tsc" }); console.log("Linting tools"); - await $({ cwd: "./tools" })`${resolvedCustomLinterPath} ${lintArgs} --config ../.golangci.yml`; + await run(resolvedCustomLinterPath, [...lintArgs, "--config", "../.golangci.yml"], { cwd: "./tools" }); } export const installTools = task({ @@ -955,7 +1048,7 @@ export const installTools = task({ description: "Installs optional tools for developing within the repo.", run: async () => { await Promise.all([ - ...[...tools].map(([tool, version]) => $`go install ${tool}${version ? `@${version}` : ""}`), + ...[...tools].map(([tool, version]) => run("go", ["install", tool + (version ? `@${version}` : "")])), buildCustomLinter(), ]); }, @@ -968,17 +1061,43 @@ export const format = task({ }); async function runFormat() { - await $`dprint fmt`; + await run("dprint", ["fmt"]); } export const checkFormat = task({ name: "check:format", description: "Checks that the repo is formatted.", run: async () => { - await $`dprint check`; + await run("dprint", ["check"]); }, }); +export const checkHerebyfile = task({ + name: "check:herebyfile", + description: "Type-checks Herebyfile.mjs.", + run: () => + run("node", [ + "./node_modules/typescript/bin/tsc", + "--noEmit", + "--allowJs", + "--checkJs", + "--target", + "es2022", + "--lib", + "es2024,esnext.array,esnext.collection,esnext.iterator", + "--module", + "nodenext", + "--moduleResolution", + "nodenext", + "--types", + "node", + "--strict", + "--esModuleInterop", + "--skipLibCheck", + "Herebyfile.mjs", + ]), +}); + const scriptTsconfigs = [ "./tools/scripts/tsc/tsconfig.json", "./tsc/internal/lsp/lsproto/_generate/tsconfig.json", @@ -990,7 +1109,7 @@ export const checkScripts = task({ run: async () => { for (const tsconfig of scriptTsconfigs) { console.log(`Type-checking ${tsconfig}`); - await $`tsc -p ${tsconfig}`; + await run("tsc", ["-p", tsconfig]); } }, }); @@ -1009,13 +1128,13 @@ function baselineAcceptTask(localBaseline, refBaseline) { } return async () => { - const toCopy = await glob(`${localBaseline}/**`, { nodir: true, ignore: `${localBaseline}/**/*.delete` }); + const toCopy = await globFiles(`${localBaseline}/**`, [`${localBaseline}/**/*.delete`]); for (const p of toCopy) { const out = localPathToRefPath(p); await fs.promises.mkdir(path.dirname(out), { recursive: true }); await fs.promises.copyFile(p, out); } - const toDelete = await glob(`${localBaseline}/**/*.delete`, { nodir: true }); + const toDelete = await globFiles(`${localBaseline}/**/*.delete`); for (const p of toDelete) { const out = localPathToRefPath(p).replace(/\.delete$/, ""); await rimraf(out); @@ -1045,7 +1164,7 @@ function getDiffTool() { export const diff = task({ name: "diff", description: "Diffs baselines using the diff tool specified by the 'DIFF' environment variable", - run: () => $`${getDiffTool()} ${refBaseline} ${localBaseline}`, + run: () => run(getDiffTool(), [refBaseline, localBaseline]), }); /** @@ -1109,7 +1228,7 @@ async function watchDebounced(name, run, options) { running = false; } if (watching) { - console.log(pc.yellowBright(`[${name}] run complete, waiting for changes...`)); + console.log(styleText("yellowBright", `[${name}] run complete, waiting for changes...`)); await promise; } } @@ -1143,9 +1262,9 @@ async function watchDebounced(name, run, options) { */ function beginRun(path) { if (debouncer.empty) { - console.log(pc.yellowBright(`[${name}] changed due to '${path}', restarting...`)); + console.log(styleText("yellowBright", `[${name}] changed due to '${path}', restarting...`)); if (running) { - console.log(pc.yellowBright(`[${name}] aborting in-progress run...`)); + console.log(styleText("yellowBright", `[${name}] aborting in-progress run...`)); } abortController.abort(); abortController = new AbortController(); @@ -1165,7 +1284,7 @@ async function watchDebounced(name, run, options) { function endWatchMode() { if (watching) { watching = false; - console.log(pc.yellowBright(`[${name}] exiting watch mode...`)); + console.log(styleText("yellowBright", `[${name}] exiting watch mode...`)); abortController.abort(); watcher.close(); } @@ -1316,7 +1435,7 @@ async function sign(filelist, unchangedOutputOkay = false) { console.log("filelist:", data); if (!process.env.MBSIGN_APPFOLDER) { - console.log(pc.yellow("Faking signing because MBSIGN_APPFOLDER is not set.")); + console.log(styleText("yellow", "Faking signing because MBSIGN_APPFOLDER is not set.")); // Fake signing for testing. @@ -1427,7 +1546,7 @@ async function sign(filelist, unchangedOutputOkay = false) { try { const dll = path.join(process.env.MBSIGN_APPFOLDER, "DDSignFiles.dll"); const filelistFlag = `/filelist:${filelistPath}`; - await $`dotnet ${dll} -- ${filelistFlag}`; + await run("dotnet", [dll, "--", filelistFlag]); } finally { await fs.promises.unlink(filelistPath); @@ -1783,7 +1902,7 @@ function goDistTargetToPlatform(target) { } async function runCheckPlatforms() { - const { stdout } = await $pipe`go tool dist list -json`; + const { stdout } = await runOutput("go", ["tool", "dist", "list", "-json"]); /** @type {GoDistTarget[]} */ const goTargets = JSON.parse(stdout); const goTargetSet = new Set(goTargets.map(({ GOOS, GOARCH }) => `${GOOS}/${GOARCH}`)); @@ -1900,8 +2019,8 @@ async function runBuildNativePreviewPackages() { } stripSourceConditions(inputPackageJson); - const { stdout: gitHead } = await $pipe`git rev-parse HEAD`; - inputPackageJson.gitHead = gitHead; + const { stdout: gitHead } = await runOutput("git", ["rev-parse", "HEAD"]); + inputPackageJson.gitHead = gitHead.trim(); inputPackageJson.publishConfig = { access: "public", tag: getPublishTag(), @@ -1931,11 +2050,11 @@ async function runBuildNativePreviewPackages() { await fs.promises.copyFile("NOTICE.txt", path.join(mainPackageDir, "NOTICE.txt")); // Build JS API and copy dist into the package. - await $`npm run -w @typescript/typescript build`; + await run("npm", ["run", "-w", "@typescript/typescript", "build"]); await cpRecursive(path.join(inputDir, "dist"), path.join(mainPackageDir, "dist")); // Validate that .d.ts files contain no external imports (all imports must start with "." or "#"). - const dtsFiles = await glob(`${mainPackageDir}/dist/**/*.d.ts`); + const dtsFiles = await globFiles(`${mainPackageDir}/dist/**/*.d.ts`); const importErrors = []; for (const dtsFile of dtsFiles) { const content = await fs.promises.readFile(dtsFile, "utf-8"); @@ -2004,12 +2123,11 @@ async function runBuildNativePreviewPackages() { await build(); // Build machines have too little space. // Clear the Go build cache between platforms. - await $`go clean -cache`; + await run("go", ["clean", "-cache"]); } } else { - const buildLimit = pLimit(os.availableParallelism()); - await Promise.all(platformBuilders.map(f => buildLimit(f))); + await runWithConcurrencyLimit(platformBuilders, os.availableParallelism()); } } @@ -2087,7 +2205,7 @@ async function runSignNativePreviewPackages() { // along with a notarization step. for (const p of filelistPaths) { // ESRP preserves entitlements from an existing ad-hoc signature. - await $pipe`go -C ./tools run ./cmd/machotool sign ${typescriptMacEntitlementsPath} ${p.path}`; + await runOutput("go", ["-C", "./tools", "run", "./cmd/machotool", "sign", typescriptMacEntitlementsPath, p.path]); const unsignedZipPath = path.join(tmp, `${p.tmpName}.unsigned.zip`); const signedZipPath = path.join(tmp, `${p.tmpName}.signed.zip`); @@ -2148,7 +2266,7 @@ async function runSignNativePreviewPackages() { for (const p of macZips) { await fs.promises.chmod(p.path, 0o755); - await $pipe`go -C ./tools run ./cmd/machotool verify ${typescriptMacEntitlementsPath} ${p.path}`; + await runOutput("go", ["-C", "./tools", "run", "./cmd/machotool", "verify", typescriptMacEntitlementsPath, p.path]); } } } @@ -2170,7 +2288,7 @@ async function runPackNativePreviewPackages() { const platforms = getPlatforms(); await Promise.all([mainNativePreviewPackage, ...platforms].map(async ({ npmDir, npmTarball }) => { - const { stdout } = await $pipe`npm pack --json ${npmDir}`; + const { stdout } = await runOutput("npm", ["pack", "--json", npmDir]); const filename = JSON.parse(stdout)[0].filename.replace("@", "").replace("/", "-"); await fs.promises.rename(filename, npmTarball); })); @@ -2291,7 +2409,10 @@ async function getPublishedPlatformPackageLibDirWorker(npmPackageName) { } console.log(`Fetching ${npmPackageName}@${version} with npm.`); - const { stdout } = await $pipe({ cwd: tarballDestination, env: releasePackageEnv })`npm pack --json ${npmPackageName}@${version}`; + const { stdout } = await runOutput("npm", ["pack", "--json", `${npmPackageName}@${version}`], { + cwd: tarballDestination, + env: releasePackageEnv, + }); const [packed] = JSON.parse(stdout); if (!packed.filename || typeof packed.filename !== "string") { throw new Error(`npm pack ${npmPackageName}@${version} did not return a filename.`); @@ -2322,7 +2443,7 @@ async function runPackVsixExtensions() { } // We don't use vscode:prepublish, as that would run the build for each package below. - await $({ cwd: extensionDir, env: releasePackageEnv })`npm run bundle:release`; + await run("npm", ["run", "bundle:release"], { cwd: extensionDir, env: releasePackageEnv }); let version = "0.0.0"; if (options.forRelease) { @@ -2361,10 +2482,16 @@ async function runPackVsixExtensions() { await fs.promises.copyFile("NOTICE.txt", path.join(thisExtensionDir, "NOTICE.txt")); - await $({ cwd: thisExtensionDir, env: releasePackageEnv })`vsce package ${version} --no-update-package-json --no-dependencies --out ${vsixPath} --target ${vscodeTarget}`; + await run("vsce", ["package", version, "--no-update-package-json", "--no-dependencies", "--out", vsixPath, "--target", vscodeTarget], { + cwd: thisExtensionDir, + env: releasePackageEnv, + }); if (options.forRelease) { - await $({ cwd: thisExtensionDir, env: releasePackageEnv })`vsce generate-manifest --packagePath ${vsixPath} --out ${vsixManifestPath}`; + await run("vsce", ["generate-manifest", "--packagePath", vsixPath, "--out", vsixManifestPath], { + cwd: thisExtensionDir, + env: releasePackageEnv, + }); await fs.promises.cp(vsixManifestPath, vsixSignaturePath); } })); @@ -2439,8 +2566,8 @@ export const tidy = task({ name: "tidy", description: "Tidies both Go modules and synchronizes the workspace.", run: async () => { - await $({ cwd: "./tsc" })`go mod tidy`; - await $({ cwd: "./tools" })`go mod tidy`; - await $`go work sync`; + await run("go", ["mod", "tidy"], { cwd: "./tsc" }); + await run("go", ["mod", "tidy"], { cwd: "./tools" }); + await run("go", ["work", "sync"]); }, }); diff --git a/package-lock.json b/package-lock.json index 708f5a6411e26..adb31ed740824 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,26 +12,18 @@ "packages/*" ], "devDependencies": { - "@types/adm-zip": "^0.5.8", - "@types/node": "latest", - "@types/tmp": "^0.2.6", - "@types/which": "^3.0.4", + "@types/node": "^22.18.0", "@unicode/unicode-15.1.0": "^1.6.17", - "adm-zip": "^0.5.17", + "adm-zip": "^0.6.0", "chokidar": "^5.0.0", "dprint": "^0.55.1", - "execa": "^9.6.1", - "glob": "^13.0.6", "hereby": "^1.15.1", - "p-limit": "^7.3.0", - "picocolors": "^1.1.1", "tar": "^7.5.19", - "tmp": "^0.2.7", - "typescript": "^6.0.3", - "which": "^6.0.1" + "tinyexec": "^1.3.0", + "typescript": "^6.0.3" }, "engines": { - "node": ">=20.19" + "node": ">=22.18" } }, "node_modules/@azu/format-text": { @@ -1213,13 +1205,6 @@ "node": ">= 8" } }, - "node_modules/@sec-ant/readable-stream": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "dev": true, - "license": "MIT" - }, "node_modules/@secretlint/config-creator": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", @@ -1394,19 +1379,6 @@ "node": ">=20.0.0" } }, - "node_modules/@sindresorhus/merge-streams": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@textlint/ast-node-types": { "version": "15.8.0", "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.8.0.tgz", @@ -1493,24 +1465,14 @@ "@textlint/ast-node-types": "15.8.0" } }, - "node_modules/@types/adm-zip": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.8.tgz", - "integrity": "sha512-RVVH7QvZYbN+ihqZ4kX/dMiowf6o+Jk1fNwiSdx0NahBJLU787zkULhGhJM8mf/obmLGmgdMM0bXsQTmyfbR7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/node": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz", - "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~8.3.0" + "undici-types": "~6.21.0" } }, "node_modules/@types/normalize-package-data": { @@ -1527,13 +1489,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/tmp": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", - "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/vscode": { "version": "1.125.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.125.0.tgz", @@ -1541,13 +1496,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/which": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.4.tgz", - "integrity": "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==", - "dev": true, - "license": "MIT" - }, "node_modules/@typescript/bundled-typescript": { "name": "typescript", "version": "7.0.2", @@ -2159,13 +2107,13 @@ ] }, "node_modules/adm-zip": { - "version": "0.5.18", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz", - "integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.0.tgz", + "integrity": "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==", "dev": true, "license": "MIT", "engines": { - "node": ">=12.0" + "node": ">=14.0" } }, "node_modules/agent-base": { @@ -2604,44 +2552,6 @@ "node": ">=18" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/css-select": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", @@ -3060,33 +2970,6 @@ "@esbuild/win32-x64": "0.28.2" } }, - "node_modules/execa": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", - "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^4.0.0", - "cross-spawn": "^7.0.6", - "figures": "^6.1.0", - "get-stream": "^9.0.0", - "human-signals": "^8.0.1", - "is-plain-obj": "^4.1.0", - "is-stream": "^4.0.1", - "npm-run-path": "^6.0.0", - "pretty-ms": "^9.2.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^4.0.0", - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.5.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -3149,22 +3032,6 @@ "reusify": "^1.0.4" } }, - "node_modules/figures": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", - "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-unicode-supported": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3267,23 +3134,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stream": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", - "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sec-ant/readable-stream": "^0.4.1", - "is-stream": "^4.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -3536,16 +3386,6 @@ "node": ">= 14" } }, - "node_modules/human-signals": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", - "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -3698,45 +3538,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-wsl": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", @@ -3753,16 +3554,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isexe": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", - "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=20" - } - }, "node_modules/istextorbinary": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", @@ -4286,36 +4077,6 @@ "dev": true, "license": "ISC" }, - "node_modules/npm-run-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", - "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0", - "unicorn-magic": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -4372,22 +4133,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-limit": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.1.tgz", - "integrity": "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.2.1" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-map": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.6.tgz", @@ -4419,19 +4164,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-ms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", - "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/parse-semver": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", @@ -4505,16 +4237,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/path-scurry": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", @@ -4611,22 +4333,6 @@ "node": ">=10" } }, - "node_modules/pretty-ms": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", - "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parse-ms": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pump": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", @@ -4923,29 +4629,6 @@ "node": ">=10" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/side-channel": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", @@ -5022,19 +4705,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -5216,19 +4886,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/strip-final-newline": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", - "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -5436,6 +5093,16 @@ "node": ">=20.0.0" } }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tmp": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", @@ -5553,9 +5220,9 @@ } }, "node_modules/undici-types": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", - "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -5668,12 +5335,12 @@ "license": "MIT" }, "node_modules/vscode-tas-client": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/vscode-tas-client/-/vscode-tas-client-0.2.1.tgz", - "integrity": "sha512-htmjTFhOkM213OKv42cyCboc6D4vhZTdGHLb+GiNvFeK66eBdTbec2P41rgIg9YxnBgcDOlkvTElRorm3VitXw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/vscode-tas-client/-/vscode-tas-client-0.3.1.tgz", + "integrity": "sha512-UD9rTFHEpFfUQgbIVsugNYk17E5zn6JW/VGwAIDmcBWT0XvYEgb6xP9m1FamdjTEvBo4o039S/1uaropj1H9FQ==", "license": "MIT", "dependencies": { - "tas-client": "^0.4.0" + "tas-client": "^0.4.3" }, "engines": { "vscode": "^1.85.0" @@ -5707,22 +5374,6 @@ "node": ">=18" } }, - "node_modules/which": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", - "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^4.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -5804,32 +5455,6 @@ "buffer-crc32": "~0.2.3" } }, - "node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz", - "integrity": "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "packages/typescript": { "name": "@typescript/typescript", "version": "0.0.0", @@ -5838,7 +5463,7 @@ "tsc": "bin/tsc" }, "devDependencies": { - "@types/node": "^25.9.4", + "@types/node": "^22.18.0", "tinybench": "^6.0.2", "vscode-jsonrpc": "^9.0.0" }, @@ -5846,30 +5471,13 @@ "node": ">=16.20.0" } }, - "packages/typescript/node_modules/@types/node": { - "version": "25.9.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.5.tgz", - "integrity": "sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, - "packages/typescript/node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, - "license": "MIT" - }, "packages/vscode-typescript": { "name": "native-preview", "version": "0.0.0", "dependencies": { "@vscode/extension-telemetry": "^1.5.2", "vscode-languageclient": "^10.0.1", - "vscode-tas-client": "^0.2.1" + "vscode-tas-client": "^0.3.1" }, "devDependencies": { "@types/vscode": "~1.125.0", diff --git a/package.json b/package.json index cde72125317de..144bf658a3476 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "license": "Apache-2.0", "engines": { - "node": ">=20.19" + "node": ">=22.18" }, "workspaces": [ "packages/*" @@ -24,23 +24,15 @@ "setup-hooks": "node tools/scripts/link-hooks.mjs" }, "devDependencies": { - "@types/adm-zip": "^0.5.8", - "@types/node": "latest", - "@types/tmp": "^0.2.6", - "@types/which": "^3.0.4", + "@types/node": "^22.18.0", "@unicode/unicode-15.1.0": "^1.6.17", - "adm-zip": "^0.5.17", + "adm-zip": "^0.6.0", "chokidar": "^5.0.0", "dprint": "^0.55.1", - "execa": "^9.6.1", - "glob": "^13.0.6", "hereby": "^1.15.1", - "p-limit": "^7.3.0", - "picocolors": "^1.1.1", "tar": "^7.5.19", - "tmp": "^0.2.7", - "typescript": "^6.0.3", - "which": "^6.0.1" + "tinyexec": "^1.3.0", + "typescript": "^6.0.3" }, "overrides": { "@microsoft/applicationinsights-web-basic": "3.3.11", @@ -53,9 +45,7 @@ "npm": "11.17.0" }, "allowScripts": { - "dprint@0.55.1": true, - "esbuild@0.28.1": true, - "@vscode/vsce-sign@2.0.9": true, + "@vscode/vsce-sign@2.1.0": true, "keytar@7.9.0": true, "dprint@0.55.2": true, "esbuild@0.28.2": true diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 1cd3718a07ae6..0b4898baf3b4c 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -96,9 +96,9 @@ "#vscode-jsonrpc/node": "./vendor/vscode-jsonrpc/lib/node/main.js" }, "scripts": { - "node": "node --experimental-strip-types --no-warnings --conditions @typescript/source", + "node": "node --conditions @typescript/source", "generate": "npm run generate:ast && npm run generate:encoder && npm run generate:sync", - "generate:ast": "node --experimental-strip-types --no-warnings ../../tools/scripts/tsc/generate-ts-ast.ts", + "generate:ast": "node ../../tools/scripts/tsc/generate-ts-ast.ts", "generate:encoder": "npm run node -- ../../tools/scripts/tsc/generate-encoder.ts", "generate:sync": "npm run node -- scripts/generateSync.ts", "build": "tsc -b", @@ -107,7 +107,7 @@ "test": "npm run test:only" }, "devDependencies": { - "@types/node": "^25.9.4", + "@types/node": "^22.18.0", "tinybench": "^6.0.2", "vscode-jsonrpc": "^9.0.0" } diff --git a/packages/typescript/scripts/generateSync.ts b/packages/typescript/scripts/generateSync.ts index ec1cd8584ca02..fb990d546e796 100755 --- a/packages/typescript/scripts/generateSync.ts +++ b/packages/typescript/scripts/generateSync.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env -S node --experimental-strip-types --no-warnings +#!/usr/bin/env node /** * Generates sync API from async API source files. @@ -18,10 +18,9 @@ * - Unwrap `Promise` → `T` in type references * * Usage: - * node --experimental-strip-types --no-warnings generateSync.ts + * node generateSync.ts */ -import { execaSync } from "execa"; import { mkdirSync, readFileSync, @@ -32,6 +31,7 @@ import { join, relative, } from "node:path"; +import { xSync } from "tinyexec"; import ts from "typescript"; function generatedHeader(asyncSourceRelPath: string): string { @@ -269,7 +269,7 @@ function removeAsyncAwaitAndPromise(source: string, fileName: string): string { // ── Formatting ─────────────────────────────────────────────────── function formatFiles(paths: string[]): void { - execaSync("dprint", ["fmt", ...paths]); + xSync("dprint", ["fmt", ...paths], { throwOnError: true }); } // ── Main ───────────────────────────────────────────────────────── diff --git a/packages/typescript/src/api/node/encoder.ts b/packages/typescript/src/api/node/encoder.ts index 0071f7970a579..5ed4bdbf71d86 100644 --- a/packages/typescript/src/api/node/encoder.ts +++ b/packages/typescript/src/api/node/encoder.ts @@ -1,3 +1,4 @@ +import { TextEncoder } from "node:util"; import type { FileReference, LiteralLikeNode, diff --git a/packages/typescript/src/api/node/node.infrastructure.ts b/packages/typescript/src/api/node/node.infrastructure.ts index 92b2922877e2a..5ba888afac659 100644 --- a/packages/typescript/src/api/node/node.infrastructure.ts +++ b/packages/typescript/src/api/node/node.infrastructure.ts @@ -39,9 +39,9 @@ export const NODE_EXTENDED_DATA_MASK = 0x00_ff_ff_ff; // source file, avoiding a direct dependency on RemoteSourceFile. // ═══════════════════════════════════════════════════════════════════════════ -// The global type is not available in earlier @types/node versions +// The global type is not available in earlier @types/node versions. export interface TextDecoder { - decode(input?: ArrayBufferView | ArrayBufferLike): string; + decode(input?: Uint8Array): string; } export interface SourceFileInfo { diff --git a/packages/typescript/src/api/node/wtf8.ts b/packages/typescript/src/api/node/wtf8.ts index 45bba64a91081..6ec4165b39848 100644 --- a/packages/typescript/src/api/node/wtf8.ts +++ b/packages/typescript/src/api/node/wtf8.ts @@ -5,6 +5,7 @@ const surrogateSecondByteMin = 0xA0; const surrogateSecondByteMax = 0xBF; const continuationByteMin = 0x80; const continuationByteMax = 0xBF; +type DecodeInput = ArrayBufferView | ArrayBufferLike; type DecodeOptions = Parameters[1]; function isWtf8Surrogate(bytes: Uint8Array, index: number): boolean { @@ -24,7 +25,7 @@ function hasSurrogateLeadByte(bytes: Uint8Array): boolean { return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).indexOf(surrogateLeadByte) >= 0; } -function toUint8Array(input: NodeJS.AllowSharedBufferSource): Uint8Array { +function toUint8Array(input: Exclude): Uint8Array { if (input instanceof Uint8Array) { return input; } @@ -35,7 +36,7 @@ function toUint8Array(input: NodeJS.AllowSharedBufferSource): Uint8Array { } export class Wtf8Decoder extends TextDecoder { - override decode(input?: NodeJS.AllowSharedBufferSource, options?: DecodeOptions): string { + override decode(input?: DecodeInput, options?: DecodeOptions): string { if (input === undefined) { return super.decode(input, options); } diff --git a/packages/vscode-typescript/package.json b/packages/vscode-typescript/package.json index b1a6d81ecdb5c..b85446bba7a61 100644 --- a/packages/vscode-typescript/package.json +++ b/packages/vscode-typescript/package.json @@ -380,7 +380,7 @@ "dependencies": { "@vscode/extension-telemetry": "^1.5.2", "vscode-languageclient": "^10.0.1", - "vscode-tas-client": "^0.2.1" + "vscode-tas-client": "^0.3.1" }, "devDependencies": { "@types/vscode": "~1.125.0", diff --git a/tools/scripts/tsc/generate-encoder.ts b/tools/scripts/tsc/generate-encoder.ts index fc1a67022a39d..953b55fe9d546 100644 --- a/tools/scripts/tsc/generate-encoder.ts +++ b/tools/scripts/tsc/generate-encoder.ts @@ -2,7 +2,7 @@ * Encoder/decoder code generator: reads tools/scripts/tsc/ast.json and produces binary * encoding/decoding code for Go and TypeScript. * - * Usage: node --experimental-strip-types tools/scripts/tsc/generate-encoder.ts + * Usage: node tools/scripts/tsc/generate-encoder.ts * * Generates: * - internal/api/encoder/encoder_generated.go @@ -10,10 +10,10 @@ * - packages/typescript/src/api/node/protocol.generated.ts */ -import { execaSync } from "execa"; import * as fs from "node:fs"; import * as path from "node:path"; import { fileURLToPath } from "node:url"; +import { xSync } from "tinyexec"; import type { KindType, MemberInfo, @@ -1989,7 +1989,10 @@ function writeAndFormat(filePath: string, content: string, formatter: string) { fs.writeFileSync(filePath, content); try { const [cmd, ...args] = formatter.split(" "); - execaSync(cmd, [...args, filePath], { stdio: "inherit", cwd: ROOT }); + xSync(cmd, [...args, filePath], { + throwOnError: true, + nodeOptions: { stdio: "inherit", cwd: ROOT }, + }); } catch { console.warn(`Warning: formatter failed for ${filePath}`); diff --git a/tools/scripts/tsc/generate-go-ast.ts b/tools/scripts/tsc/generate-go-ast.ts index b64a685d90d67..e85658397729c 100644 --- a/tools/scripts/tsc/generate-go-ast.ts +++ b/tools/scripts/tsc/generate-go-ast.ts @@ -1,7 +1,7 @@ /** * Go AST code generator: reads tools/scripts/tsc/ast.json and produces internal/ast/ast_generated.go * - * Usage: node --experimental-strip-types tools/scripts/tsc/generate-go-ast.ts + * Usage: node tools/scripts/tsc/generate-go-ast.ts * * Generates: * - Struct definitions for each node kind @@ -14,10 +14,10 @@ * - Is*() type guard functions */ -import { execaSync } from "execa"; import * as fs from "node:fs"; import * as path from "node:path"; import { fileURLToPath } from "node:url"; +import { xSync } from "tinyexec"; import type { MemberInfo, NodeType, @@ -1084,7 +1084,10 @@ function generateKind(): string { function writeAndFormat(filePath: string, content: string) { fs.writeFileSync(filePath, content); - execaSync("dprint", ["fmt", filePath], { stdio: "inherit", cwd: ROOT }); + xSync("dprint", ["fmt", filePath], { + throwOnError: true, + nodeOptions: { stdio: "inherit", cwd: ROOT }, + }); console.log(`Wrote ${filePath}`); } diff --git a/tools/scripts/tsc/generate-ts-ast.ts b/tools/scripts/tsc/generate-ts-ast.ts index 753c7286ae094..054ac3576dd11 100644 --- a/tools/scripts/tsc/generate-ts-ast.ts +++ b/tools/scripts/tsc/generate-ts-ast.ts @@ -5,13 +5,13 @@ * - packages/typescript/src/ast/factory.generated.ts * - packages/typescript/src/ast/is.generated.ts * - * Usage: node --experimental-strip-types tools/scripts/tsc/generate-ts-ast.ts + * Usage: node tools/scripts/tsc/generate-ts-ast.ts */ -import { execaSync } from "execa"; import * as fs from "node:fs"; import * as path from "node:path"; import { fileURLToPath } from "node:url"; +import { xSync } from "tinyexec"; import type { MemberInfo, NodeType, @@ -1717,7 +1717,10 @@ function generateVisitor(): string { function writeAndFormat(filePath: string, content: string) { fs.writeFileSync(filePath, content); - execaSync("dprint", ["fmt", filePath], { stdio: "inherit", cwd: ROOT }); + xSync("dprint", ["fmt", filePath], { + throwOnError: true, + nodeOptions: { stdio: "inherit", cwd: ROOT }, + }); console.log(`Generated ${filePath}`); } diff --git a/tsc/internal/lsp/lsproto/_generate/fetchModel.mts b/tsc/internal/lsp/lsproto/_generate/fetchModel.mts index 21957cfe5a607..ee3a9111d1441 100755 --- a/tsc/internal/lsp/lsproto/_generate/fetchModel.mts +++ b/tsc/internal/lsp/lsproto/_generate/fetchModel.mts @@ -1,6 +1,6 @@ -#!/usr/bin/env -S node --experimental-strip-types +#!/usr/bin/env node -// Usage: node --experimental-strip-types fetchModel.mts +// Usage: node fetchModel.mts import fs from "node:fs"; import path from "node:path"; diff --git a/tsc/internal/lsp/lsproto/_generate/generate.mts b/tsc/internal/lsp/lsproto/_generate/generate.mts index b8466ff18d705..6f65a9af99c1e 100755 --- a/tsc/internal/lsp/lsproto/_generate/generate.mts +++ b/tsc/internal/lsp/lsproto/_generate/generate.mts @@ -1,11 +1,11 @@ -#!/usr/bin/env -S node --experimental-strip-types +#!/usr/bin/env node -// Usage: node --experimental-strip-types generate.mts +// Usage: node generate.mts -import { $ } from "execa"; import fs from "node:fs"; import path from "node:path"; import url from "node:url"; +import { x } from "tinyexec"; import type { Enumeration, MetaModel, @@ -3608,7 +3608,10 @@ async function main() { const generatedCode = generateCode(); fs.writeFileSync(out, generatedCode); - await $({ cwd: repoRoot })`dprint fmt ${out}`; + await x("dprint", ["fmt", out], { + throwOnError: true, + nodeOptions: { cwd: repoRoot, stdio: "inherit" }, + }); console.log(`Successfully generated ${out}`); } diff --git a/tsc/internal/stringutil/_scripts/generate-unicode-data.mts b/tsc/internal/stringutil/_scripts/generate-unicode-data.mts index bd726d68026d5..0353b2141aae1 100644 --- a/tsc/internal/stringutil/_scripts/generate-unicode-data.mts +++ b/tsc/internal/stringutil/_scripts/generate-unicode-data.mts @@ -1,4 +1,4 @@ -#!/usr/bin/env -S node --experimental-strip-types --no-warnings +#!/usr/bin/env node import * as fs from "fs"; import * as path from "path"; diff --git a/tsc/internal/stringutil/generate.go b/tsc/internal/stringutil/generate.go index 3c5ad7ad481fe..196e2d59e3904 100644 --- a/tsc/internal/stringutil/generate.go +++ b/tsc/internal/stringutil/generate.go @@ -1,4 +1,4 @@ package stringutil -//go:generate node --experimental-strip-types --no-warnings ./_scripts/generate-unicode-data.mts +//go:generate node ./_scripts/generate-unicode-data.mts //go:generate npx dprint fmt js_case_generated.go identifier_parts_generated.go