diff --git a/dist/esbuild-plugin.js b/dist/esbuild-plugin.js index 7a69977..d325a92 100644 --- a/dist/esbuild-plugin.js +++ b/dist/esbuild-plugin.js @@ -4,6 +4,7 @@ import crypto from "node:crypto"; import { globSync } from "glob"; const URL_SEPARATOR = "/"; const assetsDirName = "assets"; +const ignoredFileNames = [".DS_Store"]; const fileHashRegexp = /(-[A-Z0-9]{8})(\.\S+)$/; const hanamiEsbuild = (options) => { return { @@ -104,6 +105,10 @@ const hanamiEsbuild = (options) => { const files = fs.readdirSync(assetDir, { recursive: true }); const assets = []; files.forEach((file) => { + // Skip ignored files + if (ignoredFileNames.includes(path.basename(file.toString()))) { + return; + } const sourcePath = path.join(assetDir, file.toString()); // Skip files loaded by esbuild; those are added to the manifest separately if (loadedFiles.has(sourcePath)) { diff --git a/src/esbuild-plugin.ts b/src/esbuild-plugin.ts index 0bdd42a..b1932b0 100644 --- a/src/esbuild-plugin.ts +++ b/src/esbuild-plugin.ts @@ -25,6 +25,7 @@ interface CopiedAsset { } const assetsDirName = "assets"; +const ignoredFileNames = [".DS_Store"]; const fileHashRegexp = /(-[A-Z0-9]{8})(\.\S+)$/; const hanamiEsbuild = (options: PluginOptions): Plugin => { @@ -146,6 +147,11 @@ const hanamiEsbuild = (options: PluginOptions): Plugin => { const assets: CopiedAsset[] = []; files.forEach((file) => { + // Skip ignored files + if (ignoredFileNames.includes(path.basename(file.toString()))) { + return; + } + const sourcePath = path.join(assetDir, file.toString()); // Skip files loaded by esbuild; those are added to the manifest separately diff --git a/test/hanami-assets.test.ts b/test/hanami-assets.test.ts index 0714926..ad7324c 100644 --- a/test/hanami-assets.test.ts +++ b/test/hanami-assets.test.ts @@ -195,6 +195,33 @@ describe("hanami-assets", () => { }); }); + test("ignores .DS_Store files", async () => { + const entryPoint = path.join(dest, "app/assets/js/app.js"); + await fs.writeFile(entryPoint, 'import "../css/app.css";'); + const cssFile = path.join(dest, "app/assets/css/app.css"); + await fs.writeFile(cssFile, ""); + + // Create .DS_Store files which are used by macOS to store meta data + await fs.writeFile(path.join(dest, "app/assets/.DS_Store"), "foobar"); + await fs.writeFile(path.join(dest, "app/assets/js/.DS_Store"), "foobar"); + await fs.writeFile(path.join(dest, "app/assets/css/.DS_Store"), "foobar"); + await fs.ensureDir(path.join(dest, "app/assets/images")); + await fs.writeFile(path.join(dest, "app/assets/images/.DS_Store"), "foobar"); + await fs.ensureDir(path.join(dest, "app/assets/images/subdir")); + await fs.writeFile(path.join(dest, "app/assets/images/subdir/.DS_Store"), "foobar"); + + await assets.run({ root: dest, argv: ["--path=app", "--dest=public/assets"] }); + + const ignoredFiles = globSync("**/.DS_Store", { cwd: path.join("public", "assets") }); + expect(ignoredFiles.length).toBe(0); + + const manifestContent = await fs.readFile( + path.join(dest, "public/assets/assets.json"), + "utf-8", + ); + expect(manifestContent).not.toMatch(/\.DS_Store/); + }); + test("generates SRI", async () => { const appEntryPoint = path.join(dest, "app/assets/js/app.js"); await fs.writeFile(appEntryPoint, "console.log('Hello, World!');");