diff --git a/.changeset/tangy-candles-cover.md b/.changeset/tangy-candles-cover.md new file mode 100644 index 0000000000..5b63d5e684 --- /dev/null +++ b/.changeset/tangy-candles-cover.md @@ -0,0 +1,6 @@ +--- +"@workflow/builders": patch +"@workflow/next": patch +--- + +Support esbuild loaders diff --git a/packages/builders/src/base-builder.ts b/packages/builders/src/base-builder.ts index 1957593d24..adf91e1514 100644 --- a/packages/builders/src/base-builder.ts +++ b/packages/builders/src/base-builder.ts @@ -128,6 +128,7 @@ export abstract class BaseBuilder { logLevel: 'silent', // External packages that should not be bundled during discovery external: this.config.externalPackages || [], + loader: this.config.esbuildLoaders, }); } catch (_) {} @@ -364,6 +365,7 @@ export abstract class BaseBuilder { '.mjs', '.cjs', ], + loader: this.config.esbuildLoaders, // Inline source maps for better stack traces in step execution. // Steps execute in Node.js context and inline sourcemaps ensure we get // meaningful stack traces with proper file names and line numbers when errors @@ -537,6 +539,7 @@ export abstract class BaseBuilder { '.mjs', '.cjs', ], + loader: this.config.esbuildLoaders, plugins: [ createSwcPlugin({ mode: 'workflow', @@ -718,6 +721,7 @@ export const POST = workflowEntrypoint(workflowCode);`; '.mjs', '.cjs', ], + loader: this.config.esbuildLoaders, plugins: [createSwcPlugin({ mode: 'client' })], }); diff --git a/packages/builders/src/types.ts b/packages/builders/src/types.ts index 8b0e2e486d..ce13f8d93c 100644 --- a/packages/builders/src/types.ts +++ b/packages/builders/src/types.ts @@ -1,3 +1,5 @@ +import type { Loader } from 'esbuild'; + export const validBuildTargets = [ 'standalone', 'vercel-build-output-api', @@ -27,6 +29,9 @@ interface BaseWorkflowConfig { // Optional prefix for debug files (e.g., "_" for Astro to ignore them) debugFilePrefix?: string; + + // Custom esbuild loaders for non-standard file extensions (e.g., { '.md': 'text' }) + esbuildLoaders?: Record; } /** diff --git a/packages/next/src/index.ts b/packages/next/src/index.ts index 0fc8e4d846..3f39073a4c 100644 --- a/packages/next/src/index.ts +++ b/packages/next/src/index.ts @@ -1,3 +1,4 @@ +import type { Loader } from 'esbuild'; import type { NextConfig } from 'next'; import semver from 'semver'; import { getNextBuilder } from './builder.js'; @@ -11,6 +12,7 @@ export function withWorkflow( ) => Promise), { workflows, + esbuildLoaders, }: { workflows?: { local?: { @@ -18,6 +20,8 @@ export function withWorkflow( dataDir?: string; }; }; + /** Custom esbuild loaders for non-standard file extensions (e.g., { '.md': 'text' }) */ + esbuildLoaders?: Record; } = {} ) { if (!process.env.VERCEL_DEPLOYMENT_ID) { @@ -137,6 +141,7 @@ export function withWorkflow( 'client-only', ...(nextConfig.serverExternalPackages || []), ], + esbuildLoaders, }); await workflowBuilder.build(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e37c0ff22b..8831b7d36a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1662,6 +1662,9 @@ importers: '@workflow/world-postgres': specifier: workspace:* version: link:../../packages/world-postgres + raw-loader: + specifier: 4.0.2 + version: 4.0.2(webpack@5.104.1(esbuild@0.25.12)) tailwindcss: specifier: ^4 version: 4.1.13 @@ -7892,6 +7895,9 @@ packages: better-sqlite3@11.10.0: resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==} + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + bin-version-check@5.1.0: resolution: {integrity: sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==} engines: {node: '>=12'} @@ -9102,6 +9108,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + enabled@2.0.0: resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} @@ -10719,6 +10729,10 @@ packages: resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} engines: {node: '>=6.11.5'} + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + local-pkg@1.1.2: resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} @@ -12413,6 +12427,12 @@ packages: resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} engines: {node: '>= 0.10'} + raw-loader@4.0.2: + resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} @@ -22043,6 +22063,8 @@ snapshots: prebuild-install: 7.1.3 optional: true + big.js@5.2.2: {} + bin-version-check@5.1.0: dependencies: bin-version: 6.0.0 @@ -23212,6 +23234,8 @@ snapshots: emoji-regex@9.2.2: {} + emojis-list@3.0.0: {} + enabled@2.0.0: {} encodeurl@2.0.0: {} @@ -25111,6 +25135,12 @@ snapshots: loader-runner@4.3.1: {} + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + local-pkg@1.1.2: dependencies: mlly: 1.8.0 @@ -27788,6 +27818,12 @@ snapshots: iconv-lite: 0.7.0 unpipe: 1.0.0 + raw-loader@4.0.2(webpack@5.104.1(esbuild@0.25.12)): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.104.1(esbuild@0.25.12) + rc9@2.1.2: dependencies: defu: 6.1.4 @@ -29085,6 +29121,17 @@ snapshots: '@swc/core': 1.15.3 esbuild: 0.25.12 + terser-webpack-plugin@5.3.16(esbuild@0.25.12)(webpack@5.104.1(esbuild@0.25.12)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.44.0 + webpack: 5.104.1(esbuild@0.25.12) + optionalDependencies: + esbuild: 0.25.12 + terser@5.44.0: dependencies: '@jridgewell/source-map': 0.3.11 @@ -30169,6 +30216,38 @@ snapshots: - esbuild - uglify-js + webpack@5.104.1(esbuild@0.25.12): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.28.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.2 + es-module-lexer: 2.0.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.16(esbuild@0.25.12)(webpack@5.104.1(esbuild@0.25.12)) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 diff --git a/workbench/nextjs-turbopack/md.d.ts b/workbench/nextjs-turbopack/md.d.ts new file mode 100644 index 0000000000..f73d61b396 --- /dev/null +++ b/workbench/nextjs-turbopack/md.d.ts @@ -0,0 +1,4 @@ +declare module '*.md' { + const content: string; + export default content; +} diff --git a/workbench/nextjs-turbopack/next.config.ts b/workbench/nextjs-turbopack/next.config.ts index 0428ff83f7..ab185914bf 100644 --- a/workbench/nextjs-turbopack/next.config.ts +++ b/workbench/nextjs-turbopack/next.config.ts @@ -4,7 +4,19 @@ import { withWorkflow } from 'workflow/next'; const nextConfig: NextConfig = { /* config options here */ serverExternalPackages: ['@node-rs/xxhash'], + turbopack: { + rules: { + '*.md': { + loaders: ['raw-loader'], + as: '*.js', + }, + }, + }, }; // export default nextConfig; -export default withWorkflow(nextConfig); +export default withWorkflow(nextConfig, { + esbuildLoaders: { + '.md': 'text', + }, +}); diff --git a/workbench/nextjs-turbopack/package.json b/workbench/nextjs-turbopack/package.json index 65da6fe340..a6e868d341 100644 --- a/workbench/nextjs-turbopack/package.json +++ b/workbench/nextjs-turbopack/package.json @@ -42,6 +42,7 @@ "@types/react-dom": "^19", "@vercel/blob": "2.0.0", "@workflow/world-postgres": "workspace:*", + "raw-loader": "4.0.2", "tailwindcss": "^4", "typescript": "catalog:" } diff --git a/workbench/nextjs-turbopack/workflows/11_custom_loader.md b/workbench/nextjs-turbopack/workflows/11_custom_loader.md new file mode 100644 index 0000000000..528fad7d41 --- /dev/null +++ b/workbench/nextjs-turbopack/workflows/11_custom_loader.md @@ -0,0 +1,7 @@ +# Test Prompt + +This is a test markdown file to verify the esbuildLoaders feature works. + +## Instructions +- Step 1: Do something +- Step 2: Do something else diff --git a/workbench/nextjs-turbopack/workflows/11_custom_loader.ts b/workbench/nextjs-turbopack/workflows/11_custom_loader.ts new file mode 100644 index 0000000000..57bc0b120d --- /dev/null +++ b/workbench/nextjs-turbopack/workflows/11_custom_loader.ts @@ -0,0 +1,23 @@ +/** + * Test for custom esbuild loaders feature. + * Verifies that non-standard file extensions (like .md) can be imported + * when configured via esbuildLoaders in withWorkflow options. + */ +import PROMPT from '@/workflows/11_custom_loader.md'; + +async function logPrompt(): Promise { + 'use step'; + + console.log('Loaded prompt:', PROMPT.slice(0, 50)); + + return PROMPT; +} + +export async function customLoader() { + 'use workflow'; + + const prompt = await logPrompt(); + console.log('Custom loader test completed. Prompt length:', prompt.length); + + return { success: true, promptLength: prompt.length }; +}