Skip to content

Commit 7c50e30

Browse files
authored
fix next.js cache (#43)
* fix the cache for next.js * add changeset
1 parent 34bfe1c commit 7c50e30

File tree

9 files changed

+271
-34
lines changed

9 files changed

+271
-34
lines changed

.changeset/lovely-ducks-join.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@navita/webpack-plugin": minor
3+
"@navita/next-plugin": minor
4+
---
5+
6+
Added functionality to disable the usage of webpacks cache via the plugins constructor. Added a custom cache solution for next.js that uses a single text file to store the cache between compilations.

examples/with-next-app-dir/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"name": "with-next-app-dir",
44
"version": "0.0.0",
55
"scripts": {
6-
"dev": "rimraf .next && next dev",
7-
"build": "rimraf .next && next build",
6+
"dev": "next dev",
7+
"build": "next build",
88
"start": "next start",
99
"lint": "next lint"
1010
},

examples/with-next-app-dir/src/app/edge/page.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { style } from "@navita/css";
22

33
const y = style({
4-
background: 'dimgray',
4+
background: 'hotpink',
5+
color: 'white',
6+
padding: 20,
57
});
68

79
export default function Edge() {

examples/with-next-app-dir/src/app/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { style } from "@navita/css";
22

33
const container = style({
4-
background: 'royalblue',
5-
color: 'white',
4+
background: 'orange',
5+
color: 'black',
66
fontSize: '2rem',
77
padding: '1rem',
88
});

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"jest": "workspace:*",
2727
"prettier": "latest",
2828
"turbo": "^1.10.7",
29-
"typescript": "5.1.3"
29+
"typescript": "5.4.5"
3030
},
3131
"devDependencies": {
3232
"@types/node": "^18.14.0"

packages/next-plugin/src/index.ts

+58-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import type { Options } from "@navita/webpack-plugin";
1+
import * as fs from "fs";
2+
import path from "node:path";
3+
import type { Options, Renderer } from "@navita/webpack-plugin";
24
import { getNavitaModule, NavitaPlugin, NAVITA_MODULE_TYPE } from "@navita/webpack-plugin";
35
import type MiniCssExtractPluginType from "mini-css-extract-plugin";
46
import type { NextConfig } from "next";
@@ -8,11 +10,12 @@ import { findPagesDir } from "next/dist/lib/find-pages-dir";
810
import type { Configuration } from "webpack";
911
import { optimizeCSSOutput } from "./optimizeCSSOutput";
1012

11-
const MiniCssExtractPlugin = NextMiniCssExtractPluginDefault['default'] as typeof MiniCssExtractPluginType;
13+
let renderer: Renderer;
14+
let lastCache: string;
1215

13-
type WebpackOptions = Options;
16+
const MiniCssExtractPlugin = NextMiniCssExtractPluginDefault['default'] as typeof MiniCssExtractPluginType;
1417

15-
interface Config extends WebpackOptions {
18+
interface Config extends Omit<Options, 'useWebpackCache' | 'onRenderInitialized'> {
1619
singleCssFile?: boolean;
1720
}
1821

@@ -94,8 +97,59 @@ export const createNavitaStylePlugin = (navitaConfig: Config = {}) =>
9497
};
9598
}
9699

100+
// Next.js creates at least three webpack instances. We can't rely on the webpack cache.
101+
const { cache, mode } = config;
102+
103+
const cacheDirectory = (
104+
typeof cache !== "boolean" && cache.type === "filesystem" ?
105+
path.resolve(cache.cacheDirectory, `navita-${mode}`) :
106+
undefined
107+
);
108+
109+
const cacheDestination = path.resolve(cacheDirectory, 'data.txt');
110+
111+
const onRenderInitialized = async (createdRenderer: Renderer) => {
112+
renderer = createdRenderer;
113+
114+
try {
115+
// Ensure the cache directory exists:
116+
await fs.promises.mkdir(cacheDirectory, { recursive: true });
117+
118+
const content = await fs.promises.readFile(cacheDestination, 'utf-8');
119+
120+
await renderer.engine.deserialize(content);
121+
122+
lastCache = renderer.engine.serialize();
123+
} catch {
124+
// This will happen if the user doesn't have write access to the cache directory.
125+
// But the same should happen with the webpack cache.
126+
}
127+
};
128+
129+
config.plugins?.push({
130+
apply(compiler) {
131+
compiler.hooks.afterEmit.tapPromise(`${NavitaPlugin.pluginName}-nextjs-custom-cache`, async () => {
132+
if (!renderer) {
133+
return;
134+
}
135+
136+
const newCache = renderer.engine.serialize();
137+
138+
if (newCache === lastCache) {
139+
return;
140+
}
141+
142+
lastCache = newCache;
143+
144+
await fs.promises.writeFile(cacheDestination, newCache);
145+
});
146+
}
147+
});
148+
97149
config.plugins?.push(
98150
new NavitaPlugin({
151+
useWebpackCache: false,
152+
onRenderInitialized,
99153
outputCss,
100154
...navitaConfig,
101155
optimizeCSSOutput,

packages/webpack-plugin/src/index.ts

+22-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { prepareCssOutput } from "./prepareCssOutput";
1414

1515
export { getNavitaDependency, getNavitaModule, NAVITA_MODULE_TYPE };
1616
export { CSSOutput, Engine, Compilation, UsedIdCache };
17+
export type { Renderer };
1718

1819
type MiniCSSExtractPlugin = {
1920
options: {
@@ -28,10 +29,19 @@ export interface Options {
2829
importMap?: ImportMap;
2930
optimizeCSSOutput?: (output: CSSOutput, compilation?: Compilation, engine?: Engine) => CSSOutput;
3031
engineOptions?: EngineOptions;
32+
onRenderInitialized?: (renderer: Renderer) => Promise<void>;
33+
/**
34+
* This uses webpacks cache to store the engine state between builds.
35+
* If you have more than one webpack instance running at the same time,
36+
* you should disable this option, and instead do something similar to
37+
* what is done in the `next-plugin` package.
38+
*/
39+
useWebpackCache?: boolean;
3140
}
3241

3342
const defaultOptions: Options = {
3443
outputCss: true,
44+
useWebpackCache: true,
3545
exclude: /node_modules/,
3646
importMap: [],
3747
};
@@ -63,6 +73,8 @@ export class NavitaPlugin {
6373
outputCss,
6474
optimizeCSSOutput,
6575
engineOptions,
76+
useWebpackCache,
77+
onRenderInitialized,
6678
} = this.options;
6779

6880
const importMap = [
@@ -72,8 +84,6 @@ export class NavitaPlugin {
7284

7385
const dev = compiler.options.mode !== "production";
7486

75-
const cacheName = `${NavitaPlugin.pluginName}-${compiler.options.mode}`;
76-
7787
const defaultEngineOptions = {
7888
enableSourceMaps: dev,
7989
enableDebugIdentifiers: dev,
@@ -116,6 +126,16 @@ export class NavitaPlugin {
116126
}
117127
});
118128

129+
if (onRenderInitialized) {
130+
await onRenderInitialized(renderer);
131+
}
132+
133+
if (!useWebpackCache) {
134+
return;
135+
}
136+
137+
const cacheName = `${NavitaPlugin.pluginName}-${compiler.options.mode}`;
138+
119139
const result = await compilation
120140
.getCache(cacheName)
121141
.getPromise<Buffer>(NavitaPlugin.pluginName, cacheKey);

0 commit comments

Comments
 (0)