zephyr-astro-integration 0.0.0-canary.7 → 0.0.0-canary.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,316 +0,0 @@
1
- import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
- import { extractAstroAssetsMap } from '../extract-astro-assets-map';
5
-
6
- const buildAssetsMapMock = jest.fn();
7
- const logFnMock = jest.fn();
8
-
9
- // Mock the zephyr-agent buildAssetsMap function
10
- jest.mock('zephyr-agent', () => ({
11
- buildAssetsMap: (...args: unknown[]) => buildAssetsMapMock(...args),
12
- logFn: (...args: unknown[]) => logFnMock(...args),
13
- }));
14
-
15
- describe('extractAstroAssetsMap', () => {
16
- let tempDir: string;
17
-
18
- beforeEach(async () => {
19
- tempDir = join(tmpdir(), `astro-test-${Date.now()}`);
20
- await mkdir(tempDir, { recursive: true });
21
- buildAssetsMapMock.mockImplementation((assets) => {
22
- const result: Record<string, unknown> = {};
23
- Object.keys(assets).forEach((key, index) => {
24
- const hash = `hash${index + 1}`;
25
- result[hash] = {
26
- hash,
27
- content: assets[key].content,
28
- type: assets[key].type,
29
- filepath: key,
30
- };
31
- });
32
- return result;
33
- });
34
- });
35
-
36
- afterEach(async () => {
37
- try {
38
- await rm(tempDir, { recursive: true, force: true });
39
- } catch {
40
- // Ignore cleanup errors
41
- }
42
- if (logFnMock && typeof logFnMock.mockRestore === 'function') {
43
- logFnMock.mockRestore();
44
- }
45
- jest.clearAllMocks();
46
- });
47
-
48
- describe('Basic Asset Extraction', () => {
49
- it('should extract assets from build directory', async () => {
50
- // Create test files
51
- await writeFile(join(tempDir, 'index.html'), '<html><body>Hello</body></html>');
52
- await writeFile(join(tempDir, 'style.css'), 'body { color: red; }');
53
- await writeFile(join(tempDir, 'script.js'), 'console.log("hello");');
54
-
55
- const assetsMap = await extractAstroAssetsMap(tempDir);
56
-
57
- expect(Object.keys(assetsMap)).toHaveLength(3);
58
- expect(buildAssetsMapMock).toHaveBeenCalledWith(
59
- expect.objectContaining({
60
- 'index.html': expect.objectContaining({
61
- content: expect.any(Buffer),
62
- type: 'text/html',
63
- }),
64
- 'style.css': expect.objectContaining({
65
- content: expect.any(Buffer),
66
- type: 'text/css',
67
- }),
68
- 'script.js': expect.objectContaining({
69
- content: expect.any(Buffer),
70
- type: 'application/javascript',
71
- }),
72
- }),
73
- expect.any(Function),
74
- expect.any(Function)
75
- );
76
- });
77
-
78
- it('should handle empty directory', async () => {
79
- const assetsMap = await extractAstroAssetsMap(tempDir);
80
- expect(Object.keys(assetsMap)).toHaveLength(0);
81
- expect(buildAssetsMapMock).toHaveBeenCalledWith(
82
- {},
83
- expect.any(Function),
84
- expect.any(Function)
85
- );
86
- });
87
- });
88
-
89
- describe('File Filtering', () => {
90
- it('should skip source maps', async () => {
91
- await writeFile(join(tempDir, 'index.html'), '<html></html>');
92
- await writeFile(join(tempDir, 'style.css.map'), '{"version":3}');
93
- await writeFile(join(tempDir, 'script.js.map'), '{"version":3}');
94
-
95
- const result = await extractAstroAssetsMap(tempDir);
96
-
97
- expect(result).toBeDefined();
98
- expect(buildAssetsMapMock).toHaveBeenCalledWith(
99
- expect.objectContaining({
100
- 'index.html': expect.any(Object),
101
- }),
102
- expect.any(Function),
103
- expect.any(Function)
104
- );
105
- expect(buildAssetsMapMock).not.toHaveBeenCalledWith(
106
- expect.objectContaining({
107
- 'style.css.map': expect.any(Object),
108
- 'script.js.map': expect.any(Object),
109
- }),
110
- expect.any(Function),
111
- expect.any(Function)
112
- );
113
- });
114
-
115
- it('should skip system files', async () => {
116
- await writeFile(join(tempDir, 'index.html'), '<html></html>');
117
- await writeFile(join(tempDir, '.DS_Store'), 'system data');
118
- await writeFile(join(tempDir, 'Thumbs.db'), 'windows thumbnail');
119
-
120
- const result = await extractAstroAssetsMap(tempDir);
121
-
122
- expect(result).toBeDefined();
123
- expect(buildAssetsMapMock).toHaveBeenCalledWith(
124
- expect.objectContaining({
125
- 'index.html': expect.any(Object),
126
- }),
127
- expect.any(Function),
128
- expect.any(Function)
129
- );
130
- });
131
-
132
- it('should skip node_modules and git directories', async () => {
133
- await mkdir(join(tempDir, 'node_modules'), { recursive: true });
134
- await mkdir(join(tempDir, '.git'), { recursive: true });
135
- await writeFile(join(tempDir, 'node_modules', 'package.json'), '{}');
136
- await writeFile(join(tempDir, '.git', 'config'), 'git config');
137
- await writeFile(join(tempDir, 'index.html'), '<html></html>');
138
-
139
- const result = await extractAstroAssetsMap(tempDir);
140
-
141
- expect(result).toBeDefined();
142
- expect(buildAssetsMapMock).toHaveBeenCalledWith(
143
- expect.objectContaining({
144
- 'index.html': expect.any(Object),
145
- }),
146
- expect.any(Function),
147
- expect.any(Function)
148
- );
149
- });
150
- });
151
-
152
- describe('File Type Detection', () => {
153
- it('should correctly identify file types', async () => {
154
- const testFiles = [
155
- ['index.html', '<html></html>', 'text/html'],
156
- ['style.css', 'body {}', 'text/css'],
157
- ['script.js', 'console.log()', 'application/javascript'],
158
- ['module.mjs', 'export default {}', 'application/javascript'],
159
- ['data.json', '{}', 'application/json'],
160
- ['image.png', Buffer.from('PNG data'), 'image/png'],
161
- ['image.jpg', Buffer.from('JPG data'), 'image/jpeg'],
162
- ['image.svg', '<svg></svg>', 'image/svg+xml'],
163
- ['favicon.ico', Buffer.from('ICO data'), 'image/x-icon'],
164
- ['font.woff', Buffer.from('WOFF data'), 'font/woff'],
165
- ['font.woff2', Buffer.from('WOFF2 data'), 'font/woff2'],
166
- ['unknown.xyz', 'unknown content', 'application/octet-stream'],
167
- ];
168
-
169
- for (const [filename, content] of testFiles) {
170
- await writeFile(join(tempDir, filename.toString()), content);
171
- }
172
-
173
- await extractAstroAssetsMap(tempDir);
174
-
175
- const buildAssetsMapCall = (buildAssetsMapMock as jest.Mock).mock.calls[0];
176
- const assets = buildAssetsMapCall[0];
177
-
178
- testFiles.forEach(([filename, , expectedType]) => {
179
- expect(assets[filename.toString()]).toHaveProperty('type', expectedType);
180
- });
181
- });
182
- });
183
-
184
- describe('Directory Traversal', () => {
185
- it('should handle nested directories', async () => {
186
- await mkdir(join(tempDir, 'assets', 'js'), { recursive: true });
187
- await mkdir(join(tempDir, 'assets', 'css'), { recursive: true });
188
- await mkdir(join(tempDir, 'images'), { recursive: true });
189
-
190
- await writeFile(join(tempDir, 'index.html'), '<html></html>');
191
- await writeFile(join(tempDir, 'assets', 'js', 'main.js'), 'console.log()');
192
- await writeFile(join(tempDir, 'assets', 'css', 'style.css'), 'body {}');
193
- await writeFile(join(tempDir, 'images', 'logo.png'), 'PNG data');
194
-
195
- const result = await extractAstroAssetsMap(tempDir);
196
-
197
- expect(result).toBeDefined();
198
- expect(buildAssetsMapMock).toHaveBeenCalledWith(
199
- expect.objectContaining({
200
- 'index.html': expect.any(Object),
201
- 'assets/js/main.js': expect.any(Object),
202
- 'assets/css/style.css': expect.any(Object),
203
- 'images/logo.png': expect.any(Object),
204
- }),
205
- expect.any(Function),
206
- expect.any(Function)
207
- );
208
- });
209
-
210
- it('should handle deeply nested directories', async () => {
211
- const deepPath = join(tempDir, 'a', 'b', 'c', 'd', 'e');
212
- await mkdir(deepPath, { recursive: true });
213
- await writeFile(join(deepPath, 'deep.txt'), 'deep file');
214
-
215
- const result = await extractAstroAssetsMap(tempDir);
216
-
217
- expect(result).toBeDefined();
218
- expect(buildAssetsMapMock).toHaveBeenCalledWith(
219
- expect.objectContaining({
220
- 'a/b/c/d/e/deep.txt': expect.objectContaining({
221
- type: 'text/plain',
222
- }),
223
- }),
224
- expect.any(Function),
225
- expect.any(Function)
226
- );
227
- });
228
- });
229
-
230
- describe('Error Handling', () => {
231
- it('should handle file read errors gracefully', async () => {
232
- await writeFile(join(tempDir, 'good.txt'), 'readable');
233
- await writeFile(join(tempDir, 'index.html'), '<html></html>');
234
-
235
- // Mock readFile to fail for specific file
236
- const originalReadFile = readFile;
237
- jest
238
- .spyOn(require('node:fs/promises'), 'readFile')
239
- .mockImplementation(async (path: any) => {
240
- if (path.toString().includes('good.txt')) {
241
- throw new Error('Permission denied');
242
- }
243
- return originalReadFile(path);
244
- });
245
-
246
- const result = await extractAstroAssetsMap(tempDir);
247
-
248
- expect(result).toBeDefined();
249
-
250
- expect(logFnMock).toHaveBeenCalledWith(
251
- 'warn',
252
- expect.stringMatching(/Failed to read file.*good\.txt/)
253
- );
254
-
255
- // Should still process the readable file
256
- expect(buildAssetsMapMock).toHaveBeenCalledWith(
257
- expect.objectContaining({
258
- 'index.html': expect.any(Object),
259
- }),
260
- expect.any(Function),
261
- expect.any(Function)
262
- );
263
-
264
- jest.restoreAllMocks();
265
- });
266
-
267
- it('should handle directory read errors gracefully', async () => {
268
- await writeFile(join(tempDir, 'index.html'), '<html></html>');
269
- await mkdir(join(tempDir, 'subdir'), { recursive: true });
270
-
271
- // Mock readdir to fail for subdirectory
272
- const originalReaddir = require('node:fs/promises').readdir;
273
- jest
274
- .spyOn(require('node:fs/promises'), 'readdir')
275
- .mockImplementation(async (path: any, options) => {
276
- if (path.toString().includes('subdir')) {
277
- throw new Error('Access denied');
278
- }
279
- return originalReaddir(path, options);
280
- });
281
-
282
- const result = await extractAstroAssetsMap(tempDir);
283
-
284
- expect(result).toBeDefined();
285
-
286
- expect(logFnMock).toHaveBeenCalledWith(
287
- 'warn',
288
- expect.stringMatching(/Failed to walk directory.*subdir/)
289
- );
290
-
291
- jest.restoreAllMocks();
292
- });
293
- });
294
-
295
- describe('Utility Functions', () => {
296
- it('should test extractBuffer function', async () => {
297
- await writeFile(join(tempDir, 'test.txt'), 'test content');
298
- await extractAstroAssetsMap(tempDir);
299
-
300
- const extractBuffer = (buildAssetsMapMock as jest.Mock).mock.calls[0][1];
301
- const testAsset = { content: Buffer.from('test'), type: 'text/plain' };
302
-
303
- expect(extractBuffer(testAsset)).toBe(testAsset.content);
304
- });
305
-
306
- it('should test getAssetType function', async () => {
307
- await writeFile(join(tempDir, 'test.txt'), 'test content');
308
- await extractAstroAssetsMap(tempDir);
309
-
310
- const getAssetType = (buildAssetsMapMock as jest.Mock).mock.calls[0][2];
311
- const testAsset = { content: Buffer.from('test'), type: 'text/plain' };
312
-
313
- expect(getAssetType(testAsset)).toBe('text/plain');
314
- });
315
- });
316
- });
@@ -1,108 +0,0 @@
1
- // Separate test file for detailed file type detection testing
2
- // This tests the getFileType logic without requiring file system operations
3
-
4
- // We need to extract the getFileType function for direct testing
5
- // Let's test it by examining the behavior through extractAstroAssetsMap
6
-
7
- import { extractAstroAssetsMap } from '../extract-astro-assets-map';
8
- import { buildAssetsMap } from 'zephyr-agent';
9
- import { mkdir, writeFile, rm } from 'node:fs/promises';
10
- import { join } from 'node:path';
11
- import { tmpdir } from 'node:os';
12
-
13
- jest.mock('zephyr-agent', () => ({
14
- buildAssetsMap: jest.fn(),
15
- }));
16
-
17
- describe('File Type Detection', () => {
18
- let tempDir: string;
19
-
20
- beforeEach(async () => {
21
- tempDir = join(tmpdir(), `file-type-test-${Date.now()}`);
22
- await mkdir(tempDir, { recursive: true });
23
-
24
- (buildAssetsMap as jest.Mock).mockImplementation((assets) => {
25
- return Object.fromEntries(
26
- Object.entries(assets).map(([key, value], index) => [
27
- `hash${index}`,
28
- { filepath: key, ...(value as Record<string, unknown>) },
29
- ])
30
- );
31
- });
32
- });
33
-
34
- afterEach(async () => {
35
- try {
36
- await rm(tempDir, { recursive: true, force: true });
37
- } catch {
38
- // Ignore cleanup errors
39
- }
40
- jest.clearAllMocks();
41
- });
42
-
43
- const testCases = [
44
- // Web files
45
- ['index.html', 'text/html'],
46
- ['INDEX.HTML', 'text/html'], // Case insensitive
47
- ['style.css', 'text/css'],
48
- ['script.js', 'application/javascript'],
49
- ['module.mjs', 'application/javascript'],
50
-
51
- // Data files
52
- ['data.json', 'application/json'],
53
- ['config.xml', 'text/xml'],
54
- ['readme.txt', 'text/plain'],
55
-
56
- // Images
57
- ['logo.png', 'image/png'],
58
- ['photo.jpg', 'image/jpeg'],
59
- ['photo.jpeg', 'image/jpeg'],
60
- ['animation.gif', 'image/gif'],
61
- ['icon.svg', 'image/svg+xml'],
62
- ['favicon.ico', 'image/x-icon'],
63
-
64
- // Fonts
65
- ['font.woff', 'font/woff'],
66
- ['font.woff2', 'font/woff2'],
67
- ['font.ttf', 'font/ttf'],
68
- ['font.eot', 'application/vnd.ms-fontobject'],
69
-
70
- // Unknown/fallback
71
- ['unknown.xyz', 'application/octet-stream'],
72
- ['no-extension', 'application/octet-stream'],
73
- ['file.', 'application/octet-stream'],
74
- ];
75
-
76
- test.each(testCases)('should detect %s as %s', async (filename, expectedType) => {
77
- await writeFile(join(tempDir, filename), 'test content');
78
-
79
- await extractAstroAssetsMap(tempDir);
80
-
81
- const buildAssetsMapCall = (buildAssetsMap as jest.Mock).mock.calls[0];
82
- const assets = buildAssetsMapCall[0];
83
-
84
- expect(assets[filename]).toHaveProperty('type', expectedType);
85
- });
86
-
87
- it('should handle files with multiple extensions correctly', async () => {
88
- const testFiles = [
89
- ['script.min.js', 'application/javascript'],
90
- ['style.min.css', 'text/css'],
91
- ['backup.tar.gz', 'application/octet-stream'], // Should use last extension
92
- ['data.test.json', 'application/json'],
93
- ];
94
-
95
- for (const [filename] of testFiles) {
96
- await writeFile(join(tempDir, filename), 'content');
97
- }
98
-
99
- await extractAstroAssetsMap(tempDir);
100
-
101
- const buildAssetsMapCall = (buildAssetsMap as jest.Mock).mock.calls[0];
102
- const assets = buildAssetsMapCall[0];
103
-
104
- testFiles.forEach(([filename, expectedType]) => {
105
- expect(assets[filename]).toHaveProperty('type', expectedType);
106
- });
107
- });
108
- });
@@ -1,275 +0,0 @@
1
- import { readdir, readFile } from 'node:fs/promises';
2
- import { join, relative, sep, isAbsolute } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { buildAssetsMap, logFn, type ZeBuildAssetsMap } from 'zephyr-agent';
5
-
6
- interface AstroAsset {
7
- content: Buffer | string;
8
- type: string;
9
- }
10
-
11
- function extractBuffer(asset: AstroAsset): Buffer | string | undefined {
12
- return asset.content;
13
- }
14
-
15
- function getAssetType(asset: AstroAsset): string {
16
- return asset.type;
17
- }
18
-
19
- /** Normalize path separators to forward slashes for cross-platform consistency */
20
- function normalizePath(filePath: string): string {
21
- return filePath.split(sep).join('/');
22
- }
23
-
24
- /** Check if a path looks like a URL route rather than a filesystem path */
25
- function looksLikeRoute(path: string): boolean {
26
- if (!path || !path.startsWith('/')) {
27
- return false;
28
- }
29
-
30
- // Dynamic routes have brackets
31
- if (path.includes('[') || path.includes(']')) {
32
- return true;
33
- }
34
-
35
- // Check if it's a real absolute filesystem path vs a route
36
- // Real absolute paths are typically long and contain multiple segments
37
- const segments = path.split('/').filter(Boolean);
38
-
39
- // Paths with just 1-2 segments like /about, /blog, /rss.xml are likely routes
40
- // Real filesystem paths like /Users/name/project/file.js have many segments
41
- if (segments.length <= 2) {
42
- return true;
43
- }
44
-
45
- return false;
46
- }
47
-
48
- type AstroAssets =
49
- | Record<string, unknown>
50
- | Map<string, unknown>
51
- | Array<unknown>
52
- | undefined
53
- | null;
54
-
55
- /**
56
- * Extract assets map from Astro's build hook assets parameter. This is more efficient
57
- * than walking the filesystem manually.
58
- */
59
- export async function extractAstroAssetsFromBuildHook(
60
- assets: AstroAssets,
61
- outputPath: string
62
- ): Promise<ZeBuildAssetsMap> {
63
- const astroAssets: Record<string, AstroAsset> = {};
64
-
65
- try {
66
- // Handle different possible structures of the assets parameter
67
- if (!assets) {
68
- // Fallback to filesystem walking if assets is not available
69
- return await extractAstroAssetsMap(outputPath);
70
- }
71
-
72
- // Assets might be an object, Map, or array depending on Astro version
73
- const assetEntries = extractAssetEntries(assets);
74
-
75
- for (const [filePath, assetInfo] of assetEntries) {
76
- try {
77
- let fullPath: string | null = null;
78
-
79
- // Handle URL objects or string paths
80
- if (assetInfo && typeof assetInfo === 'object' && 'href' in assetInfo) {
81
- // It's a URL object
82
- fullPath = fileURLToPath(assetInfo as URL);
83
- } else if (typeof assetInfo === 'string' && assetInfo) {
84
- // It's a string path - skip if it looks like a route
85
- if (looksLikeRoute(assetInfo)) {
86
- continue;
87
- }
88
-
89
- // Only treat as absolute if it's really an absolute filesystem path
90
- // (not just a route starting with /)
91
- fullPath =
92
- isAbsolute(assetInfo) && !looksLikeRoute(assetInfo)
93
- ? assetInfo // Absolute file system path
94
- : join(outputPath, assetInfo); // Relative path or route
95
- } else if (typeof filePath === 'string' && filePath) {
96
- // Use the key as the file path only if it looks like a file path
97
- if (looksLikeRoute(filePath)) {
98
- continue;
99
- }
100
-
101
- // Only treat as absolute if it's really an absolute filesystem path
102
- fullPath =
103
- isAbsolute(filePath) && !looksLikeRoute(filePath)
104
- ? filePath // Absolute file system path
105
- : join(outputPath, filePath); // Relative path or route
106
- }
107
-
108
- // Skip if we couldn't determine a valid file path
109
- if (!fullPath) {
110
- continue;
111
- }
112
-
113
- // Skip files we don't want to upload
114
- const relativePath = normalizePath(relative(outputPath, fullPath));
115
- if (shouldSkipFile(relativePath)) {
116
- continue;
117
- }
118
-
119
- // Read the file content
120
- const content = await readFile(fullPath);
121
- const fileType = getFileType(relativePath);
122
-
123
- astroAssets[relativePath] = {
124
- content,
125
- type: fileType,
126
- };
127
- } catch (readError) {
128
- logFn('warn', `Failed to read asset file ${filePath}: ${readError}`);
129
- continue;
130
- }
131
- }
132
-
133
- // If we didn't find any assets from the hook, fallback to filesystem walking
134
- if (Object.keys(astroAssets).length === 0) {
135
- return await extractAstroAssetsMap(outputPath);
136
- }
137
-
138
- return buildAssetsMap(astroAssets, extractBuffer, getAssetType);
139
- } catch (error) {
140
- logFn(
141
- 'warn',
142
- 'Error processing assets from Astro build hook:' + JSON.stringify(error, null, 2)
143
- );
144
- // Fallback to filesystem walking on any error
145
- return await extractAstroAssetsMap(outputPath);
146
- }
147
- }
148
-
149
- /**
150
- * Extract asset entries from the Astro assets parameter. Handles different possible data
151
- * structures.
152
- */
153
- function extractAssetEntries(assets: AstroAssets): [string, unknown][] {
154
- const entries: [string, unknown][] = [];
155
-
156
- if (Array.isArray(assets)) {
157
- // Handle array of assets
158
- assets.forEach((asset) => {
159
- if (typeof asset === 'string') {
160
- entries.push([asset, asset]);
161
- } else if (asset && typeof asset === 'object') {
162
- // Could be an object with path/url properties
163
- const assetObj = asset as Record<string, unknown>;
164
- const path =
165
- assetObj['path'] || assetObj['url'] || assetObj['href'] || assetObj['pathname'];
166
- if (path && typeof path === 'string') {
167
- entries.push([path, asset]);
168
- }
169
- }
170
- });
171
- } else if (assets instanceof Map) {
172
- // Handle Map objects
173
- for (const [key, value] of assets.entries()) {
174
- entries.push([key, value]);
175
- }
176
- } else if (assets && typeof assets === 'object') {
177
- // Handle plain objects
178
- for (const [key, value] of Object.entries(assets)) {
179
- if (Array.isArray(value)) {
180
- // If value is an array, it might contain multiple assets for this route
181
- value.forEach((item) => {
182
- entries.push([key, item]);
183
- });
184
- } else {
185
- entries.push([key, value]);
186
- }
187
- }
188
- }
189
-
190
- return entries;
191
- }
192
-
193
- export async function extractAstroAssetsMap(buildDir: string): Promise<ZeBuildAssetsMap> {
194
- const assets: Record<string, AstroAsset> = {};
195
-
196
- // Recursively walk through the build directory
197
- async function walkDir(dirPath: string): Promise<void> {
198
- try {
199
- const entries = await readdir(dirPath, { withFileTypes: true });
200
-
201
- for (const entry of entries) {
202
- const fullPath = join(dirPath, entry.name);
203
-
204
- if (entry.isDirectory()) {
205
- await walkDir(fullPath);
206
- } else if (entry.isFile()) {
207
- // Get relative path from build directory
208
- const relativePath = normalizePath(relative(buildDir, fullPath));
209
-
210
- // Skip certain files that shouldn't be uploaded
211
- if (shouldSkipFile(relativePath)) {
212
- continue;
213
- }
214
-
215
- try {
216
- const content = await readFile(fullPath);
217
- const fileType = getFileType(relativePath);
218
-
219
- assets[relativePath] = {
220
- content,
221
- type: fileType,
222
- };
223
- } catch (readError) {
224
- logFn('warn', `Failed to read file ${fullPath}: ${readError}`);
225
- }
226
- }
227
- }
228
- } catch (error) {
229
- logFn('warn', `Failed to walk directory ${dirPath}: ${error}`);
230
- }
231
- }
232
-
233
- await walkDir(buildDir);
234
-
235
- return buildAssetsMap(assets, extractBuffer, getAssetType);
236
- }
237
-
238
- function shouldSkipFile(filePath: string): boolean {
239
- // Skip common files that shouldn't be uploaded
240
- const skipPatterns = [
241
- /\.map$/, // Source maps
242
- /node_modules/, // Node modules
243
- /\.git/, // Git files
244
- /\.DS_Store$/, // macOS files
245
- /thumbs\.db$/i, // Windows files
246
- ];
247
-
248
- return skipPatterns.some((pattern) => pattern.test(filePath));
249
- }
250
-
251
- function getFileType(filePath: string): string {
252
- const extension = filePath.split('.').pop()?.toLowerCase() || '';
253
-
254
- const typeMap: Record<string, string> = {
255
- html: 'text/html',
256
- css: 'text/css',
257
- js: 'application/javascript',
258
- mjs: 'application/javascript',
259
- json: 'application/json',
260
- png: 'image/png',
261
- jpg: 'image/jpeg',
262
- jpeg: 'image/jpeg',
263
- gif: 'image/gif',
264
- svg: 'image/svg+xml',
265
- ico: 'image/x-icon',
266
- woff: 'font/woff',
267
- woff2: 'font/woff2',
268
- ttf: 'font/ttf',
269
- eot: 'application/vnd.ms-fontobject',
270
- xml: 'text/xml',
271
- txt: 'text/plain',
272
- };
273
-
274
- return typeMap[extension] || 'application/octet-stream';
275
- }