Skip to content

Commit 0ee12d9

Browse files
pranaygpadriandlam
authored andcommitted
Normalize Workbenches
Normalize trigger scripts across workbenches fix: include hono in local build test test: include src dir for test test: add workflow dir config in test to fix sveltekit dev tests add temp 7_full in example wokrflow format fix(sveltekit): detecting workflow folders and customizable dir Remove 7_full and 1_simple error replace API symlink in webpack workbench Fix sveltekit and vite tests Fix sveltekit symlinks Test fixes Fix sveltekit workflows path Dont symlink routes in vite Include e2e tests for hono and vite fix error tests post normalization wip - attempted fixes
1 parent 4b70739 commit 0ee12d9

File tree

7 files changed

+155
-124
lines changed

7 files changed

+155
-124
lines changed

packages/sveltekit/src/builder.ts

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { constants } from 'node:fs';
2-
import { access, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
3-
import { join, resolve } from 'node:path';
4-
import { BaseBuilder, type SvelteKitConfig } from '@workflow/builders';
1+
import { constants } from "node:fs";
2+
import { access, mkdir, readFile, stat, writeFile } from "node:fs/promises";
3+
import { join, resolve } from "node:path";
4+
import { BaseBuilder, type SvelteKitConfig } from "@workflow/builders";
55

66
// Helper function code for converting SvelteKit requests to standard Request objects
77
const SVELTEKIT_REQUEST_CONVERTER = `
@@ -23,26 +23,26 @@ export class SvelteKitBuilder extends BaseBuilder {
2323

2424
super({
2525
...config,
26-
dirs: ['workflows', 'src/workflows', 'routes', 'src/routes'],
27-
buildTarget: 'sveltekit' as const,
28-
stepsBundlePath: '', // unused in base
29-
workflowsBundlePath: '', // unused in base
30-
webhookBundlePath: '', // unused in base
26+
dirs: ["workflows", "src/workflows", "routes", "src/routes"],
27+
buildTarget: "sveltekit" as const,
28+
stepsBundlePath: "", // unused in base
29+
workflowsBundlePath: "", // unused in base
30+
webhookBundlePath: "", // unused in base
3131
workingDir,
3232
});
3333
}
3434

3535
override async build(): Promise<void> {
3636
// Find SvelteKit routes directory (src/routes or routes)
3737
const routesDir = await this.findRoutesDirectory();
38-
const workflowGeneratedDir = join(routesDir, '.well-known/workflow/v1');
38+
const workflowGeneratedDir = join(routesDir, ".well-known/workflow/v1");
3939

4040
// Ensure output directories exist
4141
await mkdir(workflowGeneratedDir, { recursive: true });
4242

4343
// Add .gitignore to exclude generated files from version control
4444
if (process.env.VERCEL_DEPLOYMENT_ID === undefined) {
45-
await writeFile(join(workflowGeneratedDir, '.gitignore'), '*');
45+
await writeFile(join(workflowGeneratedDir, ".gitignore"), "*");
4646
}
4747

4848
// Get workflow and step files to bundle
@@ -74,21 +74,21 @@ export class SvelteKitBuilder extends BaseBuilder {
7474
tsPaths?: Record<string, string[]>;
7575
}) {
7676
// Create steps route: .well-known/workflow/v1/step/+server.js
77-
const stepsRouteDir = join(workflowGeneratedDir, 'step');
77+
const stepsRouteDir = join(workflowGeneratedDir, "step");
7878
await mkdir(stepsRouteDir, { recursive: true });
7979

8080
await this.createStepsBundle({
81-
format: 'esm',
81+
format: "esm",
8282
inputFiles,
83-
outfile: join(stepsRouteDir, '+server.js'),
83+
outfile: join(stepsRouteDir, "+server.js"),
8484
externalizeNonSteps: true,
8585
tsBaseUrl,
8686
tsPaths,
8787
});
8888

8989
// Post-process the generated file to wrap with SvelteKit request converter
90-
const stepsRouteFile = join(stepsRouteDir, '+server.js');
91-
let stepsRouteContent = await readFile(stepsRouteFile, 'utf-8');
90+
const stepsRouteFile = join(stepsRouteDir, "+server.js");
91+
let stepsRouteContent = await readFile(stepsRouteFile, "utf-8");
9292

9393
// Replace the default export with SvelteKit-compatible handler
9494
stepsRouteContent = stepsRouteContent.replace(
@@ -97,7 +97,7 @@ export class SvelteKitBuilder extends BaseBuilder {
9797
export const POST = async ({request}) => {
9898
const normalRequest = await convertSvelteKitRequest(request);
9999
return stepEntrypoint(normalRequest);
100-
}`
100+
}`,
101101
);
102102

103103
await writeFile(stepsRouteFile, stepsRouteContent);
@@ -115,21 +115,21 @@ export const POST = async ({request}) => {
115115
tsPaths?: Record<string, string[]>;
116116
}) {
117117
// Create workflows route: .well-known/workflow/v1/flow/+server.js
118-
const workflowsRouteDir = join(workflowGeneratedDir, 'flow');
118+
const workflowsRouteDir = join(workflowGeneratedDir, "flow");
119119
await mkdir(workflowsRouteDir, { recursive: true });
120120

121121
await this.createWorkflowsBundle({
122-
format: 'esm',
123-
outfile: join(workflowsRouteDir, '+server.js'),
122+
format: "esm",
123+
outfile: join(workflowsRouteDir, "+server.js"),
124124
bundleFinalOutput: false,
125125
inputFiles,
126126
tsBaseUrl,
127127
tsPaths,
128128
});
129129

130130
// Post-process the generated file to wrap with SvelteKit request converter
131-
const workflowsRouteFile = join(workflowsRouteDir, '+server.js');
132-
let workflowsRouteContent = await readFile(workflowsRouteFile, 'utf-8');
131+
const workflowsRouteFile = join(workflowsRouteDir, "+server.js");
132+
let workflowsRouteContent = await readFile(workflowsRouteFile, "utf-8");
133133

134134
// Replace the default export with SvelteKit-compatible handler
135135
workflowsRouteContent = workflowsRouteContent.replace(
@@ -138,7 +138,7 @@ export const POST = async ({request}) => {
138138
export const POST = async ({request}) => {
139139
const normalRequest = await convertSvelteKitRequest(request);
140140
return workflowEntrypoint(workflowCode)(normalRequest);
141-
}`
141+
}`,
142142
);
143143
await writeFile(workflowsRouteFile, workflowsRouteContent);
144144
}
@@ -151,7 +151,7 @@ export const POST = async ({request}) => {
151151
// Create webhook route: .well-known/workflow/v1/webhook/[token]/+server.js
152152
const webhookRouteFile = join(
153153
workflowGeneratedDir,
154-
'webhook/[token]/+server.js'
154+
"webhook/[token]/+server.js",
155155
);
156156

157157
await this.createWebhookBundle({
@@ -161,18 +161,18 @@ export const POST = async ({request}) => {
161161
});
162162

163163
// Post-process the generated file to wrap with SvelteKit request converter
164-
let webhookRouteContent = await readFile(webhookRouteFile, 'utf-8');
164+
let webhookRouteContent = await readFile(webhookRouteFile, "utf-8");
165165

166166
// Update handler signature to accept token as parameter
167167
webhookRouteContent = webhookRouteContent.replace(
168168
/async function handler\(request\) \{[\s\S]*?const token = decodeURIComponent\(pathParts\[pathParts\.length - 1\]\);/,
169-
`async function handler(request, token) {`
169+
`async function handler(request, token) {`,
170170
);
171171

172172
// Remove the URL parsing code since we get token from params
173173
webhookRouteContent = webhookRouteContent.replace(
174174
/const url = new URL\(request\.url\);[\s\S]*?const pathParts = url\.pathname\.split\('\/'\);[\s\S]*?\n/,
175-
''
175+
"",
176176
);
177177

178178
// Replace all HTTP method exports with SvelteKit-compatible handlers
@@ -191,15 +191,15 @@ export const PUT = createSvelteKitHandler('PUT');
191191
export const PATCH = createSvelteKitHandler('PATCH');
192192
export const DELETE = createSvelteKitHandler('DELETE');
193193
export const HEAD = createSvelteKitHandler('HEAD');
194-
export const OPTIONS = createSvelteKitHandler('OPTIONS');`
194+
export const OPTIONS = createSvelteKitHandler('OPTIONS');`,
195195
);
196196

197197
await writeFile(webhookRouteFile, webhookRouteContent);
198198
}
199199

200200
private async findRoutesDirectory(): Promise<string> {
201-
const routesDir = resolve(this.config.workingDir, 'src/routes');
202-
const rootRoutesDir = resolve(this.config.workingDir, 'routes');
201+
const routesDir = resolve(this.config.workingDir, "src/routes");
202+
const rootRoutesDir = resolve(this.config.workingDir, "routes");
203203

204204
// Try src/routes first (standard SvelteKit convention)
205205
try {
@@ -216,15 +216,36 @@ export const OPTIONS = createSvelteKitHandler('OPTIONS');`
216216
const rootRoutesStats = await stat(rootRoutesDir);
217217
if (!rootRoutesStats.isDirectory()) {
218218
throw new Error(
219-
`Path exists but is not a directory: ${rootRoutesDir}`
219+
`Path exists but is not a directory: ${rootRoutesDir}`,
220220
);
221221
}
222222
return rootRoutesDir;
223223
} catch {
224224
throw new Error(
225-
'Could not find SvelteKit routes directory. Expected either "src/routes" or "routes" to exist.'
225+
'Could not find SvelteKit routes directory. Expected either "src/routes" or "routes" to exist.',
226226
);
227227
}
228228
}
229229
}
230230
}
231+
232+
/**
233+
* Gets the list of directories to scan for workflow files.
234+
*/
235+
export function getWorkflowDirs(options?: { dirs?: string[] }): string[] {
236+
return unique([
237+
// User-provided directories take precedence
238+
...(options?.dirs ?? []),
239+
// Scan routes directories (like Next.js does with app/pages directories)
240+
// This allows workflows to be placed anywhere in the routes tree
241+
"routes",
242+
"src/routes",
243+
// Also scan dedicated workflow directories for organization
244+
"workflows",
245+
"src/workflows",
246+
]).sort();
247+
}
248+
249+
function unique<T>(array: T[]): T[] {
250+
return Array.from(new Set(array));
251+
}

