Skip to content
Open
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
8 changes: 8 additions & 0 deletions .changeset/sharp-laws-feel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@workflow/sveltekit": patch
"workflow": patch
"@workflow/rollup": patch
"@workflow/nitro": patch
---

Refactor to use @workflow/rollup package
2 changes: 1 addition & 1 deletion packages/builders/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@workflow/tsconfig": "workspace:*"
},
"dependencies": {
"@swc/core": "1.11.24",
"@swc/core": "catalog:",
"@workflow/swc-plugin": "workspace:*",
"@workflow/errors": "workspace:*",
"@workflow/core": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"dependencies": {
"@oclif/core": "4.0.0",
"@oclif/plugin-help": "6.2.31",
"@swc/core": "1.11.24",
"@swc/core": "catalog:",
"@workflow/builders": "workspace:*",
"@workflow/swc-plugin": "workspace:*",
"@workflow/errors": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"clean": "tsc --build --clean && rm -rf dist"
},
"dependencies": {
"@swc/core": "1.11.24",
"@swc/core": "catalog:",
"@workflow/builders": "workspace:*",
"@workflow/core": "workspace:*",
"@workflow/swc-plugin": "workspace:*",
Expand Down
4 changes: 2 additions & 2 deletions packages/nitro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
},
"exports": {
".": "./dist/index.js",
"./rollup": "./dist/rollup.js",
"./vite": "./dist/vite.js"
},
"scripts": {
Expand All @@ -27,10 +26,11 @@
"clean": "tsc --build --clean && rm -rf dist"
},
"dependencies": {
"@swc/core": "1.11.24",
"@swc/core": "catalog:",
"@workflow/swc-plugin": "workspace:*",
"@workflow/builders": "workspace:*",
"@workflow/core": "workspace:*",
"@workflow/rollup": "workspace:*",
"exsolve": "1.0.7",
"pathe": "2.0.3"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/nitro/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Nitro, NitroModule, RollupConfig } from 'nitro/types';
import { join } from 'pathe';
import { LocalBuilder, VercelBuilder } from './builders.js';
import { workflowRollupPlugin } from './rollup.js';
import { workflowTransformPlugin } from '@workflow/rollup';
import type { ModuleOptions } from './types';

export type { ModuleOptions };
Expand All @@ -14,7 +14,7 @@ export default {

// Add transform plugin
nitro.hooks.hook('rollup:before', (_nitro: Nitro, config: RollupConfig) => {
(config.plugins as Array<unknown>).push(workflowRollupPlugin());
(config.plugins as Array<unknown>).push(workflowTransformPlugin());
});

// NOTE: Temporary workaround for debug unenv mock
Expand Down
4 changes: 2 additions & 2 deletions packages/nitro/src/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { LocalBuilder } from './builders.js';
import type { Plugin as VitePlugin } from 'vite';
import type { ModuleOptions } from './index.js';
import nitroModule from './index.js';
import { workflowRollupPlugin } from './rollup.js';
import { workflowTransformPlugin } from '@workflow/rollup';

export function workflow(options?: ModuleOptions): Plugin[] {
let builder: LocalBuilder | undefined;

return [
workflowRollupPlugin() as VitePlugin,
workflowTransformPlugin() as VitePlugin,
{
name: 'workflow:nitro',
nitro: {
Expand Down
1 change: 1 addition & 0 deletions packages/rollup/LICENSE.md
3 changes: 3 additions & 0 deletions packages/rollup/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @workflow/rollup

Rollup plugin for the Workflow DevKit.
37 changes: 37 additions & 0 deletions packages/rollup/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@workflow/rollup",
"version": "4.0.0-beta.1",
"description": "Rollup plugin for Workflow DevKit",
"type": "module",
"main": "dist/index.js",
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/vercel/workflow.git",
"directory": "packages/rollup"
},
"exports": {
".": "./dist/index.js"
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"clean": "tsc --build --clean && rm -rf dist"
},
"dependencies": {
"@swc/core": "catalog:",
"@workflow/swc-plugin": "workspace:*",
"exsolve": "1.0.7"
},
"devDependencies": {
"@types/node": "catalog:",
"@workflow/tsconfig": "workspace:*",
"rollup": "^4.53.2"
}
}
13 changes: 3 additions & 10 deletions packages/nitro/src/rollup.ts → packages/rollup/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import { relative } from 'node:path';
import { transform } from '@swc/core';
import { resolveModulePath } from 'exsolve';
import type { RollupConfig } from 'nitro/types';
import type { Plugin } from 'rollup';

type RollupPlugin = Exclude<
RollupConfig['plugins'],
undefined | void | null | false | Promise<unknown> | Array<unknown>
>;

// https://github.com/vercel/workflow/blob/feat/nitro/packages/next/src/loader.ts

export function workflowRollupPlugin(): RollupPlugin {
export function workflowTransformPlugin(): Plugin {
return {
name: 'workflow:transform',
// This transform applies the "use workflow"/"use step"
Expand Down Expand Up @@ -41,7 +34,7 @@ export function workflowRollupPlugin(): RollupPlugin {
const lowerPath = normalizedFilepath.toLowerCase();

let relativeFilename: string;
if (lowerPath.startsWith(lowerWd + '/')) {
if (lowerPath.startsWith(`${lowerWd}/`)) {
// File is under working directory - manually calculate relative path
relativeFilename = normalizedFilepath.substring(
normalizedWorkingDir.length + 1
Expand Down
12 changes: 12 additions & 0 deletions packages/rollup/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "@workflow/tsconfig/base.json",
"compilerOptions": {
"outDir": "dist",
"target": "es2022",
"module": "preserve",
"baseUrl": ".",
"moduleResolution": "bundler"
},
"include": ["src"],
"exclude": ["node_modules", "**/*.test.ts"]
}
3 changes: 2 additions & 1 deletion packages/sveltekit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@
"clean": "tsc --build --clean && rm -rf dist"
},
"dependencies": {
"@swc/core": "1.11.24",
"@swc/core": "catalog:",
"@workflow/builders": "workspace:*",
"@workflow/swc-plugin": "workspace:*",
"@workflow/rollup": "workspace:*",
"fs-extra": "^11.3.2",
"exsolve": "^1.0.7",
"pathe": "^2.0.3"
Expand Down
180 changes: 53 additions & 127 deletions packages/sveltekit/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,150 +1,76 @@
import { relative } from 'node:path';
import { transform } from '@swc/core';
import { resolveModulePath } from 'exsolve';
import type { HotUpdateOptions, Plugin } from 'vite';
import { SvelteKitBuilder } from './builder.js';
import { workflowTransformPlugin } from '@workflow/rollup';

export function workflowPlugin(): Plugin {
export function workflowPlugin(): Plugin[] {
let builder: SvelteKitBuilder;

return {
name: 'workflow:sveltekit',
return [
workflowTransformPlugin(),
{
name: 'workflow:sveltekit',

// TODO: Move this to @workflow/vite or something since this is vite specific
// Transform workflow files with SWC
async transform(code: string, id: string) {
// Only apply the transform if file needs it
if (!code.match(/(use step|use workflow)/)) {
return null;
}
configResolved() {
builder = new SvelteKitBuilder();
},

const isTypeScript = id.endsWith('.ts') || id.endsWith('.tsx');
const isTsx = id.endsWith('.tsx');
// TODO: Move this to @workflow/vite or something since this is vite specific
async hotUpdate(options: HotUpdateOptions) {
const { file, server, read } = options;

const swcPlugin = resolveModulePath('@workflow/swc-plugin', {
from: [import.meta.url],
});

// Calculate relative filename for SWC plugin
// The SWC plugin uses filename to generate workflowId, so it must be relative
const workingDir = process.cwd();
const normalizedWorkingDir = workingDir
.replace(/\\/g, '/')
.replace(/\/$/, '');
const normalizedFilepath = id.replace(/\\/g, '/');

// Windows fix: Use case-insensitive comparison to work around drive letter casing issues
const lowerWd = normalizedWorkingDir.toLowerCase();
const lowerPath = normalizedFilepath.toLowerCase();

let relativeFilename: string;
if (lowerPath.startsWith(lowerWd + '/')) {
// File is under working directory - manually calculate relative path
relativeFilename = normalizedFilepath.substring(
normalizedWorkingDir.length + 1
);
} else if (lowerPath === lowerWd) {
// File IS the working directory (shouldn't happen)
relativeFilename = '.';
} else {
// Use relative() for files outside working directory
relativeFilename = relative(workingDir, id).replace(/\\/g, '/');

if (relativeFilename.startsWith('../')) {
relativeFilename = relativeFilename
.split('/')
.filter((part) => part !== '..')
.join('/');
// Check if this is a TS/JS file that might contain workflow directives
const jsTsRegex = /\.(ts|tsx|js|jsx|mjs|cjs)$/;
if (!jsTsRegex.test(file)) {
return;
}
}

// Final safety check - ensure we never pass an absolute path to SWC
if (relativeFilename.includes(':') || relativeFilename.startsWith('/')) {
// This should rarely happen, but use filename split as last resort
relativeFilename = normalizedFilepath.split('/').pop() || 'unknown.ts';
}

// Transform with SWC
const result = await transform(code, {
filename: relativeFilename,
jsc: {
parser: {
syntax: isTypeScript ? 'typescript' : 'ecmascript',
tsx: isTsx,
},
target: 'es2022',
experimental: {
plugins: [[swcPlugin, { mode: 'client' }]],
},
},
minify: false,
sourceMaps: true,
inlineSourcesContent: true,
});

return {
code: result.code,
map: result.map ? JSON.parse(result.map) : null,
};
},

configResolved() {
builder = new SvelteKitBuilder();
},
// Read the file to check for workflow/step directives
let content: string;
try {
content = await read();
} catch {
// File might have been deleted - trigger rebuild to update generated routes
console.log('Workflow file deleted, regenerating routes...');
try {
await builder.build();
} catch (buildError) {
// Build might fail if files are being deleted during test cleanup
// Log but don't crash - the next successful change will trigger a rebuild
console.error('Build failed during file deletion:', buildError);
}
return;
}

// TODO: Move this to @workflow/vite or something since this is vite specific
async hotUpdate(options: HotUpdateOptions) {
const { file, server, read } = options;
const useWorkflowPattern = /^\s*(['"])use workflow\1;?\s*$/m;
const useStepPattern = /^\s*(['"])use step\1;?\s*$/m;

// Check if this is a TS/JS file that might contain workflow directives
const jsTsRegex = /\.(ts|tsx|js|jsx|mjs|cjs)$/;
if (!jsTsRegex.test(file)) {
return;
}
if (
!useWorkflowPattern.test(content) &&
!useStepPattern.test(content)
) {
return;
}

// Read the file to check for workflow/step directives
let content: string;
try {
content = await read();
} catch {
// File might have been deleted - trigger rebuild to update generated routes
console.log('Workflow file deleted, regenerating routes...');
// Rebuild everything - simpler and more reliable than tracking individual files
console.log('Workflow file changed, regenerating routes...');
try {
await builder.build();
} catch (buildError) {
// Build might fail if files are being deleted during test cleanup
// Build might fail if files are being modified/deleted during test cleanup
// Log but don't crash - the next successful change will trigger a rebuild
console.error('Build failed during file deletion:', buildError);
console.error('Build failed during HMR:', buildError);
return;
}
return;
}

const useWorkflowPattern = /^\s*(['"])use workflow\1;?\s*$/m;
const useStepPattern = /^\s*(['"])use step\1;?\s*$/m;
// Trigger full reload of workflow routes
server.ws.send({
type: 'full-reload',
path: '*',
});

if (!useWorkflowPattern.test(content) && !useStepPattern.test(content)) {
// Let Vite handle the normal HMR for the changed file
return;
}

// Rebuild everything - simpler and more reliable than tracking individual files
console.log('Workflow file changed, regenerating routes...');
try {
await builder.build();
} catch (buildError) {
// Build might fail if files are being modified/deleted during test cleanup
// Log but don't crash - the next successful change will trigger a rebuild
console.error('Build failed during HMR:', buildError);
return;
}

// Trigger full reload of workflow routes
server.ws.send({
type: 'full-reload',
path: '*',
});

// Let Vite handle the normal HMR for the changed file
return;
},
},
};
];
}
Loading
Loading