diff --git a/packages/fresh/src/dev/builder_test.ts b/packages/fresh/src/dev/builder_test.ts index be5f3e7ec0f..c854352746f 100644 --- a/packages/fresh/src/dev/builder_test.ts +++ b/packages/fresh/src/dev/builder_test.ts @@ -281,6 +281,42 @@ Deno.test({ sanitizeResources: false, }); +Deno.test({ + name: "Builder - .well-known static files", + fn: async () => { + await using _tmp = await withTmpDir(); + const tmp = _tmp.dir; + + const foo = path.join(tmp, ".well-known", "foo.json"); + await Deno.mkdir(path.dirname(foo), { recursive: true }); + await Deno.writeTextFile(foo, JSON.stringify({ ok: true })); + + const builder = new Builder({ + outDir: path.join(tmp, "dist"), + staticDir: tmp, + }); + const app = new App().use(staticFiles()); + const abort = new AbortController(); + const port = 8011; + await builder.listen(() => Promise.resolve(app), { + port, + signal: abort.signal, + }); + + const res = await fetch( + `http://localhost:${port}/.well-known/foo.json`, + ); + const json = await res.json(); + await abort.abort(); + + expect(res.ok).toBe(true); + expect(res.status).toBe(200); + expect(json).toEqual({ ok: true }); + }, + sanitizeOps: false, + sanitizeResources: false, +}); + Deno.test({ name: "Builder - write prod routePattern", fn: async () => { diff --git a/packages/fresh/src/dev/dev_build_cache.ts b/packages/fresh/src/dev/dev_build_cache.ts index fb32e50fe72..7378c5f1ce0 100644 --- a/packages/fresh/src/dev/dev_build_cache.ts +++ b/packages/fresh/src/dev/dev_build_cache.ts @@ -142,7 +142,7 @@ export class MemoryBuildCache implements DevBuildCache { for (let i = 0; i < transformed.length; i++) { const file = transformed[i]; const relative = path.relative(this.#config.staticDir, file.path); - if (relative.startsWith(".")) { + if (relative.startsWith("..")) { throw new Error( `Processed file resolved outside of static dir ${file.path}`, ); @@ -158,7 +158,7 @@ export class MemoryBuildCache implements DevBuildCache { try { const filePath = path.join(this.#config.staticDir, pathname); const relative = path.relative(this.#config.staticDir, filePath); - if (!relative.startsWith(".") && (await Deno.stat(filePath)).isFile) { + if (!relative.startsWith("..") && (await Deno.stat(filePath)).isFile) { const pathname = new URL(relative, "http://localhost").pathname; this.addUnprocessedFile(pathname, this.#config.staticDir); return this.readFile(pathname); @@ -301,8 +301,9 @@ export class DiskBuildCache implements DevBuildCache { includeDirs: false, includeFiles: true, followSymlinks: false, - // Skip any folder or file starting with a "." - skip: [/\/\.[^/]+(\/|$)/], + // Skip any folder or file starting with a ".", but allow + // ".well-known" for things like PWA manifests + skip: [/\/\.(?!well-known)[^/]+(\/|$)/], }); for await (const entry of entries) {