1
- import { Buffer } from 'node:buffer '
1
+ import { readFile } from 'node:fs/promises '
2
2
import { join } from 'node:path'
3
3
import { inspect } from 'node:util'
4
4
5
5
import type { NetlifyPluginOptions } from '@netlify/build'
6
6
import glob from 'fast-glob'
7
+ import type { PrerenderManifest } from 'next/dist/build/index.js'
7
8
import { beforeEach , describe , expect , Mock , test , vi } from 'vitest'
8
9
9
- import { mockFileSystem } from '../../../tests/index.js'
10
+ import { decodeBlobKey , encodeBlobKey , mockFileSystem } from '../../../tests/index.js'
10
11
import { type FixtureTestContext } from '../../../tests/utils/contexts.js'
11
12
import { createFsFixture } from '../../../tests/utils/fixture.js'
12
13
import { PluginContext , RequiredServerFilesManifest } from '../plugin-context.js'
@@ -21,7 +22,19 @@ type Context = FixtureTestContext & {
21
22
const createFsFixtureWithBasePath = (
22
23
fixture : Record < string , string > ,
23
24
ctx : Omit < Context , 'pluginContext' > ,
24
- basePath = '' ,
25
+
26
+ {
27
+ basePath = '' ,
28
+ // eslint-disable-next-line unicorn/no-useless-undefined
29
+ i18n = undefined ,
30
+ dynamicRoutes = { } ,
31
+ } : {
32
+ basePath ?: string
33
+ i18n ?: Pick < NonNullable < RequiredServerFilesManifest [ 'config' ] [ 'i18n' ] > , 'locales' >
34
+ dynamicRoutes ?: {
35
+ [ route : string ] : Pick < PrerenderManifest [ 'dynamicRoutes' ] [ '' ] , 'fallback' >
36
+ }
37
+ } = { } ,
25
38
) => {
26
39
return createFsFixture (
27
40
{
@@ -32,8 +45,10 @@ const createFsFixtureWithBasePath = (
32
45
appDir : ctx . relativeAppDir ,
33
46
config : {
34
47
distDir : ctx . publishDir ,
48
+ i18n,
35
49
} ,
36
50
} as Pick < RequiredServerFilesManifest , 'relativeAppDir' | 'appDir' > ) ,
51
+ [ join ( ctx . publishDir , 'prerender-manifest.json' ) ] : JSON . stringify ( { dynamicRoutes } ) ,
37
52
} ,
38
53
ctx ,
39
54
)
@@ -121,7 +136,7 @@ describe('Regular Repository layout', () => {
121
136
'.next/static/sub-dir/test2.js' : '' ,
122
137
} ,
123
138
ctx ,
124
- '/base/path' ,
139
+ { basePath : '/base/path' } ,
125
140
)
126
141
127
142
await copyStaticAssets ( pluginContext )
@@ -168,7 +183,7 @@ describe('Regular Repository layout', () => {
168
183
'public/another-asset.json' : '' ,
169
184
} ,
170
185
ctx ,
171
- '/base/path' ,
186
+ { basePath : '/base/path' } ,
172
187
)
173
188
174
189
await copyStaticAssets ( pluginContext )
@@ -182,26 +197,100 @@ describe('Regular Repository layout', () => {
182
197
)
183
198
} )
184
199
185
- test < Context > ( 'should copy the static pages to the publish directory if there are no corresponding JSON files' , async ( {
186
- pluginContext,
187
- ...ctx
188
- } ) => {
189
- await createFsFixtureWithBasePath (
190
- {
191
- '.next/server/pages/test.html' : '' ,
192
- '.next/server/pages/test2.html' : '' ,
193
- '.next/server/pages/test3.json' : '' ,
194
- } ,
195
- ctx ,
196
- )
200
+ describe ( 'should copy the static pages to the publish directory if there are no corresponding JSON files and mark wether html file is a fallback' , ( ) => {
201
+ test < Context > ( 'no i18n' , async ( { pluginContext, ...ctx } ) => {
202
+ await createFsFixtureWithBasePath (
203
+ {
204
+ '.next/server/pages/test.html' : '' ,
205
+ '.next/server/pages/test2.html' : '' ,
206
+ '.next/server/pages/test3.json' : '' ,
207
+ '.next/server/pages/blog/[slug].html' : '' ,
208
+ } ,
209
+ ctx ,
210
+ {
211
+ dynamicRoutes : {
212
+ '/blog/[slug]' : {
213
+ fallback : '/blog/[slug].html' ,
214
+ } ,
215
+ } ,
216
+ } ,
217
+ )
197
218
198
- await copyStaticContent ( pluginContext )
199
- const files = await glob ( '**/*' , { cwd : pluginContext . blobDir , dot : true } )
219
+ await copyStaticContent ( pluginContext )
220
+ const files = await glob ( '**/*' , { cwd : pluginContext . blobDir , dot : true } )
221
+
222
+ const expectedStaticPages = [ 'blog/[slug].html' , 'test.html' , 'test2.html' ]
223
+ const expectedFallbacks = new Set ( [ 'blog/[slug].html' ] )
224
+
225
+ expect ( files . map ( ( path ) => decodeBlobKey ( path ) ) . sort ( ) ) . toEqual ( expectedStaticPages )
226
+
227
+ for ( const page of expectedStaticPages ) {
228
+ const expectedIsFallback = expectedFallbacks . has ( page )
229
+
230
+ const blob = JSON . parse (
231
+ await readFile ( join ( pluginContext . blobDir , await encodeBlobKey ( page ) ) , 'utf-8' ) ,
232
+ )
200
233
201
- expect ( files . map ( ( path ) => Buffer . from ( path , 'base64' ) . toString ( 'utf-8' ) ) . sort ( ) ) . toEqual ( [
202
- 'test.html' ,
203
- 'test2.html' ,
204
- ] )
234
+ expect ( blob , `${ page } should ${ expectedIsFallback ? '' : 'not ' } be a fallback` ) . toEqual ( {
235
+ html : '' ,
236
+ isFallback : expectedIsFallback ,
237
+ } )
238
+ }
239
+ } )
240
+
241
+ test < Context > ( 'with i18n' , async ( { pluginContext, ...ctx } ) => {
242
+ await createFsFixtureWithBasePath (
243
+ {
244
+ '.next/server/pages/de/test.html' : '' ,
245
+ '.next/server/pages/de/test2.html' : '' ,
246
+ '.next/server/pages/de/test3.json' : '' ,
247
+ '.next/server/pages/de/blog/[slug].html' : '' ,
248
+ '.next/server/pages/en/test.html' : '' ,
249
+ '.next/server/pages/en/test2.html' : '' ,
250
+ '.next/server/pages/en/test3.json' : '' ,
251
+ '.next/server/pages/en/blog/[slug].html' : '' ,
252
+ } ,
253
+ ctx ,
254
+ {
255
+ dynamicRoutes : {
256
+ '/blog/[slug]' : {
257
+ fallback : '/blog/[slug].html' ,
258
+ } ,
259
+ } ,
260
+ i18n : {
261
+ locales : [ 'en' , 'de' ] ,
262
+ } ,
263
+ } ,
264
+ )
265
+
266
+ await copyStaticContent ( pluginContext )
267
+ const files = await glob ( '**/*' , { cwd : pluginContext . blobDir , dot : true } )
268
+
269
+ const expectedStaticPages = [
270
+ 'de/blog/[slug].html' ,
271
+ 'de/test.html' ,
272
+ 'de/test2.html' ,
273
+ 'en/blog/[slug].html' ,
274
+ 'en/test.html' ,
275
+ 'en/test2.html' ,
276
+ ]
277
+ const expectedFallbacks = new Set ( [ 'en/blog/[slug].html' , 'de/blog/[slug].html' ] )
278
+
279
+ expect ( files . map ( ( path ) => decodeBlobKey ( path ) ) . sort ( ) ) . toEqual ( expectedStaticPages )
280
+
281
+ for ( const page of expectedStaticPages ) {
282
+ const expectedIsFallback = expectedFallbacks . has ( page )
283
+
284
+ const blob = JSON . parse (
285
+ await readFile ( join ( pluginContext . blobDir , await encodeBlobKey ( page ) ) , 'utf-8' ) ,
286
+ )
287
+
288
+ expect ( blob , `${ page } should ${ expectedIsFallback ? '' : 'not ' } be a fallback` ) . toEqual ( {
289
+ html : '' ,
290
+ isFallback : expectedIsFallback ,
291
+ } )
292
+ }
293
+ } )
205
294
} )
206
295
207
296
test < Context > ( 'should not copy the static pages to the publish directory if there are corresponding JSON files' , async ( {
@@ -269,7 +358,7 @@ describe('Mono Repository', () => {
269
358
'apps/app-1/.next/static/sub-dir/test2.js' : '' ,
270
359
} ,
271
360
ctx ,
272
- '/base/path' ,
361
+ { basePath : '/base/path' } ,
273
362
)
274
363
275
364
await copyStaticAssets ( pluginContext )
@@ -316,7 +405,7 @@ describe('Mono Repository', () => {
316
405
'apps/app-1/public/another-asset.json' : '' ,
317
406
} ,
318
407
ctx ,
319
- '/base/path' ,
408
+ { basePath : '/base/path' } ,
320
409
)
321
410
322
411
await copyStaticAssets ( pluginContext )
@@ -330,26 +419,100 @@ describe('Mono Repository', () => {
330
419
)
331
420
} )
332
421
333
- test < Context > ( 'should copy the static pages to the publish directory if there are no corresponding JSON files' , async ( {
334
- pluginContext,
335
- ...ctx
336
- } ) => {
337
- await createFsFixtureWithBasePath (
338
- {
339
- 'apps/app-1/.next/server/pages/test.html' : '' ,
340
- 'apps/app-1/.next/server/pages/test2.html' : '' ,
341
- 'apps/app-1/.next/server/pages/test3.json' : '' ,
342
- } ,
343
- ctx ,
344
- )
422
+ describe ( 'should copy the static pages to the publish directory if there are no corresponding JSON files and mark wether html file is a fallback' , ( ) => {
423
+ test < Context > ( 'no i18n' , async ( { pluginContext, ...ctx } ) => {
424
+ await createFsFixtureWithBasePath (
425
+ {
426
+ 'apps/app-1/.next/server/pages/test.html' : '' ,
427
+ 'apps/app-1/.next/server/pages/test2.html' : '' ,
428
+ 'apps/app-1/.next/server/pages/test3.json' : '' ,
429
+ 'apps/app-1/.next/server/pages/blog/[slug].html' : '' ,
430
+ } ,
431
+ ctx ,
432
+ {
433
+ dynamicRoutes : {
434
+ '/blog/[slug]' : {
435
+ fallback : '/blog/[slug].html' ,
436
+ } ,
437
+ } ,
438
+ } ,
439
+ )
345
440
346
- await copyStaticContent ( pluginContext )
347
- const files = await glob ( '**/*' , { cwd : pluginContext . blobDir , dot : true } )
441
+ await copyStaticContent ( pluginContext )
442
+ const files = await glob ( '**/*' , { cwd : pluginContext . blobDir , dot : true } )
443
+
444
+ const expectedStaticPages = [ 'blog/[slug].html' , 'test.html' , 'test2.html' ]
445
+ const expectedFallbacks = new Set ( [ 'blog/[slug].html' ] )
446
+
447
+ expect ( files . map ( ( path ) => decodeBlobKey ( path ) ) . sort ( ) ) . toEqual ( expectedStaticPages )
448
+
449
+ for ( const page of expectedStaticPages ) {
450
+ const expectedIsFallback = expectedFallbacks . has ( page )
451
+
452
+ const blob = JSON . parse (
453
+ await readFile ( join ( pluginContext . blobDir , await encodeBlobKey ( page ) ) , 'utf-8' ) ,
454
+ )
348
455
349
- expect ( files . map ( ( path ) => Buffer . from ( path , 'base64' ) . toString ( 'utf-8' ) ) . sort ( ) ) . toEqual ( [
350
- 'test.html' ,
351
- 'test2.html' ,
352
- ] )
456
+ expect ( blob , `${ page } should ${ expectedIsFallback ? '' : 'not ' } be a fallback` ) . toEqual ( {
457
+ html : '' ,
458
+ isFallback : expectedIsFallback ,
459
+ } )
460
+ }
461
+ } )
462
+
463
+ test < Context > ( 'with i18n' , async ( { pluginContext, ...ctx } ) => {
464
+ await createFsFixtureWithBasePath (
465
+ {
466
+ 'apps/app-1/.next/server/pages/de/test.html' : '' ,
467
+ 'apps/app-1/.next/server/pages/de/test2.html' : '' ,
468
+ 'apps/app-1/.next/server/pages/de/test3.json' : '' ,
469
+ 'apps/app-1/.next/server/pages/de/blog/[slug].html' : '' ,
470
+ 'apps/app-1/.next/server/pages/en/test.html' : '' ,
471
+ 'apps/app-1/.next/server/pages/en/test2.html' : '' ,
472
+ 'apps/app-1/.next/server/pages/en/test3.json' : '' ,
473
+ 'apps/app-1/.next/server/pages/en/blog/[slug].html' : '' ,
474
+ } ,
475
+ ctx ,
476
+ {
477
+ dynamicRoutes : {
478
+ '/blog/[slug]' : {
479
+ fallback : '/blog/[slug].html' ,
480
+ } ,
481
+ } ,
482
+ i18n : {
483
+ locales : [ 'en' , 'de' ] ,
484
+ } ,
485
+ } ,
486
+ )
487
+
488
+ await copyStaticContent ( pluginContext )
489
+ const files = await glob ( '**/*' , { cwd : pluginContext . blobDir , dot : true } )
490
+
491
+ const expectedStaticPages = [
492
+ 'de/blog/[slug].html' ,
493
+ 'de/test.html' ,
494
+ 'de/test2.html' ,
495
+ 'en/blog/[slug].html' ,
496
+ 'en/test.html' ,
497
+ 'en/test2.html' ,
498
+ ]
499
+ const expectedFallbacks = new Set ( [ 'en/blog/[slug].html' , 'de/blog/[slug].html' ] )
500
+
501
+ expect ( files . map ( ( path ) => decodeBlobKey ( path ) ) . sort ( ) ) . toEqual ( expectedStaticPages )
502
+
503
+ for ( const page of expectedStaticPages ) {
504
+ const expectedIsFallback = expectedFallbacks . has ( page )
505
+
506
+ const blob = JSON . parse (
507
+ await readFile ( join ( pluginContext . blobDir , await encodeBlobKey ( page ) ) , 'utf-8' ) ,
508
+ )
509
+
510
+ expect ( blob , `${ page } should ${ expectedIsFallback ? '' : 'not ' } be a fallback` ) . toEqual ( {
511
+ html : '' ,
512
+ isFallback : expectedIsFallback ,
513
+ } )
514
+ }
515
+ } )
353
516
} )
354
517
355
518
test < Context > ( 'should not copy the static pages to the publish directory if there are corresponding JSON files' , async ( {
0 commit comments