Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shy-mice-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@workflow/next": patch
---

Add Next.js cache invalidation on swc transform change
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ node_modules
target
# compiled wasm
*.wasm
# swc-plugin build hash
packages/swc-plugin-workflow/build-hash.json

# SWC plugin cache
.swc
Expand Down
9 changes: 9 additions & 0 deletions packages/next/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { NextConfig } from 'next';
import path from 'path';
import semver from 'semver';
import { getNextBuilder } from './builder.js';
import { maybeInvalidateCacheOnSwcChange } from './swc-cache.js';

/**
* Default directories to scan for workflows and steps.
Expand Down Expand Up @@ -136,6 +138,13 @@ export function withWorkflow(
!process.env.WORKFLOW_NEXT_PRIVATE_BUILT &&
phase !== 'phase-production-server'
) {
// Check swc-plugin build hash and invalidate cache if changed
const distDir = path.resolve(
Comment thread
ijjk marked this conversation as resolved.
process.cwd(),
nextConfig.distDir || '.next'
);
maybeInvalidateCacheOnSwcChange(distDir);

const shouldWatch = process.env.NODE_ENV === 'development';
const NextBuilder = await getNextBuilder();
const workflowBuilder = new NextBuilder({
Expand Down
51 changes: 51 additions & 0 deletions packages/next/src/swc-cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import fs from 'fs';
import path from 'path';

/**
* Checks if the SWC plugin build has changed and invalidates the Next.js cache if needed.
* Also registers an exit handler to write the current build hash for future comparisons.
*
* @param distDir - The Next.js dist directory (e.g., '.next')
*/
export function maybeInvalidateCacheOnSwcChange(distDir: string): void {
const cacheDir = path.join(distDir, 'cache');
const devCacheDir = path.join(distDir, 'dev', 'cache');
const workflowJsonPath = path.join(cacheDir, 'workflow.json');
const swcPluginBuildHash = require('@workflow/swc-plugin/build-hash.json')
.buildHash as string;

let shouldInvalidateCache = false;
try {
const existing = JSON.parse(fs.readFileSync(workflowJsonPath, 'utf-8'));
if (existing.swcPluginBuildHash !== swcPluginBuildHash) {
shouldInvalidateCache = true;
}
} catch {
// File doesn't exist or is invalid
shouldInvalidateCache = true;
}

if (shouldInvalidateCache) {
console.log('workflow transform upgraded, invalidating Next.js cache');
// Delete cache directories
const cacheDirs = [cacheDir, devCacheDir];
for (const dir of cacheDirs) {
if (fs.existsSync(dir)) {
fs.rmSync(dir, { recursive: true, force: true });
}
}
}

// Write workflow.json lazily on process exit
process.on('exit', () => {
try {
fs.mkdirSync(cacheDir, { recursive: true });
fs.writeFileSync(
workflowJsonPath,
JSON.stringify({ swcPluginBuildHash }, null, 2)
);
} catch {
// Ignore errors on exit
}
});
}
20 changes: 19 additions & 1 deletion packages/swc-plugin-workflow/build.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { createHash } from 'node:crypto';
import { execSync } from 'node:child_process';
import { copyFileSync, existsSync, readdirSync } from 'node:fs';
import {
copyFileSync,
existsSync,
readdirSync,
readFileSync,
writeFileSync,
} from 'node:fs';

function runCommand(command) {
try {
Expand Down Expand Up @@ -116,4 +123,15 @@ if (!existsSync(wasmSource)) {
console.log('Copying WASM file...');
copyFileSync(wasmSource, wasmDest);

// Generate hash of the WASM file for cache invalidation
console.log('Generating build hash...');
const wasmContent = readFileSync(wasmDest);
const buildHash = createHash('sha256')
.update(wasmContent)
.digest('hex')
.slice(0, 16);
const buildHashPath = new URL('build-hash.json', import.meta.url);
writeFileSync(buildHashPath, JSON.stringify({ buildHash }, null, 2));
console.log(`Build hash: ${buildHash}`);

console.log('Build complete!');
7 changes: 5 additions & 2 deletions packages/swc-plugin-workflow/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"type": "module",
"main": "swc_plugin_workflow.wasm",
"files": [
"swc_plugin_workflow.wasm"
"swc_plugin_workflow.wasm",
"build-hash.json"
],
"publishConfig": {
"access": "public"
Expand All @@ -17,7 +18,9 @@
"directory": "packages/swc-plugin-workflow"
},
"exports": {
".": "./swc_plugin_workflow.wasm"
".": "./swc_plugin_workflow.wasm",
"./package.json": "./package.json",
"./build-hash.json": "./build-hash.json"
},
"peerDependencies": {
"@swc/core": "catalog:"
Expand Down
2 changes: 1 addition & 1 deletion packages/swc-plugin-workflow/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"extends": ["//"],
"tasks": {
"build": {
"outputs": ["swc_plugin_workflow.wasm"],
"outputs": ["swc_plugin_workflow.wasm", "build-hash.json"],
"env": ["RUSTUP_HOME", "CARGO_HOME"]
}
}
Expand Down
Loading