packages/sveltekit/src/plugin.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ import { resolveModulePath } from 'exsolve';
44
import type { HotUpdateOptions, Plugin } from 'vite';
55
import { SvelteKitBuilder } from './builder.js';
66

7-
export function workflowPlugin(): Plugin {
7+
export interface WorkflowPluginOptions {
8+
/**
9+
* Directories to scan for workflow files.
10+
* If not specified, defaults to ['workflows', 'src/workflows', 'routes', 'src/routes']
11+
*/
12+
dirs?: string[];
13+
}
14+
15+
export function workflowPlugin(options?: WorkflowPluginOptions): Plugin {
816
let builder: SvelteKitBuilder;
917

1018
return {
@@ -89,7 +97,9 @@ export function workflowPlugin(): Plugin {
8997
},
9098

9199
configResolved() {
92-
builder = new SvelteKitBuilder();
100+
builder = new SvelteKitBuilder({
101+
dirs: options?.dirs,
102+
});
93103
},
94104

95105
// TODO: Move this to @workflow/vite or something since this is vite specific

workbench/example/workflows/7_full.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
1-
import { createWebhook, sleep } from 'workflow';
1+
import { createWebhook, sleep } from "workflow";
22

33
export async function handleUserSignup(email: string) {
4-
'use workflow';
4+
"use workflow";
55

66
const user = await createUser(email);
77
await sendWelcomeEmail(user);
88

9-
await sleep('5s');
9+
await sleep("5s");
1010

1111
const webhook = createWebhook();
1212
await sendOnboardingEmail(user, webhook.url);
1313

1414
await webhook;
15-
console.log('Webhook Resolved');
15+
console.log("Webhook Resolved");
1616

17-
return { userId: user.id, status: 'onboarded' };
17+
return { userId: user.id, status: "onboarded" };
1818
}
1919

2020
async function createUser(email: string) {
21-
'use step';
21+
"use step";
2222

2323
console.log(`Creating a new user with email: ${email}`);
2424

2525
return { id: crypto.randomUUID(), email };
2626
}
2727

2828
async function sendWelcomeEmail(user: { id: string; email: string }) {
29-
'use step';
29+
"use step";
3030

3131
console.log(`Sending welcome email to user: ${user.id}`);
3232
}
3333

3434
async function sendOnboardingEmail(
3535
user: { id: string; email: string },
36-
callback: string
36+
callback: string,
3737
) {
38-
'use step';
38+
"use step";
3939

4040
console.log(`Sending onboarding email to user: ${user.id}`);
4141

0 commit comments

Comments
 (0)