Skip to content

Commit 53f0f36

Browse files
feat: add mono repository support (#123)
* feat: add mono repository support chore: fix fixture preparation * Update src/run/handlers/cache.cts Co-authored-by: Eduardo Bouças <[email protected]> * Update .eslintrc.cjs Co-authored-by: Eduardo Bouças <[email protected]> * Update .eslintrc.cjs Co-authored-by: Eduardo Bouças <[email protected]> * chore: update comment --------- Co-authored-by: Eduardo Bouças <[email protected]>
1 parent 18f18e3 commit 53f0f36

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+4113
-464
lines changed

.eslintrc.cjs

+36-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ module.exports = {
1616
'import/no-namespace': 'off',
1717
'import/extensions': 'off',
1818
'max-depth': 'off',
19+
'func-style': 'off',
20+
'class-methods-use-this': 'off',
21+
'promise/prefer-await-to-then': 'off',
22+
'promise/prefer-await-to-callbacks': 'off',
23+
'promise/catch-or-return': 'off',
24+
'promise/always-return': 'off',
25+
'max-nested-callbacks': 'off',
26+
'no-inline-comments': 'off',
27+
'line-comment-position': 'off',
28+
'max-lines': 'off',
1929
},
2030
overrides: [
2131
...overrides,
@@ -29,8 +39,32 @@ module.exports = {
2939
{
3040
files: ['src/**/*.test.*'],
3141
rules: {
32-
'max-statements': off,
33-
'max-lines-per-function': off,
42+
'max-statements': 'off',
43+
'max-lines-per-function': 'off',
44+
},
45+
},
46+
{
47+
files: ['src/build/**/*.ts'],
48+
rules: {
49+
'no-restricted-imports': [
50+
'error',
51+
{
52+
paths: [
53+
{
54+
name: 'path',
55+
importNames: ['resolve'],
56+
message:
57+
'Please use `PluginContext.resolve` instead to comply with our monorepo support',
58+
},
59+
{
60+
name: 'node:path',
61+
importNames: ['resolve'],
62+
message:
63+
'Please use `PluginContext.resolve` instead to comply with our monorepo support',
64+
},
65+
],
66+
},
67+
],
3468
},
3569
},
3670
],

.github/workflows/run-tests.yml

+2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ jobs:
7878
run: npm ci
7979
- name: 'Build'
8080
run: npm run build
81+
- name: 'Lint'
82+
run: npm run lint
8183
- name: 'Prepare Fixtures'
8284
run: npm run pretest
8385
- name: 'Test'

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ edge-runtime/vendor
1111
/playwright/.cache/
1212

1313
deno.lock
14+
.eslintcache

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@netlify/next-runtime",
3-
"version": "5.0.0-alpha.27",
3+
"version": "5.0.0-alpha.30",
44
"description": "Run Next.js seamlessly on Netlify",
55
"main": "./dist/index.js",
66
"type": "module",
@@ -18,6 +18,7 @@
1818
"pretest": "node tests/prepare.mjs",
1919
"build": "node ./tools/build.js",
2020
"build:watch": "node ./tools/build.js --watch",
21+
"lint": "eslint --cache --format=codeframe --max-warnings=0 src/**/*.ts",
2122
"test": "vitest",
2223
"test:ci": "vitest run --reporter=default --retry=3",
2324
"e2e": "playwright test",

src/build/cache.ts

+7-12
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
1-
import type { NetlifyPluginOptions } from '@netlify/build'
2-
import { resolve } from 'node:path'
1+
import { join } from 'node:path'
32

4-
export const saveBuildCache = async ({
5-
constants: { PUBLISH_DIR },
6-
utils: { cache },
7-
}: Pick<NetlifyPluginOptions, 'constants' | 'utils'>) => {
8-
if (await cache.save(resolve(PUBLISH_DIR, 'cache'))) {
3+
import type { PluginContext } from './plugin-context.js'
4+
5+
export const saveBuildCache = async (ctx: PluginContext) => {
6+
if (await ctx.utils.cache.save(join(ctx.publishDir, 'cache'))) {
97
console.log('Next.js cache saved.')
108
} else {
119
console.log('No Next.js cache to save.')
1210
}
1311
}
1412

15-
export const restoreBuildCache = async ({
16-
constants: { PUBLISH_DIR },
17-
utils: { cache },
18-
}: Pick<NetlifyPluginOptions, 'constants' | 'utils'>) => {
19-
if (await cache.restore(resolve(PUBLISH_DIR, 'cache'))) {
13+
export const restoreBuildCache = async (ctx: PluginContext) => {
14+
if (await ctx.utils.cache.restore(join(ctx.publishDir, 'cache'))) {
2015
console.log('Next.js cache restored.')
2116
} else {
2217
console.log('No Next.js cache to restore.')

src/build/config.ts

-42
This file was deleted.

src/build/constants.ts

-27
This file was deleted.

src/build/content/prerendered.ts

+26-85
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,16 @@
1-
import type { NetlifyPluginOptions } from '@netlify/build'
2-
import glob from 'fast-glob'
3-
import { Buffer } from 'node:buffer'
41
import { existsSync } from 'node:fs'
5-
import { mkdir, readFile, writeFile } from 'node:fs/promises'
6-
import { dirname, resolve } from 'node:path'
7-
import { getPrerenderManifest } from '../config.js'
8-
import { BLOB_DIR } from '../constants.js'
9-
10-
export type CacheEntry = {
11-
lastModified: number
12-
value: CacheValue
13-
}
14-
15-
type CacheValue = PageCacheValue | RouteCacheValue | FetchCacheValue
16-
17-
export type PageCacheValue = {
18-
kind: 'PAGE'
19-
html: string
20-
pageData: string
21-
headers?: { [k: string]: string }
22-
status?: number
23-
}
24-
25-
type RouteCacheValue = {
26-
kind: 'ROUTE'
27-
body: string
28-
headers: { [k: string]: string }
29-
status: number
30-
}
2+
import { readFile } from 'node:fs/promises'
3+
import { join } from 'node:path'
314

32-
type FetchCacheValue = {
33-
kind: 'FETCH'
34-
data: {
35-
headers: { [k: string]: string }
36-
body: string
37-
url: string
38-
status?: number
39-
tags?: string[]
40-
}
41-
}
42-
43-
/**
44-
* Write a cache entry to the blob upload directory using
45-
* base64 keys to avoid collisions with directories
46-
*/
47-
const writeCacheEntry = async (key: string, value: CacheValue) => {
48-
const path = resolve(BLOB_DIR, Buffer.from(key).toString('base64'))
49-
const entry = JSON.stringify({
50-
lastModified: Date.now(),
51-
value,
52-
} satisfies CacheEntry)
5+
import glob from 'fast-glob'
536

54-
await mkdir(dirname(path), { recursive: true })
55-
await writeFile(path, entry, 'utf-8')
56-
}
7+
import type {
8+
CacheValue,
9+
FetchCacheValue,
10+
PageCacheValue,
11+
PluginContext,
12+
RouteCacheValue,
13+
} from '../plugin-context.js'
5714

5815
/**
5916
* Normalize routes by stripping leading slashes and ensuring root path is index
@@ -87,15 +44,10 @@ const buildFetchCacheValue = async (path: string): Promise<FetchCacheValue> => (
8744
/**
8845
* Upload prerendered content to the blob store
8946
*/
90-
export const copyPrerenderedContent = async ({
91-
constants: { PUBLISH_DIR },
92-
utils: {
93-
build: { failBuild },
94-
},
95-
}: Pick<NetlifyPluginOptions, 'constants' | 'utils'>) => {
47+
export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void> => {
9648
try {
9749
// read prerendered content and build JSON key/values for the blob store
98-
const manifest = await getPrerenderManifest({ PUBLISH_DIR })
50+
const manifest = await ctx.getPrerenderManifest()
9951

10052
await Promise.all(
10153
Object.entries(manifest.routes).map(async ([route, meta]) => {
@@ -104,62 +56,51 @@ export const copyPrerenderedContent = async ({
10456

10557
switch (true) {
10658
case meta.dataRoute?.endsWith('.json'):
107-
value = await buildPagesCacheValue(resolve(PUBLISH_DIR, 'server/pages', key))
59+
value = await buildPagesCacheValue(join(ctx.publishDir, 'server/pages', key))
10860
break
109-
11061
case meta.dataRoute?.endsWith('.rsc'):
111-
value = await buildAppCacheValue(resolve(PUBLISH_DIR, 'server/app', key))
62+
value = await buildAppCacheValue(join(ctx.publishDir, 'server/app', key))
11263
break
113-
11464
case meta.dataRoute === null:
115-
value = await buildRouteCacheValue(resolve(PUBLISH_DIR, 'server/app', key))
65+
value = await buildRouteCacheValue(join(ctx.publishDir, 'server/app', key))
11666
break
117-
11867
default:
11968
throw new Error(`Unrecognized content: ${route}`)
12069
}
12170

122-
await writeCacheEntry(key, value)
71+
await ctx.writeCacheEntry(key, value)
12372
}),
12473
)
12574

12675
// app router 404 pages are not in the prerender manifest
12776
// so we need to check for them manually
128-
if (existsSync(resolve(PUBLISH_DIR, `server/app/_not-found.html`))) {
77+
if (existsSync(join(ctx.publishDir, `server/app/_not-found.html`))) {
12978
const key = '404'
130-
const value = await buildAppCacheValue(resolve(PUBLISH_DIR, 'server/app/_not-found'))
131-
await writeCacheEntry(key, value)
79+
const value = await buildAppCacheValue(join(ctx.publishDir, 'server/app/_not-found'))
80+
await ctx.writeCacheEntry(key, value)
13281
}
13382
} catch (error) {
134-
failBuild(
135-
'Failed assembling prerendered content for upload',
136-
error instanceof Error ? { error } : {},
137-
)
83+
ctx.failBuild('Failed assembling prerendered content for upload', error)
13884
}
13985
}
14086

14187
/**
14288
* Upload fetch content to the blob store
14389
*/
144-
export const copyFetchContent = async ({
145-
constants: { PUBLISH_DIR },
146-
utils: {
147-
build: { failBuild },
148-
},
149-
}: Pick<NetlifyPluginOptions, 'constants' | 'utils'>) => {
90+
export const copyFetchContent = async (ctx: PluginContext): Promise<void> => {
15091
try {
15192
const paths = await glob(['!(*.*)'], {
152-
cwd: resolve(PUBLISH_DIR, 'cache/fetch-cache'),
93+
cwd: join(ctx.publishDir, 'cache/fetch-cache'),
15394
extglob: true,
15495
})
15596

15697
await Promise.all(
157-
paths.map(async (key) => {
158-
const value = await buildFetchCacheValue(resolve(PUBLISH_DIR, 'cache/fetch-cache', key))
159-
await writeCacheEntry(key, value)
98+
paths.map(async (key): Promise<void> => {
99+
const value = await buildFetchCacheValue(join(ctx.publishDir, 'cache/fetch-cache', key))
100+
await ctx.writeCacheEntry(key, value)
160101
}),
161102
)
162103
} catch (error) {
163-
failBuild('Failed assembling fetch content for upload', error instanceof Error ? { error } : {})
104+
ctx.failBuild('Failed assembling fetch content for upload', error)
164105
}
165106
}

0 commit comments

Comments
 (0)