Skip to content

Commit

Permalink
fix next.js cache (#43)
Browse files Browse the repository at this point in the history
* fix the cache for next.js

* add changeset
  • Loading branch information
Zn4rK authored Apr 10, 2024
1 parent 34bfe1c commit 7c50e30
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 34 deletions.
6 changes: 6 additions & 0 deletions .changeset/lovely-ducks-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@navita/webpack-plugin": minor
"@navita/next-plugin": minor
---

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.
4 changes: 2 additions & 2 deletions examples/with-next-app-dir/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"name": "with-next-app-dir",
"version": "0.0.0",
"scripts": {
"dev": "rimraf .next && next dev",
"build": "rimraf .next && next build",
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
Expand Down
4 changes: 3 additions & 1 deletion examples/with-next-app-dir/src/app/edge/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { style } from "@navita/css";

const y = style({
background: 'dimgray',
background: 'hotpink',
color: 'white',
padding: 20,
});

export default function Edge() {
Expand Down
4 changes: 2 additions & 2 deletions examples/with-next-app-dir/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { style } from "@navita/css";

const container = style({
background: 'royalblue',
color: 'white',
background: 'orange',
color: 'black',
fontSize: '2rem',
padding: '1rem',
});
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"jest": "workspace:*",
"prettier": "latest",
"turbo": "^1.10.7",
"typescript": "5.1.3"
"typescript": "5.4.5"
},
"devDependencies": {
"@types/node": "^18.14.0"
Expand Down
62 changes: 58 additions & 4 deletions packages/next-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { Options } from "@navita/webpack-plugin";
import * as fs from "fs";
import path from "node:path";
import type { Options, Renderer } from "@navita/webpack-plugin";
import { getNavitaModule, NavitaPlugin, NAVITA_MODULE_TYPE } from "@navita/webpack-plugin";
import type MiniCssExtractPluginType from "mini-css-extract-plugin";
import type { NextConfig } from "next";
Expand All @@ -8,11 +10,12 @@ import { findPagesDir } from "next/dist/lib/find-pages-dir";
import type { Configuration } from "webpack";
import { optimizeCSSOutput } from "./optimizeCSSOutput";

const MiniCssExtractPlugin = NextMiniCssExtractPluginDefault['default'] as typeof MiniCssExtractPluginType;
let renderer: Renderer;
let lastCache: string;

type WebpackOptions = Options;
const MiniCssExtractPlugin = NextMiniCssExtractPluginDefault['default'] as typeof MiniCssExtractPluginType;

interface Config extends WebpackOptions {
interface Config extends Omit<Options, 'useWebpackCache' | 'onRenderInitialized'> {
singleCssFile?: boolean;
}

Expand Down Expand Up @@ -94,8 +97,59 @@ export const createNavitaStylePlugin = (navitaConfig: Config = {}) =>
};
}

// Next.js creates at least three webpack instances. We can't rely on the webpack cache.
const { cache, mode } = config;

const cacheDirectory = (
typeof cache !== "boolean" && cache.type === "filesystem" ?
path.resolve(cache.cacheDirectory, `navita-${mode}`) :
undefined
);

const cacheDestination = path.resolve(cacheDirectory, 'data.txt');

const onRenderInitialized = async (createdRenderer: Renderer) => {
renderer = createdRenderer;

try {
// Ensure the cache directory exists:
await fs.promises.mkdir(cacheDirectory, { recursive: true });

const content = await fs.promises.readFile(cacheDestination, 'utf-8');

await renderer.engine.deserialize(content);

lastCache = renderer.engine.serialize();
} catch {
// This will happen if the user doesn't have write access to the cache directory.
// But the same should happen with the webpack cache.
}
};

config.plugins?.push({
apply(compiler) {
compiler.hooks.afterEmit.tapPromise(`${NavitaPlugin.pluginName}-nextjs-custom-cache`, async () => {
if (!renderer) {
return;
}

const newCache = renderer.engine.serialize();

if (newCache === lastCache) {
return;
}

lastCache = newCache;

await fs.promises.writeFile(cacheDestination, newCache);
});
}
});

config.plugins?.push(
new NavitaPlugin({
useWebpackCache: false,
onRenderInitialized,
outputCss,
...navitaConfig,
optimizeCSSOutput,
Expand Down
24 changes: 22 additions & 2 deletions packages/webpack-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { prepareCssOutput } from "./prepareCssOutput";

export { getNavitaDependency, getNavitaModule, NAVITA_MODULE_TYPE };
export { CSSOutput, Engine, Compilation, UsedIdCache };
export type { Renderer };

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

const defaultOptions: Options = {
outputCss: true,
useWebpackCache: true,
exclude: /node_modules/,
importMap: [],
};
Expand Down Expand Up @@ -63,6 +73,8 @@ export class NavitaPlugin {
outputCss,
optimizeCSSOutput,
engineOptions,
useWebpackCache,
onRenderInitialized,
} = this.options;

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

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

const cacheName = `${NavitaPlugin.pluginName}-${compiler.options.mode}`;

const defaultEngineOptions = {
enableSourceMaps: dev,
enableDebugIdentifiers: dev,
Expand Down Expand Up @@ -116,6 +126,16 @@ export class NavitaPlugin {
}
});

if (onRenderInitialized) {
await onRenderInitialized(renderer);
}

if (!useWebpackCache) {
return;
}

const cacheName = `${NavitaPlugin.pluginName}-${compiler.options.mode}`;

const result = await compilation
.getCache(cacheName)
.getPromise<Buffer>(NavitaPlugin.pluginName, cacheKey);
Expand Down
Loading

0 comments on commit 7c50e30

Please sign in to comment.