Skip to content

Commit c96f71d

Browse files
committed
feat: add asset filename filtering to improve asset collection
- Introduced a new utility function to ignore specific asset filenames that are not real app assets, such as system files (.DS_Store, Thumbs.db). - Updated asset collection logic in both appCollector and pullAssets to filter out ignored filenames. - Added tests to ensure that ignored files are correctly excluded from the asset collection process.
1 parent a26b7e6 commit c96f71d

4 files changed

Lines changed: 38 additions & 1 deletion

File tree

src/core/appCollector.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import path from 'path';
88

99
import type { ArtifactProp } from './artifacts.js';
1010
import { ARTIFACT_CONFIGS } from './artifacts.js';
11+
import { isIgnoredAssetFileName } from './assetIgnore.js';
1112
import { processWithConcurrency } from './concurrency.js';
1213

1314
export interface ParsedAppFiles {
@@ -45,6 +46,7 @@ async function collectAssetBasenames(rootDir: string): Promise<string[]> {
4546
return entries
4647
.filter((e) => e.isFile())
4748
.map((e) => e.name)
49+
.filter((name) => !isIgnoredAssetFileName(name))
4850
.sort();
4951
} catch {
5052
return [];

src/core/assetIgnore.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Asset filenames that should never be treated as real app assets.
3+
* These are commonly created by OS tooling and can accidentally appear in `assets/`.
4+
*/
5+
export function isIgnoredAssetFileName(fileName: string): boolean {
6+
const name = fileName.trim();
7+
if (name === '') return true;
8+
9+
// macOS Finder metadata
10+
if (name === '.DS_Store') return true;
11+
12+
// AppleDouble sidecar files (e.g. when copying to non-HFS filesystems)
13+
if (name.startsWith('._')) return true;
14+
15+
// Windows metadata
16+
if (name === 'Thumbs.db') return true;
17+
if (name === 'desktop.ini') return true;
18+
19+
return false;
20+
}

src/core/pullAssets.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import path from 'path';
33

44
import type { AssetDTO } from './dto.js';
55
import type { upsertEnvConfig } from './envConfig.js';
6+
import { isIgnoredAssetFileName } from './assetIgnore.js';
67

78
export class AssetPullError extends Error {
89
constructor(
@@ -210,7 +211,10 @@ export async function applyCloudAssetsToFs(params: {
210211
let localFiles: string[] = [];
211212
try {
212213
const entries = await fs.readdir(assetsDir, { withFileTypes: true });
213-
localFiles = entries.filter((e) => e.isFile()).map((e) => e.name);
214+
localFiles = entries
215+
.filter((e) => e.isFile())
216+
.map((e) => e.name)
217+
.filter((name) => !isIgnoredAssetFileName(name));
214218
} catch {
215219
localFiles = [];
216220
}

tests/core/appCollector.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ describe('collectAppFiles', () => {
2424
expect(result.assetFiles).toEqual(['logo.png']);
2525
});
2626

27+
it('excludes .DS_Store and AppleDouble files from assets', async () => {
28+
await fs.mkdir(path.join(tmpDir, 'assets'), { recursive: true });
29+
await fs.writeFile(path.join(tmpDir, 'assets', '.DS_Store'), Buffer.from([1]));
30+
await fs.writeFile(path.join(tmpDir, 'assets', '._logo.png'), Buffer.from([2]));
31+
await fs.writeFile(path.join(tmpDir, 'assets', 'logo.png'), Buffer.from([3]));
32+
33+
const result = await collectAppFiles(tmpDir);
34+
35+
expect(result.assetFiles).toEqual(['logo.png']);
36+
});
37+
2738
it('collects screens, widgets, scripts', async () => {
2839
await fs.mkdir(path.join(tmpDir, 'screens'), { recursive: true });
2940
await fs.mkdir(path.join(tmpDir, 'widgets'), { recursive: true });

0 commit comments

Comments
 (0)