feat: auto-strip UTF-8 BOM when reading local files in CLI (#8911)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-04-22 08:50:47 -07:00
committed by GitHub
parent dc896737ac
commit 99bc96d0b2
22 changed files with 204 additions and 87 deletions
+9 -9
View File
@@ -2,7 +2,7 @@ import { GlobalOptions } from "../../types.ts";
import { requireLogin } from "../../core/auth.ts";
import { resolveWorkspace, validatePath } from "../../core/context.ts";
import type { PermissionedAsContext } from "../../core/permissioned_as.ts";
import { readFile, writeFile, stat, mkdir } from "node:fs/promises";
import { writeFile, stat, mkdir } from "node:fs/promises";
import { Buffer } from "node:buffer";
import { colors } from "@cliffy/ansi/colors";
import { Command } from "@cliffy/command";
@@ -12,7 +12,7 @@ import * as log from "../../core/log.ts";
import { sep as SEP } from "node:path";
import * as path from "node:path";
import { stringify as yamlStringify } from "yaml";
import { deepEqual } from "../../utils/utils.ts";
import { deepEqual, readTextFile, readTextFileSync } from "../../utils/utils.ts";
import * as wmill from "../../../gen/services.gen.ts";
import * as specificItems from "../../core/specific_items.ts";
import { getCurrentGitBranch } from "../../utils/git.ts";
@@ -114,7 +114,7 @@ export async function computePushMetadataHash(
): Promise<string> {
const remotePath = removeExtensionToPath(filePath).replaceAll(SEP, "/");
const metadataWithType = await parseMetadataFile(remotePath, undefined);
const metadataContent = await readFile(metadataWithType.path, "utf-8");
const metadataContent = await readTextFile(metadataWithType.path);
return await generateScriptHash({}, content, metadataContent);
}
@@ -141,7 +141,7 @@ async function push(opts: PushOptions, filePath: string) {
// Warn about metadata state before pushing
try {
const content = await readFile(filePath, "utf-8");
const content = await readTextFile(filePath);
const remotePath = removeExtensionToPath(filePath).replaceAll(SEP, "/");
const contentHash = await computePushMetadataHash(filePath, content);
const conf = await readLockfile();
@@ -432,7 +432,7 @@ export async function handleFile(
} catch {
log.debug(`Script ${remotePath} does not exist on remote`);
}
const content = await readFile(path, "utf-8");
const content = await readTextFile(path);
if (opts?.skipScriptsMetadata) {
// if (codebase) {
@@ -619,7 +619,7 @@ export async function readModulesFromDisk(
} else if (entry.isFile() && !entry.name.endsWith(".lock") && !isEntryPointFile(entry.name, isTopLevel)) {
// Skip lock files — they're handled as the `lock` field on ScriptModule
if (exts.some((ext) => entry.name.endsWith(ext))) {
const content = fs.readFileSync(fullPath, "utf-8");
const content = readTextFileSync(fullPath);
const language = inferContentTypeFromFilePath(entry.name, defaultTs);
// Check for an accompanying lock file (helper.lock)
@@ -627,7 +627,7 @@ export async function readModulesFromDisk(
const lockPath = path.join(dirPath, baseName + ".lock");
let lock: string | undefined;
if (fs.existsSync(lockPath)) {
lock = fs.readFileSync(lockPath, "utf-8");
lock = readTextFileSync(lockPath);
}
modules[relPath] = {
@@ -958,7 +958,7 @@ export async function resolve(input: string): Promise<Record<string, any>> {
input = new TextDecoder().decode(Buffer.concat(chunks));
}
if (input[0] == "@") {
input = await readFile(input.substring(1), "utf-8");
input = await readTextFile(input.substring(1));
}
try {
return JSON.parse(input);
@@ -1404,7 +1404,7 @@ async function preview(
const codebases = await listSyncCodebases(opts);
const language = inferContentTypeFromFilePath(filePath, opts?.defaultTs);
const content = await readFile(filePath, "utf-8");
const content = await readTextFile(filePath);
const input = opts.data ? await resolve(opts.data) : {};
// Read modules from __mod/ folder if present