Skip to content

Commit c58c2f9

Browse files
authored
Fix/nextjs sdk: inline lru-cache (#3590)
## Description Add a short description of what changes you made, why you made them, and any other context that you think might be helpful for someone to better understand what is contained in this pull request. This sort of information is useful for people reviewing the code, as well as anyone from the future trying to understand why changes were made or why a bug started happening. _Screenshot_ If relevant, add a screenshot or two of the changes you made.
1 parent a40d5ab commit c58c2f9

File tree

14 files changed

+85
-50
lines changed

14 files changed

+85
-50
lines changed

.changeset/cold-pillows-agree.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@builder.io/sdk-react-nextjs": patch
3+
---
4+
5+
misc: inline `lru-cache` dependency

.changeset/young-ladybugs-lick.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@builder.io/sdk-react-nextjs': patch
3+
---
4+
5+
Fix: previewing content while inline editing.
6+
Chore: refactored build process to `preserveModules` in output.

examples/next-js-sdk-gen-2-experimental-app-directory/package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/next-js-sdk-gen-2-experimental-app-directory/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"lint": "next lint"
99
},
1010
"dependencies": {
11-
"@builder.io/sdk-react-nextjs": "^0.14.30-0",
11+
"@builder.io/sdk-react-nextjs": "^0.16.11",
1212
"next": "^14.2.4",
1313
"react": "^18.3.1",
1414
"react-dom": "^18.3.1"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"lint:fix": "prettier --write '**/*.{js,jsx,ts,tsx}'",
3232
"update-npm-dependency": "zx ./scripts/update-npm-dependency.mjs",
3333
"g:changeset": "changeset",
34-
"g:nx": "nx"
34+
"g:nx": "cd $INIT_CWD && nx"
3535
},
3636
"engines": {
3737
"yarn": ">= 3.0.0"

packages/sdks-tests/src/e2e-tests/hydration.spec.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ test.describe('Hydration', () => {
2424

2525
test('No mismatch on A/B test content', async ({ page, packageName }) => {
2626
test.skip(packageName === 'angular-ssr', 'Angular SSR does not support A/B tests');
27-
test.fail(
28-
packageName === 'nextjs-sdk-next-app',
29-
"NextJS SDK currently does not support SSR'd A/B tests. Losing variants are stripped from SSR'd Content too late."
30-
);
3127
await page.goto('/ab-test-interactive');
3228
await page.locator('a').locator('visible=true').first().click({ timeout: 10000 });
3329
await findTextInPage({ page, text: 'Stack at tablet' });

packages/sdks-tests/src/e2e-tests/slot.spec.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,14 @@ import { checkIsRN, test } from '../helpers/index.js';
55
test.describe('Slot', () => {
66
test('slot should render', async ({ page, packageName }) => {
77
// gen1-remix and gen1-next skipped because React.useContext is not recognized
8-
// rsc skipped because it fetches the slot content from the server
9-
test.fail(['gen1-remix', 'gen1-next', 'nextjs-sdk-next-app'].includes(packageName));
8+
test.fail(['gen1-remix', 'gen1-next'].includes(packageName));
109
await page.goto('/slot');
1110
await expect(page.locator('text=Inside a slot!!')).toBeVisible();
1211
});
1312

1413
test('slot should render in the correct place', async ({ page, packageName, sdk }) => {
1514
// gen1-remix and gen1-next skipped because React.useContext is not recognized
16-
// rsc skipped because it fetches the slot content from the server
17-
test.fail(['gen1-remix', 'gen1-next', 'nextjs-sdk-next-app'].includes(packageName));
15+
test.fail(['gen1-remix', 'gen1-next'].includes(packageName));
1816
await page.goto('/slot');
1917
const builderTextElements = checkIsRN(sdk)
2018
? page.locator('[data-testid="div"]')
@@ -27,8 +25,7 @@ test.describe('Slot', () => {
2725

2826
test('slot should render with symbol (with content)', async ({ page, packageName }) => {
2927
// gen1-remix and gen1-next skipped because React.useContext is not recognized
30-
// rsc skipped because it fetches the slot content from the server
31-
test.fail(['gen1-remix', 'gen1-next', 'nextjs-sdk-next-app'].includes(packageName));
28+
test.fail(['gen1-remix', 'gen1-next'].includes(packageName));
3229
await page.goto('/slot-with-symbol');
3330

3431
await expect(page.locator('text=This is called recursion!')).toBeVisible();

packages/sdks-tests/src/e2e-tests/symbols.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,7 @@ test.describe('Symbols', () => {
229229
await page.goto('/nested-symbols');
230230

231231
// gen1-remix and gen1-next are also skipped because React.useContext is not recognized
232-
// rsc skipped because it fetches the content from the server
233-
test.fail(['gen1-remix', 'gen1-next', 'nextjs-sdk-next-app'].includes(packageName));
232+
test.fail(['gen1-remix', 'gen1-next'].includes(packageName));
234233

235234
const symbols = page.locator('[builder-model="symbol"]');
236235
await expect(symbols).toHaveCount(2);

packages/sdks/output/nextjs/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,7 @@
110110
"vite": "^4.4.9"
111111
},
112112
"dependencies": {
113-
"isolated-vm": "^5.0.0",
114-
"lru-cache": "^10.0.0"
113+
"isolated-vm": "^5.0.0"
115114
},
116115
"nx": {
117116
"targets": {

packages/sdks/output/nextjs/vite.config.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { viteOutputGenerator } from '@builder.io/sdks/output-generation/index.js';
22
import react from '@vitejs/plugin-react';
3+
import fs from 'node:fs';
34
import { defineConfig, Plugin } from 'vite';
45

56
const SERVER_ENTRY = 'server-entry';
@@ -42,33 +43,37 @@ export default defineConfig({
4243
'node:module',
4344
'isolated-vm',
4445
'next/navigation',
45-
'lru-cache',
4646
],
4747
output: {
4848
globals: {
4949
react: 'react',
5050
'react-dom': 'ReactDOM',
5151
'react/jsx-runtime': 'react/jsx-runtime',
5252
},
53+
preserveModules: true,
5354
minifyInternalExports: false,
54-
manualChunks(id, { getModuleIds, getModuleInfo }) {
55-
const moduleInfo = getModuleInfo(id);
56-
57-
/**
58-
* We make sure any code used by the server entry is bundled into it,
59-
* so that it doesn't get marked with `use client`.
60-
*/
61-
if (
62-
moduleInfo?.importers.some((x) => x.includes('server-index.ts'))
63-
) {
64-
return SERVER_ENTRY;
65-
}
66-
},
55+
/**
56+
* preserve directives from the original file
57+
*/
6758
banner(chunk) {
68-
if (chunk.name === BLOCKS_EXPORTS_ENTRY) {
69-
return "'use client';";
59+
const filePath = chunk.facadeModuleId;
60+
if (chunk.importedBindings?.['react']?.includes('createContext')) {
61+
return "'use client';\n";
62+
} else if (filePath) {
63+
const content = fs.readFileSync(filePath, 'utf-8');
64+
const DIRECTIVES = [
65+
"'use client'",
66+
'"use client"',
67+
'"use server"',
68+
"'use server'",
69+
];
70+
const directive = DIRECTIVES.find((directive) =>
71+
content.startsWith(directive)
72+
);
73+
if (directive) {
74+
return directive + '\n';
75+
}
7076
}
71-
7277
return '';
7378
},
7479
},

packages/sdks/overrides/rsc/src/helpers/preview-lru-cache/helpers.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,40 @@ export const getIdFromSearchParams = (searchParams: URLSearchParams) => {
1010

1111
return previewedId;
1212
};
13+
14+
export class LRUCache<K, V> {
15+
private capacity: number;
16+
private cache: Map<K, V>;
17+
18+
constructor({ capacity }: { capacity: number }) {
19+
this.capacity = capacity;
20+
this.cache = new Map<K, V>();
21+
}
22+
23+
get(key: K): V | undefined {
24+
if (!this.cache.has(key)) {
25+
return undefined;
26+
}
27+
28+
// Remove and re-insert the key-value pair to move it to the end (most recently used)
29+
const value = this.cache.get(key)!;
30+
this.cache.delete(key);
31+
this.cache.set(key, value);
32+
33+
return value;
34+
}
35+
36+
set(key: K, value: V): void {
37+
if (this.cache.has(key)) {
38+
// If the key exists, remove it first
39+
this.cache.delete(key);
40+
} else if (this.cache.size >= this.capacity) {
41+
// If the cache is at capacity, remove the least recently used item (first item)
42+
const firstKey = this.cache.keys().next().value;
43+
this.cache.delete(firstKey);
44+
}
45+
46+
// Add the new key-value pair
47+
this.cache.set(key, value);
48+
}
49+
}
Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
1-
/**
2-
* This is an LRU cache to hold preview content on the server-side.
3-
*
4-
* Note: This logic is only used by the NextJS SDK.
5-
*/
6-
7-
import { LRUCache } from 'lru-cache';
1+
import { LRUCache } from './helpers.js';
82
import type { GlobalWCache } from './types.js';
93

104
export function init() {
115
if (!(globalThis as GlobalWCache)._BUILDER_PREVIEW_LRU_CACHE) {
126
(globalThis as GlobalWCache)._BUILDER_PREVIEW_LRU_CACHE = new LRUCache({
13-
max: 500,
14-
// how long to live in ms
15-
ttl: 1000 * 60 * 5,
7+
capacity: 500,
168
});
179
}
1810
}

packages/sdks/overrides/rsc/src/helpers/preview-lru-cache/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Note: This logic is only used by the NextJS SDK.
55
*/
66

7-
import type { LRUCache } from 'lru-cache';
7+
import type { LRUCache } from './helpers.js';
88
import type { BuilderContent } from '../../types/builder-content.js';
99

1010
type BuilderLRUCache = LRUCache<string, BuilderContent>;

yarn.lock

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4009,7 +4009,6 @@ __metadata:
40094009
"@builder.io/sdks": "workspace:*"
40104010
"@vitejs/plugin-react": ^4.0.4
40114011
isolated-vm: ^5.0.0
4012-
lru-cache: ^10.0.0
40134012
next: ">=13.4.20"
40144013
react: ^18.2.0
40154014
typescript: ^5.1.6
@@ -35291,7 +35290,7 @@ __metadata:
3529135290
languageName: node
3529235291
linkType: hard
3529335292

35294-
"lru-cache@npm:^10.0.0, lru-cache@npm:^10.0.1, lru-cache@npm:^10.0.2, lru-cache@npm:^9.1.1 || ^10.0.0":
35293+
"lru-cache@npm:^10.0.1, lru-cache@npm:^10.0.2, lru-cache@npm:^9.1.1 || ^10.0.0":
3529535294
version: 10.1.0
3529635295
resolution: "lru-cache@npm:10.1.0"
3529735296
checksum: 58056d33e2500fbedce92f8c542e7c11b50d7d086578f14b7074d8c241422004af0718e08a6eaae8705cee09c77e39a61c1c79e9370ba689b7010c152e6a76ab

0 commit comments

Comments
 (0)