zephyr-astro-integration 0.0.0-canary.7 → 0.0.0-canary.9
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.
- package/package.json +2 -2
- package/.eslintrc.json +0 -20
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/jest.config.ts +0 -11
- package/project.json +0 -37
- package/src/__test__/integration.spec.ts +0 -19
- package/src/index.ts +0 -2
- package/src/lib/__test__/astro-integration-zephyr.spec.ts +0 -242
- package/src/lib/astro-integration-zephyr.ts +0 -63
- package/src/lib/internal/__test__/extract-astro-assets-from-build-hook.spec.ts +0 -269
- package/src/lib/internal/__test__/extract-astro-assets-map.spec.ts +0 -316
- package/src/lib/internal/__test__/file-type-detection.spec.ts +0 -108
- package/src/lib/internal/extract-astro-assets-map.ts +0 -275
- package/tsconfig.json +0 -23
- package/tsconfig.lib.json +0 -11
- package/tsconfig.spec.json +0 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zephyr-astro-integration",
|
|
3
|
-
"version": "0.0.0-canary.
|
|
3
|
+
"version": "0.0.0-canary.9",
|
|
4
4
|
"description": "Astro integration for Zephyr",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"types": "dist/index.d.ts",
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"is-ci": "^4.1.0",
|
|
20
|
-
"zephyr-agent": "0.0.0-canary.
|
|
20
|
+
"zephyr-agent": "0.0.0-canary.9"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/jest": "29.5.14",
|
package/.eslintrc.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": ["../../.eslintrc.json"],
|
|
3
|
-
"ignorePatterns": ["!**/*"],
|
|
4
|
-
"overrides": [
|
|
5
|
-
{
|
|
6
|
-
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
7
|
-
"rules": {
|
|
8
|
-
"no-empty": "off"
|
|
9
|
-
}
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
"files": ["*.ts", "*.tsx"],
|
|
13
|
-
"rules": {}
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"files": ["*.js", "*.jsx"],
|
|
17
|
-
"rules": {}
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":"5.8.3"}
|
package/jest.config.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
export default {
|
|
3
|
-
displayName: 'zephyr-astro-integration',
|
|
4
|
-
preset: '../../jest.preset.js',
|
|
5
|
-
testEnvironment: 'node',
|
|
6
|
-
transform: {
|
|
7
|
-
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
|
8
|
-
},
|
|
9
|
-
moduleFileExtensions: ['ts', 'js', 'html'],
|
|
10
|
-
coverageDirectory: '../../coverage/libs/zephyr-astro-integration',
|
|
11
|
-
};
|
package/project.json
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "zephyr-astro-integration",
|
|
3
|
-
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
-
"sourceRoot": "libs/zephyr-astro-integration/src",
|
|
5
|
-
"projectType": "library",
|
|
6
|
-
"tags": [],
|
|
7
|
-
"targets": {
|
|
8
|
-
"build": {
|
|
9
|
-
"executor": "@nx/js:tsc",
|
|
10
|
-
"outputs": ["{options.outputPath}"],
|
|
11
|
-
"options": {
|
|
12
|
-
"rootDir": "libs/zephyr-astro-integration/src",
|
|
13
|
-
"outputPath": "libs/zephyr-astro-integration/dist",
|
|
14
|
-
"tsConfig": "libs/zephyr-astro-integration/tsconfig.lib.json",
|
|
15
|
-
"main": "libs/zephyr-astro-integration/src/index.ts"
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
"nx-release-publish": {
|
|
19
|
-
"options": {
|
|
20
|
-
"packageRoot": "dist\\{projectRoot}"
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
"release": {
|
|
24
|
-
"command": "pnpm dist-tag add zephyr-astro-integration@$(npm view zephyr-astro-integration@next version) latest"
|
|
25
|
-
},
|
|
26
|
-
"lint": {
|
|
27
|
-
"executor": "@nx/eslint:lint"
|
|
28
|
-
},
|
|
29
|
-
"test": {
|
|
30
|
-
"executor": "@nx/jest:jest",
|
|
31
|
-
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
|
32
|
-
"options": {
|
|
33
|
-
"jestConfig": "libs/zephyr-astro-integration/jest.config.ts"
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { withZephyr } from '../index';
|
|
2
|
-
|
|
3
|
-
describe('Integration Export', () => {
|
|
4
|
-
it('should export withZephyr function', () => {
|
|
5
|
-
expect(typeof withZephyr).toBe('function');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
it('should create a valid Astro integration', () => {
|
|
9
|
-
const integration = withZephyr();
|
|
10
|
-
|
|
11
|
-
expect(integration).toMatchObject({
|
|
12
|
-
name: 'with-zephyr',
|
|
13
|
-
hooks: expect.objectContaining({
|
|
14
|
-
'astro:config:done': expect.any(Function),
|
|
15
|
-
'astro:build:done': expect.any(Function),
|
|
16
|
-
}),
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath } from 'node:url';
|
|
2
|
-
import { logFn, zeBuildDashData, ZephyrEngine, ZephyrError } from 'zephyr-agent';
|
|
3
|
-
import { withZephyr } from '../astro-integration-zephyr';
|
|
4
|
-
import {
|
|
5
|
-
extractAstroAssetsFromBuildHook,
|
|
6
|
-
extractAstroAssetsMap,
|
|
7
|
-
} from '../internal/extract-astro-assets-map';
|
|
8
|
-
|
|
9
|
-
// Mock dependencies
|
|
10
|
-
jest.mock('zephyr-agent', () => ({
|
|
11
|
-
ZephyrEngine: {
|
|
12
|
-
defer_create: jest.fn(),
|
|
13
|
-
},
|
|
14
|
-
logFn: jest.fn(),
|
|
15
|
-
ZephyrError: {
|
|
16
|
-
format: jest.fn(),
|
|
17
|
-
},
|
|
18
|
-
zeBuildDashData: jest.fn(),
|
|
19
|
-
}));
|
|
20
|
-
|
|
21
|
-
jest.mock('node:url', () => ({
|
|
22
|
-
fileURLToPath: jest.fn(),
|
|
23
|
-
}));
|
|
24
|
-
|
|
25
|
-
jest.mock('../internal/extract-astro-assets-map', () => ({
|
|
26
|
-
extractAstroAssetsFromBuildHook: jest.fn(),
|
|
27
|
-
extractAstroAssetsMap: jest.fn(),
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
interface MockZephyrEngine {
|
|
31
|
-
buildProperties: { output: string };
|
|
32
|
-
start_new_build: jest.Mock;
|
|
33
|
-
upload_assets: jest.Mock;
|
|
34
|
-
build_finished: jest.Mock;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
describe('withZephyr', () => {
|
|
38
|
-
let mockZephyrEngine: MockZephyrEngine;
|
|
39
|
-
let mockZephyrDefer: Promise<MockZephyrEngine>;
|
|
40
|
-
let mockZephyrDeferCreate: jest.Mock;
|
|
41
|
-
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
jest.clearAllMocks();
|
|
44
|
-
|
|
45
|
-
// Mock ZephyrEngine setup
|
|
46
|
-
mockZephyrEngine = {
|
|
47
|
-
buildProperties: { output: '' },
|
|
48
|
-
start_new_build: jest.fn(),
|
|
49
|
-
upload_assets: jest.fn(),
|
|
50
|
-
build_finished: jest.fn(),
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
mockZephyrDefer = Promise.resolve(mockZephyrEngine);
|
|
54
|
-
|
|
55
|
-
mockZephyrDeferCreate = jest.fn();
|
|
56
|
-
|
|
57
|
-
const mockDeferCreate = jest.fn().mockReturnValue({
|
|
58
|
-
zephyr_engine_defer: mockZephyrDefer,
|
|
59
|
-
zephyr_defer_create: mockZephyrDeferCreate,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
(ZephyrEngine.defer_create as jest.Mock).mockImplementation(mockDeferCreate);
|
|
63
|
-
|
|
64
|
-
(fileURLToPath as jest.Mock).mockImplementation((url: URL) => url.pathname);
|
|
65
|
-
(extractAstroAssetsFromBuildHook as jest.Mock).mockResolvedValue({});
|
|
66
|
-
(extractAstroAssetsMap as jest.Mock).mockResolvedValue({});
|
|
67
|
-
(ZephyrError.format as jest.Mock).mockImplementation((error: Error) => error.message);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('Basic Integration Structure', () => {
|
|
71
|
-
it('should return an Astro integration object', () => {
|
|
72
|
-
const integration = withZephyr();
|
|
73
|
-
|
|
74
|
-
expect(integration).toHaveProperty('name', 'with-zephyr');
|
|
75
|
-
expect(integration).toHaveProperty('hooks');
|
|
76
|
-
expect(integration.hooks).toHaveProperty('astro:config:done');
|
|
77
|
-
expect(integration.hooks).toHaveProperty('astro:build:done');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('should accept options parameter', () => {
|
|
81
|
-
const integration = withZephyr();
|
|
82
|
-
|
|
83
|
-
expect(integration).toHaveProperty('name', 'with-zephyr');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should have the correct hook functions', () => {
|
|
87
|
-
const integration = withZephyr();
|
|
88
|
-
|
|
89
|
-
expect(typeof integration.hooks['astro:config:done']).toBe('function');
|
|
90
|
-
expect(typeof integration.hooks['astro:build:done']).toBe('function');
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe('astro:config:done hook', () => {
|
|
95
|
-
it('should initialize ZephyrEngine with correct context from config.root', async () => {
|
|
96
|
-
const integration = withZephyr();
|
|
97
|
-
|
|
98
|
-
const mockConfig = {
|
|
99
|
-
root: new URL('file:///test/project/'),
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
await integration.hooks['astro:config:done']?.({
|
|
103
|
-
config: mockConfig,
|
|
104
|
-
} as Parameters<NonNullable<(typeof integration.hooks)['astro:config:done']>>[0]);
|
|
105
|
-
|
|
106
|
-
expect(fileURLToPath).toHaveBeenCalledWith(mockConfig.root);
|
|
107
|
-
expect(mockZephyrDeferCreate).toHaveBeenCalledWith({
|
|
108
|
-
builder: 'astro',
|
|
109
|
-
context: '/test/project/',
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should handle errors during setup', async () => {
|
|
114
|
-
const testError = new Error('Setup failed');
|
|
115
|
-
mockZephyrDeferCreate.mockImplementation(() => {
|
|
116
|
-
throw testError;
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const integration = withZephyr();
|
|
120
|
-
const mockConfig = { root: new URL('file:///test/project/') };
|
|
121
|
-
|
|
122
|
-
await integration.hooks['astro:config:done']?.({
|
|
123
|
-
config: mockConfig,
|
|
124
|
-
} as Parameters<NonNullable<(typeof integration.hooks)['astro:config:done']>>[0]);
|
|
125
|
-
|
|
126
|
-
expect(ZephyrError.format).toHaveBeenCalledWith(testError);
|
|
127
|
-
expect(logFn).toHaveBeenCalledWith('error', 'Setup failed');
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe('astro:build:done hook', () => {
|
|
132
|
-
it('should complete the full build workflow with assets parameter', async () => {
|
|
133
|
-
const integration = withZephyr();
|
|
134
|
-
const mockDir = new URL('file:///test/dist/');
|
|
135
|
-
const mockAssets = { 'index.html': '/test/dist/index.html' };
|
|
136
|
-
const mockAssetsMap = { hash1: { content: 'test', type: 'text/html' } };
|
|
137
|
-
const mockBuildStats = { stats: 'test' };
|
|
138
|
-
|
|
139
|
-
(extractAstroAssetsFromBuildHook as jest.Mock).mockResolvedValue(mockAssetsMap);
|
|
140
|
-
(zeBuildDashData as jest.Mock).mockResolvedValue(mockBuildStats);
|
|
141
|
-
|
|
142
|
-
await integration.hooks['astro:build:done']?.({
|
|
143
|
-
dir: mockDir,
|
|
144
|
-
assets: mockAssets,
|
|
145
|
-
} as unknown as Parameters<
|
|
146
|
-
NonNullable<(typeof integration.hooks)['astro:build:done']>
|
|
147
|
-
>[0]);
|
|
148
|
-
|
|
149
|
-
expect(fileURLToPath).toHaveBeenCalledWith(mockDir);
|
|
150
|
-
expect(mockZephyrEngine.buildProperties.output).toBe('/test/dist/');
|
|
151
|
-
expect(mockZephyrEngine.start_new_build).toHaveBeenCalled();
|
|
152
|
-
expect(extractAstroAssetsFromBuildHook).toHaveBeenCalledWith(
|
|
153
|
-
mockAssets,
|
|
154
|
-
'/test/dist/'
|
|
155
|
-
);
|
|
156
|
-
expect(mockZephyrEngine.upload_assets).toHaveBeenCalledWith({
|
|
157
|
-
assetsMap: mockAssetsMap,
|
|
158
|
-
buildStats: mockBuildStats,
|
|
159
|
-
});
|
|
160
|
-
expect(mockZephyrEngine.build_finished).toHaveBeenCalled();
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should handle missing assets parameter gracefully', async () => {
|
|
164
|
-
const integration = withZephyr();
|
|
165
|
-
const mockDir = new URL('file:///test/dist/');
|
|
166
|
-
const mockAssetsMap = { hash1: { content: 'test', type: 'text/html' } };
|
|
167
|
-
const mockBuildStats = { stats: 'test' };
|
|
168
|
-
|
|
169
|
-
(extractAstroAssetsFromBuildHook as jest.Mock).mockResolvedValue(mockAssetsMap);
|
|
170
|
-
(zeBuildDashData as jest.Mock).mockResolvedValue(mockBuildStats);
|
|
171
|
-
|
|
172
|
-
// Call without assets parameter
|
|
173
|
-
await integration.hooks['astro:build:done']?.({
|
|
174
|
-
dir: mockDir,
|
|
175
|
-
} as Parameters<NonNullable<(typeof integration.hooks)['astro:build:done']>>[0]);
|
|
176
|
-
|
|
177
|
-
expect(extractAstroAssetsFromBuildHook).toHaveBeenCalledWith(
|
|
178
|
-
undefined,
|
|
179
|
-
'/test/dist/'
|
|
180
|
-
);
|
|
181
|
-
expect(mockZephyrEngine.upload_assets).toHaveBeenCalled();
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('should handle errors during build completion', async () => {
|
|
185
|
-
const integration = withZephyr();
|
|
186
|
-
const mockDir = new URL('file:///test/dist/');
|
|
187
|
-
const mockAssets = { 'index.html': '/test/dist/index.html' };
|
|
188
|
-
const testError = new Error('Build failed');
|
|
189
|
-
|
|
190
|
-
(extractAstroAssetsFromBuildHook as jest.Mock).mockRejectedValue(testError);
|
|
191
|
-
|
|
192
|
-
await integration.hooks['astro:build:done']?.({
|
|
193
|
-
dir: mockDir,
|
|
194
|
-
assets: mockAssets,
|
|
195
|
-
} as unknown as Parameters<
|
|
196
|
-
NonNullable<(typeof integration.hooks)['astro:build:done']>
|
|
197
|
-
>[0]);
|
|
198
|
-
|
|
199
|
-
expect(ZephyrError.format).toHaveBeenCalledWith(testError);
|
|
200
|
-
expect(logFn).toHaveBeenCalledWith('error', 'Build failed');
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should handle engine initialization errors', async () => {
|
|
204
|
-
const badEngine = Promise.reject(new Error('Engine failed'));
|
|
205
|
-
(ZephyrEngine.defer_create as jest.Mock).mockReturnValue({
|
|
206
|
-
zephyr_engine_defer: badEngine,
|
|
207
|
-
zephyr_defer_create: jest.fn(),
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
const integration = withZephyr();
|
|
211
|
-
const mockDir = new URL('file:///test/dist/');
|
|
212
|
-
|
|
213
|
-
await integration.hooks['astro:build:done']?.({
|
|
214
|
-
dir: mockDir,
|
|
215
|
-
} as Parameters<NonNullable<(typeof integration.hooks)['astro:build:done']>>[0]);
|
|
216
|
-
|
|
217
|
-
expect(ZephyrError.format).toHaveBeenCalledWith(new Error('Engine failed'));
|
|
218
|
-
expect(logFn).toHaveBeenCalledWith('error', 'Engine failed');
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
describe('Integration Lifecycle', () => {
|
|
223
|
-
it('should handle sequential hook calls correctly', async () => {
|
|
224
|
-
const integration = withZephyr();
|
|
225
|
-
|
|
226
|
-
// First call config:done
|
|
227
|
-
const mockConfig = { root: new URL('file:///test/project/') };
|
|
228
|
-
await integration.hooks['astro:config:done']?.({
|
|
229
|
-
config: mockConfig,
|
|
230
|
-
} as Parameters<NonNullable<(typeof integration.hooks)['astro:config:done']>>[0]);
|
|
231
|
-
|
|
232
|
-
// Then call build:done
|
|
233
|
-
const mockDir = new URL('file:///test/dist/');
|
|
234
|
-
await integration.hooks['astro:build:done']?.({
|
|
235
|
-
dir: mockDir,
|
|
236
|
-
} as Parameters<NonNullable<(typeof integration.hooks)['astro:build:done']>>[0]);
|
|
237
|
-
|
|
238
|
-
expect(mockZephyrEngine.start_new_build).toHaveBeenCalled();
|
|
239
|
-
expect(mockZephyrEngine.build_finished).toHaveBeenCalled();
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import type { AstroIntegration, HookParameters } from 'astro';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import { logFn, zeBuildDashData, ZephyrEngine, ZephyrError } from 'zephyr-agent';
|
|
4
|
-
import { extractAstroAssetsFromBuildHook } from './internal/extract-astro-assets-map';
|
|
5
|
-
|
|
6
|
-
type AstroBuildDoneParams = HookParameters<'astro:build:done'> & {
|
|
7
|
-
assets?: Record<string, unknown> | Map<string, unknown> | Array<unknown>;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export function withZephyr(): AstroIntegration {
|
|
11
|
-
const { zephyr_engine_defer, zephyr_defer_create } = ZephyrEngine.defer_create();
|
|
12
|
-
|
|
13
|
-
return {
|
|
14
|
-
name: 'with-zephyr',
|
|
15
|
-
hooks: {
|
|
16
|
-
'astro:config:done': async ({ config }: HookParameters<'astro:config:done'>) => {
|
|
17
|
-
// config.root is a URL object, convert to file path
|
|
18
|
-
const contextPath = fileURLToPath(config.root);
|
|
19
|
-
try {
|
|
20
|
-
// Initialize ZephyrEngine with Astro context
|
|
21
|
-
zephyr_defer_create({
|
|
22
|
-
builder: 'astro',
|
|
23
|
-
context: contextPath,
|
|
24
|
-
});
|
|
25
|
-
} catch (error) {
|
|
26
|
-
logFn('error', ZephyrError.format(error));
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
'astro:build:done': async ({
|
|
30
|
-
dir,
|
|
31
|
-
...params
|
|
32
|
-
}: HookParameters<'astro:build:done'>) => {
|
|
33
|
-
try {
|
|
34
|
-
const zephyr_engine = await zephyr_engine_defer;
|
|
35
|
-
|
|
36
|
-
// Convert URL to file system path
|
|
37
|
-
const outputPath = fileURLToPath(dir);
|
|
38
|
-
|
|
39
|
-
// Set output directory for ZephyrEngine
|
|
40
|
-
zephyr_engine.buildProperties.output = outputPath;
|
|
41
|
-
|
|
42
|
-
// Start a new build
|
|
43
|
-
await zephyr_engine.start_new_build();
|
|
44
|
-
|
|
45
|
-
// Extract assets from params if available (Astro v5+), fallback to filesystem walking
|
|
46
|
-
const assets = (params as AstroBuildDoneParams).assets;
|
|
47
|
-
const assetsMap = await extractAstroAssetsFromBuildHook(assets, outputPath);
|
|
48
|
-
|
|
49
|
-
// Upload assets and build stats
|
|
50
|
-
await zephyr_engine.upload_assets({
|
|
51
|
-
assetsMap,
|
|
52
|
-
buildStats: await zeBuildDashData(zephyr_engine),
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Mark build as finished
|
|
56
|
-
await zephyr_engine.build_finished();
|
|
57
|
-
} catch (error) {
|
|
58
|
-
logFn('error', ZephyrError.format(error));
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
}
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { tmpdir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { buildAssetsMap } from 'zephyr-agent';
|
|
5
|
-
import { extractAstroAssetsFromBuildHook } from '../extract-astro-assets-map';
|
|
6
|
-
|
|
7
|
-
// Mock the zephyr-agent buildAssetsMap function
|
|
8
|
-
jest.mock('zephyr-agent', () => ({
|
|
9
|
-
buildAssetsMap: jest.fn(),
|
|
10
|
-
logFn: jest.fn(),
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
// This test focuses on the assets parameter parsing logic
|
|
14
|
-
// Fallback behavior is tested in the integration tests
|
|
15
|
-
|
|
16
|
-
describe('extractAstroAssetsFromBuildHook', () => {
|
|
17
|
-
let tempDir: string;
|
|
18
|
-
let consoleSpy: jest.SpyInstance;
|
|
19
|
-
|
|
20
|
-
beforeEach(async () => {
|
|
21
|
-
tempDir = join(tmpdir(), `astro-assets-hook-test-${Date.now()}`);
|
|
22
|
-
await mkdir(tempDir, { recursive: true });
|
|
23
|
-
consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {
|
|
24
|
-
// Mock implementation
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// Mock buildAssetsMap to return a simple hash-based result
|
|
28
|
-
(buildAssetsMap as jest.Mock).mockImplementation((assets) => {
|
|
29
|
-
const result: Record<string, unknown> = {};
|
|
30
|
-
Object.keys(assets).forEach((key, index) => {
|
|
31
|
-
const hash = `hash${index + 1}`;
|
|
32
|
-
result[hash] = {
|
|
33
|
-
hash,
|
|
34
|
-
content: assets[key].content,
|
|
35
|
-
type: assets[key].type,
|
|
36
|
-
filepath: key,
|
|
37
|
-
};
|
|
38
|
-
});
|
|
39
|
-
return result;
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// No need to mock fallback for core functionality tests
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
afterEach(async () => {
|
|
46
|
-
try {
|
|
47
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
48
|
-
} catch {
|
|
49
|
-
// Ignore cleanup errors
|
|
50
|
-
}
|
|
51
|
-
if (consoleSpy && typeof consoleSpy.mockRestore === 'function') {
|
|
52
|
-
consoleSpy.mockRestore();
|
|
53
|
-
}
|
|
54
|
-
jest.clearAllMocks();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe('Assets Parameter Handling', () => {
|
|
58
|
-
it('should handle assets as a plain object with file paths', async () => {
|
|
59
|
-
// Create test files
|
|
60
|
-
await writeFile(join(tempDir, 'index.html'), '<html><body>Hello</body></html>');
|
|
61
|
-
await writeFile(join(tempDir, 'style.css'), 'body { color: red; }');
|
|
62
|
-
|
|
63
|
-
const assets = {
|
|
64
|
-
'index.html': join(tempDir, 'index.html'),
|
|
65
|
-
'style.css': join(tempDir, 'style.css'),
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
await extractAstroAssetsFromBuildHook(assets, tempDir);
|
|
69
|
-
|
|
70
|
-
expect(buildAssetsMap).toHaveBeenCalledWith(
|
|
71
|
-
expect.objectContaining({
|
|
72
|
-
'index.html': expect.objectContaining({
|
|
73
|
-
content: expect.any(Buffer),
|
|
74
|
-
type: 'text/html',
|
|
75
|
-
}),
|
|
76
|
-
'style.css': expect.objectContaining({
|
|
77
|
-
content: expect.any(Buffer),
|
|
78
|
-
type: 'text/css',
|
|
79
|
-
}),
|
|
80
|
-
}),
|
|
81
|
-
expect.any(Function),
|
|
82
|
-
expect.any(Function)
|
|
83
|
-
);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should handle assets as a Map', async () => {
|
|
87
|
-
// Create test files
|
|
88
|
-
await writeFile(join(tempDir, 'script.js'), 'console.log("hello");');
|
|
89
|
-
|
|
90
|
-
const assets = new Map([['script.js', join(tempDir, 'script.js')]]);
|
|
91
|
-
|
|
92
|
-
const result = await extractAstroAssetsFromBuildHook(assets, tempDir);
|
|
93
|
-
|
|
94
|
-
expect(result).toBeDefined();
|
|
95
|
-
expect(buildAssetsMap).toHaveBeenCalledWith(
|
|
96
|
-
expect.objectContaining({
|
|
97
|
-
'script.js': expect.objectContaining({
|
|
98
|
-
type: 'application/javascript',
|
|
99
|
-
}),
|
|
100
|
-
}),
|
|
101
|
-
expect.any(Function),
|
|
102
|
-
expect.any(Function)
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should handle assets as an array of strings', async () => {
|
|
107
|
-
// Create test files
|
|
108
|
-
await writeFile(join(tempDir, 'data.json'), '{"key": "value"}');
|
|
109
|
-
|
|
110
|
-
const assets = [join(tempDir, 'data.json')];
|
|
111
|
-
|
|
112
|
-
await extractAstroAssetsFromBuildHook(assets, tempDir);
|
|
113
|
-
|
|
114
|
-
expect(buildAssetsMap).toHaveBeenCalledWith(
|
|
115
|
-
expect.objectContaining({
|
|
116
|
-
'data.json': expect.objectContaining({
|
|
117
|
-
type: 'application/json',
|
|
118
|
-
}),
|
|
119
|
-
}),
|
|
120
|
-
expect.any(Function),
|
|
121
|
-
expect.any(Function)
|
|
122
|
-
);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('should handle assets as an array of objects with path properties', async () => {
|
|
126
|
-
// Create test files
|
|
127
|
-
await writeFile(join(tempDir, 'image.png'), 'PNG data');
|
|
128
|
-
|
|
129
|
-
const assets = [{ path: join(tempDir, 'image.png'), size: 1024 }];
|
|
130
|
-
|
|
131
|
-
await extractAstroAssetsFromBuildHook(assets, tempDir);
|
|
132
|
-
|
|
133
|
-
expect(buildAssetsMap).toHaveBeenCalledWith(
|
|
134
|
-
expect.objectContaining({
|
|
135
|
-
'image.png': expect.objectContaining({
|
|
136
|
-
type: 'image/png',
|
|
137
|
-
}),
|
|
138
|
-
}),
|
|
139
|
-
expect.any(Function),
|
|
140
|
-
expect.any(Function)
|
|
141
|
-
);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should handle URL objects', async () => {
|
|
145
|
-
// Create test file
|
|
146
|
-
await writeFile(join(tempDir, 'favicon.ico'), 'ICO data');
|
|
147
|
-
|
|
148
|
-
const assets = {
|
|
149
|
-
'favicon.ico': new URL(`file://${join(tempDir, 'favicon.ico')}`),
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
await extractAstroAssetsFromBuildHook(assets, tempDir);
|
|
153
|
-
|
|
154
|
-
expect(buildAssetsMap).toHaveBeenCalledWith(
|
|
155
|
-
expect.objectContaining({
|
|
156
|
-
'favicon.ico': expect.objectContaining({
|
|
157
|
-
type: 'image/x-icon',
|
|
158
|
-
}),
|
|
159
|
-
}),
|
|
160
|
-
expect.any(Function),
|
|
161
|
-
expect.any(Function)
|
|
162
|
-
);
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Fallback behavior is tested in integration tests to avoid mocking complexity
|
|
167
|
-
|
|
168
|
-
describe('File Filtering', () => {
|
|
169
|
-
it('should skip files that match skip patterns', async () => {
|
|
170
|
-
// Create test files including ones that should be skipped
|
|
171
|
-
await writeFile(join(tempDir, 'index.html'), '<html></html>');
|
|
172
|
-
await writeFile(join(tempDir, 'style.css.map'), '{"version":3}');
|
|
173
|
-
|
|
174
|
-
const assets = {
|
|
175
|
-
'index.html': join(tempDir, 'index.html'),
|
|
176
|
-
'style.css.map': join(tempDir, 'style.css.map'),
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
await extractAstroAssetsFromBuildHook(assets, tempDir);
|
|
180
|
-
|
|
181
|
-
expect(buildAssetsMap).toHaveBeenCalledWith(
|
|
182
|
-
expect.objectContaining({
|
|
183
|
-
'index.html': expect.any(Object),
|
|
184
|
-
}),
|
|
185
|
-
expect.any(Function),
|
|
186
|
-
expect.any(Function)
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
// Should not include the .map file
|
|
190
|
-
expect(buildAssetsMap).toHaveBeenCalledWith(
|
|
191
|
-
expect.not.objectContaining({
|
|
192
|
-
'style.css.map': expect.any(Object),
|
|
193
|
-
}),
|
|
194
|
-
expect.any(Function),
|
|
195
|
-
expect.any(Function)
|
|
196
|
-
);
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
describe('Error Handling', () => {
|
|
201
|
-
// File read error handling and fallback behavior is tested in integration tests
|
|
202
|
-
|
|
203
|
-
it('should handle complex nested asset structures', async () => {
|
|
204
|
-
// Create test file
|
|
205
|
-
await writeFile(join(tempDir, 'complex.json'), '{"nested": "data"}');
|
|
206
|
-
|
|
207
|
-
const assets = {
|
|
208
|
-
route1: [join(tempDir, 'complex.json'), { path: join(tempDir, 'complex.json') }],
|
|
209
|
-
route2: {
|
|
210
|
-
url: join(tempDir, 'complex.json'),
|
|
211
|
-
},
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
await extractAstroAssetsFromBuildHook(assets, tempDir);
|
|
215
|
-
|
|
216
|
-
expect(buildAssetsMap).toHaveBeenCalledWith(
|
|
217
|
-
expect.objectContaining({
|
|
218
|
-
'complex.json': expect.any(Object),
|
|
219
|
-
}),
|
|
220
|
-
expect.any(Function),
|
|
221
|
-
expect.any(Function)
|
|
222
|
-
);
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
describe('Path Resolution', () => {
|
|
227
|
-
it('should handle relative paths correctly', async () => {
|
|
228
|
-
// Create test file
|
|
229
|
-
await writeFile(join(tempDir, 'relative.css'), 'body {}');
|
|
230
|
-
|
|
231
|
-
const assets = {
|
|
232
|
-
'relative.css': 'relative.css', // Relative path
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
await extractAstroAssetsFromBuildHook(assets, tempDir);
|
|
236
|
-
|
|
237
|
-
expect(buildAssetsMap).toHaveBeenCalledWith(
|
|
238
|
-
expect.objectContaining({
|
|
239
|
-
'relative.css': expect.objectContaining({
|
|
240
|
-
type: 'text/css',
|
|
241
|
-
}),
|
|
242
|
-
}),
|
|
243
|
-
expect.any(Function),
|
|
244
|
-
expect.any(Function)
|
|
245
|
-
);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('should handle absolute paths correctly', async () => {
|
|
249
|
-
// Create test file
|
|
250
|
-
await writeFile(join(tempDir, 'absolute.js'), 'console.log()');
|
|
251
|
-
|
|
252
|
-
const assets = {
|
|
253
|
-
'absolute.js': join(tempDir, 'absolute.js'), // Absolute path
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
await extractAstroAssetsFromBuildHook(assets, tempDir);
|
|
257
|
-
|
|
258
|
-
expect(buildAssetsMap).toHaveBeenCalledWith(
|
|
259
|
-
expect.objectContaining({
|
|
260
|
-
'absolute.js': expect.objectContaining({
|
|
261
|
-
type: 'application/javascript',
|
|
262
|
-
}),
|
|
263
|
-
}),
|
|
264
|
-
expect.any(Function),
|
|
265
|
-
expect.any(Function)
|
|
266
|
-
);
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
});
|