diff --git a/.changeset/fix-copytraced-package-json-suffix.md b/.changeset/fix-copytraced-package-json-suffix.md new file mode 100644 index 000000000..32b6f5fd9 --- /dev/null +++ b/.changeset/fix-copytraced-package-json-suffix.md @@ -0,0 +1,10 @@ +--- +"@opennextjs/aws": patch +--- + +`copyTracedFiles`: only register a traced file as a node package when its +basename is exactly `package.json`, not when its filename merely ends with +`package.json` (e.g. `care-package.json`). The previous suffix-only check +caused harmless but noisy `Failed to copy ` errors in the Cloudflare +adapter's workerd-package copy step whenever a project traced a JSON data +file whose name happened to end in `-package.json`. diff --git a/packages/open-next/src/build/copyTracedFiles.ts b/packages/open-next/src/build/copyTracedFiles.ts index 9f6689c0f..6becc9129 100644 --- a/packages/open-next/src/build/copyTracedFiles.ts +++ b/packages/open-next/src/build/copyTracedFiles.ts @@ -85,6 +85,16 @@ export function isNonLinuxPlatformPackage(srcPath: string): boolean { return nonLinuxPlatformRegex.test(srcPath); } +// `path.basename` only splits on the OS-native separator, so we explicitly +// look at both `/` and `\` to handle paths produced under either platform. +export function isPackageJson(modulePath: string): boolean { + const lastSep = Math.max( + modulePath.lastIndexOf("/"), + modulePath.lastIndexOf("\\"), + ); + return modulePath.slice(lastSep + 1) === "package.json"; +} + function copyPatchFile(outputDir: string) { const patchFile = path.join(__dirname, "patch", "patchedAsyncStorage.js"); const outputPatchFile = path.join(outputDir, "patchedAsyncStorage.cjs"); @@ -153,7 +163,7 @@ export async function copyTracedFiles({ filesToCopy.set(src, dst); const module = path.join(dotNextDir, subDir, tracedPath); - if (module.endsWith("package.json")) { + if (isPackageJson(module)) { nodePackages.set(path.dirname(module), path.dirname(dst)); } }); diff --git a/packages/tests-unit/tests/build/copyTracedFiles.test.ts b/packages/tests-unit/tests/build/copyTracedFiles.test.ts index 814c822b7..7644cdc88 100644 --- a/packages/tests-unit/tests/build/copyTracedFiles.test.ts +++ b/packages/tests-unit/tests/build/copyTracedFiles.test.ts @@ -1,6 +1,7 @@ import { isExcluded, isNonLinuxPlatformPackage, + isPackageJson, } from "@opennextjs/aws/build/copyTracedFiles.js"; describe("isExcluded", () => { @@ -129,3 +130,27 @@ describe("isNonLinuxPlatformPackage", () => { ).toBe(false); }); }); + +describe("isPackageJson", () => { + test("should detect package.json", () => { + expect(isPackageJson("/project/node_modules/next/package.json")).toBe(true); + expect(isPackageJson("package.json")).toBe(true); + }); + + test("should not treat files whose names end with 'package.json' as package.json", () => { + // Regression: traced JSON data files in user code can have names like + // `care-package.json` that share the "package.json" suffix without being + // real package manifests. The previous `endsWith("package.json")` check + // mis-classified them, causing `Failed to copy ` errors in the + // Cloudflare adapter's workerd-package copy step. + expect(isPackageJson("/project/data/tables/care-package.json")).toBe(false); + expect(isPackageJson("/project/fixtures/my-package.json")).toBe(false); + }); + + test("should handle Windows-style path separators", () => { + expect(isPackageJson("C:\\project\\node_modules\\next\\package.json")).toBe( + true, + ); + expect(isPackageJson("C:\\project\\data\\care-package.json")).toBe(false); + }); +});