From 784def7a8441629ff26dd35ca6be7f43af130043 Mon Sep 17 00:00:00 2001 From: Noel Cothren Date: Mon, 2 Mar 2026 13:36:35 -0700 Subject: [PATCH 1/2] fix: bypass .prettierignore in apigen task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `.prettierignore` added in #3256 includes `src/clients/` to prevent the Claude Code formatting hook from touching auto-generated code. However, the `apigen` gulp task's internal Prettier step uses the same `prettier()` helper, which consults `.prettierignore` — causing it to skip formatting entirely for all generated client files. This meant running `gulp apigen` produced raw openapi-generator output (4-space indent, single quotes) instead of Prettier-formatted output (2-space indent, double quotes per .prettierrc), creating large cosmetic diffs across all clients even when only one spec changed. Add an `ignoreIgnoreFile` option to the `prettier()` helper so `apigen` can bypass `.prettierignore` while all other callers remain unaffected. Co-Authored-By: Claude Opus 4.6 --- Gulpfile.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Gulpfile.js b/Gulpfile.js index 4a74dd5cd..f511165b5 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -998,7 +998,9 @@ export async function apigen() { .map(([key, value]) => `${key}=${value}`) .join(","); - const format = await prettier(); + // bypass .prettierignore since it excludes src/clients/ (to prevent other tools from + // reformatting generated code), but apigen itself should always format its own output + const format = await prettier({ ignoreIgnoreFile: true }); for (const [spec, path] of clients) { // other client generator types: https://openapi-generator.tech/docs/generators#client-generators @@ -1053,7 +1055,7 @@ export async function format() { ); } -async function prettier() { +async function prettier({ ignoreIgnoreFile = false } = {}) { const { check, format, getFileInfo, resolveConfigFile, resolveConfig } = await import("prettier"); const configFile = (await resolveConfigFile()) ?? ".prettierrc"; const config = await resolveConfig(configFile); @@ -1061,10 +1063,12 @@ async function prettier() { return async function* process(source) { for await (const file of source) { if (file.contents != null) { - // check if the file is in .prettierignore before trying to format it - const fileInfo = await getFileInfo(file.path, { ignorePath: ".prettierignore" }); - if (fileInfo.ignored) { - continue; + if (!ignoreIgnoreFile) { + // check if the file is in .prettierignore before trying to format it + const fileInfo = await getFileInfo(file.path, { ignorePath: ".prettierignore" }); + if (fileInfo.ignored) { + continue; + } } const options = { filepath: file.path, ...config }; const code = file.contents.toString(); From 73a5446ca6a91337625441b9058d5eac3e88c771 Mon Sep 17 00:00:00 2001 From: Noel Cothren Date: Mon, 2 Mar 2026 14:26:34 -0700 Subject: [PATCH 2/2] refactor: simplify prettier integration to use CLI directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the custom Gulp streaming `prettier()` helper with direct `npx prettier` CLI calls via `spawnSync`. The CLI handles config resolution, .prettierignore, file discovery, and writing — eliminating the need for the manual async iterator, Gulp src/dest/pipeline wiring, and the `ignoreIgnoreFile` workaround from the previous commit. For `apigen`, uses `--ignore-path /dev/null` to bypass .prettierignore (which excludes src/clients/ to prevent other tools from reformatting generated code). Co-Authored-By: Claude Opus 4.6 --- Gulpfile.js | 67 ++++++++++++++--------------------------------------- 1 file changed, 18 insertions(+), 49 deletions(-) diff --git a/Gulpfile.js b/Gulpfile.js index f511165b5..9e6efaa36 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -10,7 +10,7 @@ import { runTests } from "@vscode/test-electron"; import { configDotenv } from "dotenv"; import { ESLint } from "eslint"; import { globSync } from "glob"; -import { dest, parallel, series, src } from "gulp"; +import { parallel, series } from "gulp"; import libCoverage from "istanbul-lib-coverage"; import libInstrument from "istanbul-lib-instrument"; import libReport from "istanbul-lib-report"; @@ -20,7 +20,6 @@ import { spawnSync } from "node:child_process"; import { existsSync, readFileSync } from "node:fs"; import { appendFile, readFile, unlink, writeFile } from "node:fs/promises"; import { basename, dirname, extname, join, resolve } from "node:path"; -import { pipeline } from "node:stream/promises"; import { rimrafSync } from "rimraf"; import { rollup, watch } from "rollup"; import copy from "rollup-plugin-copy"; @@ -998,10 +997,6 @@ export async function apigen() { .map(([key, value]) => `${key}=${value}`) .join(","); - // bypass .prettierignore since it excludes src/clients/ (to prevent other tools from - // reformatting generated code), but apigen itself should always format its own output - const format = await prettier({ ignoreIgnoreFile: true }); - for (const [spec, path] of clients) { // other client generator types: https://openapi-generator.tech/docs/generators#client-generators const result = spawnSync( @@ -1023,15 +1018,18 @@ export async function apigen() { shell: IS_WINDOWS, }, ); - - // apply prettier formatting to generated code - await pipeline( - src(join(path, "**", "*.ts")), - format, - dest((file) => file.base), - ); if (result.error) throw result.error; if (result.status !== 0) throw new Error(`Failed to generate client for ${spec}`); + + // apply prettier formatting to generated code, bypassing .prettierignore since it + // excludes src/clients/ (to prevent other tools from reformatting generated code) + const prettierResult = spawnSync( + "npx", + ["prettier", "--write", "--ignore-path", "/dev/null", join(path, "**", "*.ts")], + { stdio: "inherit", shell: IS_WINDOWS }, + ); + if (prettierResult.error) throw prettierResult.error; + if (prettierResult.status !== 0) throw new Error(`Failed to format generated code in ${path}`); } // While here, also run `npx gql-tada generate output` to generate GraphQL types. @@ -1044,43 +1042,14 @@ export async function apigen() { } format.description = "Enforce Prettier formatting for all TS/JS/MD/HTML/YAML files."; -export async function format() { - const transform = await prettier(); - // Prettier's API does not have a magic method to just fix everything - // So this is where we add some Gulp FileSystem API to make it work - return pipeline( - src(["src/**/*.{ts,css,html,json}", "*.md", "*.js"]), - transform, - dest((file) => file.base), +export function format() { + const result = spawnSync( + "npx", + ["prettier", "--write", "src/**/*.{ts,css,html,json}", "*.md", "*.js"], + { stdio: "inherit", shell: IS_WINDOWS }, ); -} - -async function prettier({ ignoreIgnoreFile = false } = {}) { - const { check, format, getFileInfo, resolveConfigFile, resolveConfig } = await import("prettier"); - const configFile = (await resolveConfigFile()) ?? ".prettierrc"; - const config = await resolveConfig(configFile); - /** @param {AsyncIterator} source */ - return async function* process(source) { - for await (const file of source) { - if (file.contents != null) { - if (!ignoreIgnoreFile) { - // check if the file is in .prettierignore before trying to format it - const fileInfo = await getFileInfo(file.path, { ignorePath: ".prettierignore" }); - if (fileInfo.ignored) { - continue; - } - } - const options = { filepath: file.path, ...config }; - const code = file.contents.toString(); - const valid = await check(code, options); - if (!valid) { - const contents = await format(code, options); - const clone = file.clone({ contents: false }); - yield Object.assign(clone, { contents: Buffer.from(contents) }); - } - } - } - }; + if (result.error) throw result.error; + if (result.status !== 0) throw new Error("Prettier formatting failed"); } icongen.description = "Generate font files from SVG icons, and update package.json accordingly.";