@workflow/builders 5.0.0-beta.4 → 5.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/base-builder.d.ts +69 -2
  2. package/dist/base-builder.d.ts.map +1 -1
  3. package/dist/base-builder.js +368 -19
  4. package/dist/base-builder.js.map +1 -1
  5. package/dist/config-helpers.d.ts +2 -1
  6. package/dist/config-helpers.d.ts.map +1 -1
  7. package/dist/config-helpers.js +1 -0
  8. package/dist/config-helpers.js.map +1 -1
  9. package/dist/constants.d.ts +3 -13
  10. package/dist/constants.d.ts.map +1 -1
  11. package/dist/constants.js +3 -13
  12. package/dist/constants.js.map +1 -1
  13. package/dist/discover-entries-esbuild-plugin.d.ts.map +1 -1
  14. package/dist/discover-entries-esbuild-plugin.js +16 -1
  15. package/dist/discover-entries-esbuild-plugin.js.map +1 -1
  16. package/dist/external-package-warning.test.d.ts +2 -0
  17. package/dist/external-package-warning.test.d.ts.map +1 -0
  18. package/dist/external-package-warning.test.js +219 -0
  19. package/dist/external-package-warning.test.js.map +1 -0
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/module-specifier.d.ts.map +1 -1
  25. package/dist/module-specifier.js +47 -9
  26. package/dist/module-specifier.js.map +1 -1
  27. package/dist/module-specifier.test.js +20 -0
  28. package/dist/module-specifier.test.js.map +1 -1
  29. package/dist/node-module-esbuild-plugin.d.ts.map +1 -1
  30. package/dist/node-module-esbuild-plugin.js +72 -22
  31. package/dist/node-module-esbuild-plugin.js.map +1 -1
  32. package/dist/node-module-esbuild-plugin.test.js +96 -0
  33. package/dist/node-module-esbuild-plugin.test.js.map +1 -1
  34. package/dist/resolve-sourcemap.test.d.ts +2 -0
  35. package/dist/resolve-sourcemap.test.d.ts.map +1 -0
  36. package/dist/resolve-sourcemap.test.js +126 -0
  37. package/dist/resolve-sourcemap.test.js.map +1 -0
  38. package/dist/standalone.d.ts +0 -3
  39. package/dist/standalone.d.ts.map +1 -1
  40. package/dist/standalone.js +10 -39
  41. package/dist/standalone.js.map +1 -1
  42. package/dist/swc-esbuild-plugin.d.ts +9 -0
  43. package/dist/swc-esbuild-plugin.d.ts.map +1 -1
  44. package/dist/swc-esbuild-plugin.js +63 -8
  45. package/dist/swc-esbuild-plugin.js.map +1 -1
  46. package/dist/swc-esbuild-plugin.test.js +279 -3
  47. package/dist/swc-esbuild-plugin.test.js.map +1 -1
  48. package/dist/types.d.ts +29 -0
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/types.js.map +1 -1
  51. package/dist/vercel-build-output-api.d.ts +0 -2
  52. package/dist/vercel-build-output-api.d.ts.map +1 -1
  53. package/dist/vercel-build-output-api.js +24 -55
  54. package/dist/vercel-build-output-api.js.map +1 -1
  55. package/package.json +5 -5
@@ -4,6 +4,7 @@ import { promisify } from 'node:util';
4
4
  import enhancedResolveOrig from 'enhanced-resolve';
5
5
  import { applySwcTransform, } from './apply-swc-transform.js';
6
6
  import { jsTsRegex, parentHasChild, } from './discover-entries-esbuild-plugin.js';
7
+ import { resolveModuleSpecifier } from './module-specifier.js';
7
8
  import { resolveWorkflowAliasRelativePath } from './workflow-alias.js';
8
9
  const NODE_RESOLVE_OPTIONS = {
9
10
  dependencyType: 'commonjs',
@@ -39,6 +40,9 @@ const NODE_ESM_RESOLVE_OPTIONS = {
39
40
  dependencyType: 'esm',
40
41
  conditionNames: ['node', 'import'],
41
42
  };
43
+ function normalizePath(path) {
44
+ return path.replace(/\\/g, '/');
45
+ }
42
46
  export function createSwcPlugin(options) {
43
47
  return {
44
48
  name: 'swc-workflow-plugin',
@@ -75,17 +79,33 @@ export function createSwcPlugin(options) {
75
79
  const specifier = args.path;
76
80
  const specifierIsPath = specifier.startsWith('.') || specifier.startsWith('/');
77
81
  let resolvedPath;
78
- // Determines whether the external path should be relativized
79
- // (project-local file) or kept as a bare specifier (npm package).
80
- let shouldMakeRelative = specifierIsPath;
82
+ // Path-style specifiers (./foo, ../foo, /abs/path) externalize as
83
+ // relative paths from `outdir`. Bare specifiers (npm packages)
84
+ // externalize as-is so Node can resolve them at runtime.
85
+ const shouldMakeRelative = specifierIsPath;
81
86
  if (specifierIsPath) {
82
87
  resolvedPath = await enhancedResolve(args.resolveDir, specifier);
83
88
  }
84
89
  else {
85
90
  // Resolve from project root so nested deps aren't externalized
86
91
  resolvedPath = await enhancedResolve(build.initialOptions.absWorkingDir || process.cwd(), specifier).catch(() => undefined); // swallow so esbuild fallback below can try
87
- // Fall back to esbuild for aliases/tsconfig paths,
88
- // but only accept project-local results
92
+ // Fall back to esbuild for aliases/tsconfig paths.
93
+ //
94
+ // If the specifier resolves to a project-local file via an
95
+ // alias/path mapping (e.g. tsconfig `paths`, esbuild `alias`,
96
+ // self-referencing package names like `@my-pkg/lib/foo`), we
97
+ // bundle it inline rather than externalizing.
98
+ //
99
+ // Externalizing such files is unsafe: we'd emit a relative
100
+ // import to the original source on disk, but that source can
101
+ // contain further alias imports. At runtime, Node's ESM loader
102
+ // doesn't know about tsconfig paths or build-time aliases, so
103
+ // those transitive imports throw `ERR_MODULE_NOT_FOUND` /
104
+ // `Package subpath ... is not defined by "exports"`.
105
+ //
106
+ // Bundling inline ensures alias-based imports are resolved at
107
+ // build time (where the alias map is known) and the runtime
108
+ // never sees an unresolvable specifier.
89
109
  if (!resolvedPath) {
90
110
  const esbuildResult = await build.resolve(specifier, {
91
111
  resolveDir: args.resolveDir,
@@ -99,15 +119,33 @@ export function createSwcPlugin(options) {
99
119
  .replace(/\\/g, '/')
100
120
  .includes('/node_modules/');
101
121
  if (isProjectLocalFile) {
102
- resolvedPath = esbuildResult.path;
103
- shouldMakeRelative = true;
122
+ // Let esbuild bundle this aliased project-local file inline
123
+ // (return null to defer to esbuild's normal pipeline). The
124
+ // SWC `onLoad` handler will still process it.
125
+ return null;
126
+ }
127
+ else if (options.entriesToBundle &&
128
+ esbuildResult.path?.endsWith('.node')) {
129
+ return {
130
+ external: true,
131
+ path: specifier,
132
+ };
104
133
  }
105
134
  }
106
135
  }
107
136
  if (!resolvedPath)
108
137
  return null;
109
138
  // Normalize to forward slashes for cross-platform comparison
110
- const normalizedResolvedPath = resolvedPath.replace(/\\/g, '/');
139
+ const normalizedResolvedPath = normalizePath(resolvedPath);
140
+ const workingDir = build.initialOptions.absWorkingDir || process.cwd();
141
+ const projectRoot = options.projectRoot || workingDir;
142
+ if (options.entriesToBundle &&
143
+ normalizedResolvedPath.endsWith('.node')) {
144
+ return {
145
+ external: true,
146
+ path: specifier,
147
+ };
148
+ }
111
149
  // Check if this module is a discovered entry whose SWC-transformed
112
150
  // code contains side effects (workflow/step/class registration).
113
151
  // Override the package.json "sideEffects": false so esbuild does not
@@ -128,6 +166,17 @@ export function createSwcPlugin(options) {
128
166
  shouldBundle = true;
129
167
  break;
130
168
  }
169
+ // Bundle project-local source files that are imported by a
170
+ // step/serde entry so direct runtime loaders do not see raw TS
171
+ // extensionless imports. Keep package dependencies external
172
+ // unless they are themselves in entriesToBundle or are parents
173
+ // of a discovered workflow/step/serde file via the check above.
174
+ if (options.bundleTransitiveLocalStepDependencies &&
175
+ isProjectLocalFile(normalizedResolvedPath, projectRoot) &&
176
+ parentHasChild(normalizedEntry, normalizedResolvedPath)) {
177
+ shouldBundle = true;
178
+ break;
179
+ }
131
180
  }
132
181
  if (shouldBundle) {
133
182
  // Let esbuild bundle this entry, but override sideEffects if needed.
@@ -275,4 +324,10 @@ export function createSwcPlugin(options) {
275
324
  },
276
325
  };
277
326
  }
327
+ function isProjectLocalFile(filePath, projectRoot) {
328
+ if (normalizePath(filePath).includes('/node_modules/')) {
329
+ return false;
330
+ }
331
+ return (resolveModuleSpecifier(filePath, projectRoot).moduleSpecifier === undefined);
332
+ }
278
333
  //# sourceMappingURL=swc-esbuild-plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"swc-esbuild-plugin.js","sourceRoot":"","sources":["../src/swc-esbuild-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { relative } from 'node:path';\nimport { promisify } from 'node:util';\nimport enhancedResolveOrig from 'enhanced-resolve';\nimport type { Plugin } from 'esbuild';\nimport {\n applySwcTransform,\n type WorkflowManifest,\n} from './apply-swc-transform.js';\nimport {\n jsTsRegex,\n parentHasChild,\n} from './discover-entries-esbuild-plugin.js';\nimport { resolveWorkflowAliasRelativePath } from './workflow-alias.js';\n\nexport interface SwcPluginOptions {\n mode: 'step' | 'workflow';\n entriesToBundle?: string[];\n outdir?: string;\n projectRoot?: string;\n workflowManifest?: WorkflowManifest;\n /**\n * Rewrite TypeScript extensions (.ts, .tsx, .mts, .cts) to their JS\n * equivalents (.js, .mjs, .cjs) in externalized import paths.\n *\n * Enable this when the output bundle is consumed directly by Node's native\n * ESM loader (e.g. vitest), which cannot resolve .ts extensions.\n *\n * Leave disabled (default) when a downstream bundler (webpack, Vite, etc.)\n * handles resolution — those tools resolve .ts natively and rewriting\n * breaks them because the .js file doesn't exist on disk.\n */\n rewriteTsExtensions?: boolean;\n /**\n * Absolute file paths of discovered workflow/step/serde entries whose\n * imports must be treated as side-effectful.\n *\n * The SWC compiler transform injects registration calls (workflow IDs,\n * step IDs, class serialization, etc.) into these files. Without this\n * override, esbuild honours `\"sideEffects\": false` from the package's\n * `package.json` and silently drops bare imports of these modules.\n */\n sideEffectEntries?: string[];\n}\n\nconst NODE_RESOLVE_OPTIONS = {\n dependencyType: 'commonjs',\n modules: ['node_modules'],\n exportsFields: ['exports'],\n importsFields: ['imports'],\n conditionNames: ['node', 'require'],\n descriptionFiles: ['package.json'],\n extensions: [\n '.ts',\n '.tsx',\n '.mts',\n '.cts',\n '.cjs',\n '.mjs',\n '.js',\n '.jsx',\n '.json',\n '.node',\n ],\n enforceExtensions: false,\n symlinks: true,\n mainFields: ['main'],\n mainFiles: ['index'],\n roots: [],\n fullySpecified: false,\n preferRelative: false,\n preferAbsolute: false,\n restrictions: [],\n};\n\nconst NODE_ESM_RESOLVE_OPTIONS = {\n ...NODE_RESOLVE_OPTIONS,\n dependencyType: 'esm',\n conditionNames: ['node', 'import'],\n};\n\nexport function createSwcPlugin(options: SwcPluginOptions): Plugin {\n return {\n name: 'swc-workflow-plugin',\n setup(build) {\n // everything is external unless explicitly configured\n // to be bundled\n const cjsResolver = promisify(\n enhancedResolveOrig.create(NODE_RESOLVE_OPTIONS)\n );\n const esmResolver = promisify(\n enhancedResolveOrig.create(NODE_ESM_RESOLVE_OPTIONS)\n );\n\n const enhancedResolve = async (context: string, path: string) => {\n try {\n return await esmResolver(context, path);\n } catch (_) {\n return cjsResolver(context, path);\n }\n };\n\n // Pre-compute the normalized side-effect entries set for O(1) lookups.\n const normalizedSideEffectEntries = new Set(\n options.sideEffectEntries?.map((e) => e.replace(/\\\\/g, '/'))\n );\n\n build.onResolve({ filter: /.*/ }, async (args) => {\n if (args.pluginData?.skipSwcPlugin) return null;\n\n if (\n !options.entriesToBundle &&\n normalizedSideEffectEntries.size === 0\n ) {\n return null;\n }\n\n // When only sideEffectEntries is set (no entriesToBundle), we only\n // need to override sideEffects for top-level bare imports — typically\n // from the virtual entry. Skip resolution for transitive imports\n // (dynamic imports, requires, etc.) to avoid unnecessary overhead.\n if (!options.entriesToBundle && args.kind !== 'import-statement') {\n return null;\n }\n\n try {\n const specifier = args.path;\n const specifierIsPath =\n specifier.startsWith('.') || specifier.startsWith('/');\n\n let resolvedPath: string | false | undefined;\n // Determines whether the external path should be relativized\n // (project-local file) or kept as a bare specifier (npm package).\n let shouldMakeRelative = specifierIsPath;\n\n if (specifierIsPath) {\n resolvedPath = await enhancedResolve(args.resolveDir, specifier);\n } else {\n // Resolve from project root so nested deps aren't externalized\n resolvedPath = await enhancedResolve(\n build.initialOptions.absWorkingDir || process.cwd(),\n specifier\n ).catch(() => undefined); // swallow so esbuild fallback below can try\n\n // Fall back to esbuild for aliases/tsconfig paths,\n // but only accept project-local results\n if (!resolvedPath) {\n const esbuildResult = await build.resolve(specifier, {\n resolveDir: args.resolveDir,\n kind: args.kind,\n pluginData: { skipSwcPlugin: true },\n });\n const didResolve =\n !!esbuildResult.path && !esbuildResult.errors.length;\n const isProjectLocalFile =\n didResolve &&\n !esbuildResult.external &&\n !esbuildResult.path\n .replace(/\\\\/g, '/')\n .includes('/node_modules/');\n if (isProjectLocalFile) {\n resolvedPath = esbuildResult.path;\n shouldMakeRelative = true;\n }\n }\n }\n\n if (!resolvedPath) return null;\n\n // Normalize to forward slashes for cross-platform comparison\n const normalizedResolvedPath = resolvedPath.replace(/\\\\/g, '/');\n\n // Check if this module is a discovered entry whose SWC-transformed\n // code contains side effects (workflow/step/class registration).\n // Override the package.json \"sideEffects\": false so esbuild does not\n // drop bare imports of these modules.\n const hasSideEffects = normalizedSideEffectEntries.has(\n normalizedResolvedPath\n );\n\n if (options.entriesToBundle) {\n let shouldBundle = false;\n for (const entryToBundle of options.entriesToBundle) {\n const normalizedEntry = entryToBundle.replace(/\\\\/g, '/');\n\n if (normalizedResolvedPath === normalizedEntry) {\n shouldBundle = true;\n break;\n }\n\n // if the current entry imports a child that needs\n // to be bundled then it needs to also be bundled so\n // that the child can have our transform applied\n if (parentHasChild(normalizedResolvedPath, normalizedEntry)) {\n shouldBundle = true;\n break;\n }\n }\n\n if (shouldBundle) {\n // Let esbuild bundle this entry, but override sideEffects if needed.\n // We must return the resolved `path` alongside `sideEffects` because\n // returning only `{ sideEffects: true }` without a path causes esbuild\n // to fall through to its own resolver, which re-reads the package.json\n // and applies `\"sideEffects\": false` from there.\n return hasSideEffects\n ? { path: resolvedPath, sideEffects: true }\n : null;\n }\n\n let externalPath: string;\n if (shouldMakeRelative) {\n // When the resolved file lives inside node_modules, let\n // esbuild bundle it rather than externalizing with a deeply\n // nested relative path. Downstream bundlers (Rollup/Vite)\n // can't rewrite opaque `__require()` calls in CJS shims, so\n // relative paths computed for `outdir` break once the output\n // is rebundled to a different directory.\n if (normalizedResolvedPath.includes('/node_modules/')) {\n return null; // let esbuild bundle it\n }\n\n externalPath = relative(\n options.outdir || process.cwd(),\n resolvedPath\n ).replace(/\\\\/g, '/');\n\n if (options.rewriteTsExtensions) {\n // Rewrite TypeScript extensions to their JS equivalents so the\n // externalized import is loadable by Node's native ESM loader.\n externalPath = externalPath\n .replace(/\\.tsx?$/, '.js')\n .replace(/\\.mts$/, '.mjs')\n .replace(/\\.cts$/, '.cjs');\n }\n } else {\n externalPath = specifier;\n }\n\n return {\n external: true,\n path: externalPath,\n sideEffects: hasSideEffects || undefined,\n };\n }\n\n // No entriesToBundle — only override sideEffects when needed.\n // We must return the resolved `path` alongside `sideEffects` because\n // returning only `{ sideEffects: true }` without a path causes esbuild\n // to fall through to its own resolver, which re-reads the package.json\n // and applies `\"sideEffects\": false` from there.\n return hasSideEffects\n ? { path: resolvedPath, sideEffects: true }\n : null;\n } catch (_) {}\n return null;\n });\n\n // Handle TypeScript and JavaScript files\n build.onLoad({ filter: jsTsRegex }, async (args) => {\n // Determine if this is a TypeScript file\n try {\n // Determine the loader based on the output\n let loader: 'js' | 'jsx' | 'tsx' = 'js';\n if (args.path.endsWith('.jsx')) {\n loader = 'jsx';\n } else if (args.path.endsWith('.tsx')) {\n loader = 'tsx';\n }\n const source = await readFile(args.path, 'utf8');\n const normalizedSource = source\n .replace(/require\\(\\s*(['\"])server-only\\1\\s*\\)/g, 'void 0')\n .replace(/require\\(\\s*(['\"])client-only\\1\\s*\\)/g, 'void 0');\n\n // Calculate relative path for SWC plugin\n // The filename parameter is used to generate workflowId/stepId, so it must be relative\n const workingDir =\n build.initialOptions.absWorkingDir || process.cwd();\n const projectRoot = options.projectRoot || workingDir;\n // Normalize paths: convert backslashes to forward slashes and remove trailing slashes\n const normalizedWorkingDir = workingDir\n .replace(/\\\\/g, '/')\n .replace(/\\/$/, '');\n const normalizedPath = args.path.replace(/\\\\/g, '/');\n\n // Windows fix: Always do case-insensitive path comparison as the PRIMARY logic\n // to work around node:path.relative() not recognizing paths with different drive\n // letter casing (e.g., D: vs d:) as being in the same tree\n const lowerWd = normalizedWorkingDir.toLowerCase();\n const lowerPath = normalizedPath.toLowerCase();\n\n let relativeFilepath: string;\n if (lowerPath.startsWith(lowerWd + '/')) {\n // File is under working directory - manually calculate relative path\n // This ensures we get a relative path even with drive letter casing issues\n relativeFilepath = normalizedPath.substring(\n normalizedWorkingDir.length + 1\n );\n } else if (lowerPath === lowerWd) {\n // File IS the working directory\n relativeFilepath = '.';\n } else {\n // File is outside working directory - use relative() and strip ../ prefixes if needed\n relativeFilepath = relative(\n normalizedWorkingDir,\n normalizedPath\n ).replace(/\\\\/g, '/');\n\n // Handle files discovered outside the working directory\n // These come back as ../path/to/file, but we want just path/to/file\n if (relativeFilepath.startsWith('../')) {\n const aliasedRelativePath =\n await resolveWorkflowAliasRelativePath(args.path, workingDir);\n if (aliasedRelativePath) {\n relativeFilepath = aliasedRelativePath;\n } else {\n relativeFilepath = relativeFilepath\n .split('/')\n .filter((part) => part !== '..')\n .join('/');\n }\n }\n }\n\n // Final safety check - ensure we never pass an absolute path to SWC\n if (\n relativeFilepath.includes(':') ||\n relativeFilepath.startsWith('/')\n ) {\n // This should never happen, but if it does, use just the filename as last resort\n console.error(\n `[ERROR] relativeFilepath is still absolute: ${relativeFilepath}`\n );\n relativeFilepath = normalizedPath.split('/').pop() || 'unknown.ts';\n }\n\n const { code: transformedCode, workflowManifest } =\n await applySwcTransform(\n relativeFilepath,\n normalizedSource,\n options.mode,\n args.path, // Pass absolute path for module specifier resolution\n projectRoot\n );\n\n if (!options.workflowManifest) {\n options.workflowManifest = {};\n }\n\n options.workflowManifest.workflows = Object.assign(\n options.workflowManifest.workflows || {},\n workflowManifest.workflows\n );\n options.workflowManifest.steps = Object.assign(\n options.workflowManifest.steps || {},\n workflowManifest.steps\n );\n options.workflowManifest.classes = Object.assign(\n options.workflowManifest.classes || {},\n workflowManifest.classes\n );\n\n return {\n contents: transformedCode,\n loader,\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n console.error(\n `❌ SWC transform error in ${args.path}:`,\n errorMessage\n );\n return {\n errors: [\n {\n text: `SWC transform failed: ${errorMessage}`,\n location: { file: args.path, line: 0, column: 0 },\n },\n ],\n };\n }\n });\n },\n };\n}\n"]}
1
+ {"version":3,"file":"swc-esbuild-plugin.js","sourceRoot":"","sources":["../src/swc-esbuild-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { relative } from 'node:path';\nimport { promisify } from 'node:util';\nimport enhancedResolveOrig from 'enhanced-resolve';\nimport type { Plugin } from 'esbuild';\nimport {\n applySwcTransform,\n type WorkflowManifest,\n} from './apply-swc-transform.js';\nimport {\n jsTsRegex,\n parentHasChild,\n} from './discover-entries-esbuild-plugin.js';\nimport { resolveModuleSpecifier } from './module-specifier.js';\nimport { resolveWorkflowAliasRelativePath } from './workflow-alias.js';\n\nexport interface SwcPluginOptions {\n mode: 'step' | 'workflow';\n entriesToBundle?: string[];\n outdir?: string;\n projectRoot?: string;\n workflowManifest?: WorkflowManifest;\n /**\n * Rewrite TypeScript extensions (.ts, .tsx, .mts, .cts) to their JS\n * equivalents (.js, .mjs, .cjs) in externalized import paths.\n *\n * Enable this when the output bundle is consumed directly by Node's native\n * ESM loader (e.g. vitest), which cannot resolve .ts extensions.\n *\n * Leave disabled (default) when a downstream bundler (webpack, Vite, etc.)\n * handles resolution — those tools resolve .ts natively and rewriting\n * breaks them because the .js file doesn't exist on disk.\n */\n rewriteTsExtensions?: boolean;\n /**\n * Bundle project-local files that are transitively imported by step entries.\n *\n * Keep this disabled when a downstream bundler consumes the generated step\n * bundle because that bundler can resolve the externalized local imports.\n * Enable it for direct runtime loading, where Node imports the generated step\n * bundle from disk without a later bundling pass.\n */\n bundleTransitiveLocalStepDependencies?: boolean;\n /**\n * Absolute file paths of discovered workflow/step/serde entries whose\n * imports must be treated as side-effectful.\n *\n * The SWC compiler transform injects registration calls (workflow IDs,\n * step IDs, class serialization, etc.) into these files. Without this\n * override, esbuild honours `\"sideEffects\": false` from the package's\n * `package.json` and silently drops bare imports of these modules.\n */\n sideEffectEntries?: string[];\n}\n\nconst NODE_RESOLVE_OPTIONS = {\n dependencyType: 'commonjs',\n modules: ['node_modules'],\n exportsFields: ['exports'],\n importsFields: ['imports'],\n conditionNames: ['node', 'require'],\n descriptionFiles: ['package.json'],\n extensions: [\n '.ts',\n '.tsx',\n '.mts',\n '.cts',\n '.cjs',\n '.mjs',\n '.js',\n '.jsx',\n '.json',\n '.node',\n ],\n enforceExtensions: false,\n symlinks: true,\n mainFields: ['main'],\n mainFiles: ['index'],\n roots: [],\n fullySpecified: false,\n preferRelative: false,\n preferAbsolute: false,\n restrictions: [],\n};\n\nconst NODE_ESM_RESOLVE_OPTIONS = {\n ...NODE_RESOLVE_OPTIONS,\n dependencyType: 'esm',\n conditionNames: ['node', 'import'],\n};\n\nfunction normalizePath(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\nexport function createSwcPlugin(options: SwcPluginOptions): Plugin {\n return {\n name: 'swc-workflow-plugin',\n setup(build) {\n // everything is external unless explicitly configured\n // to be bundled\n const cjsResolver = promisify(\n enhancedResolveOrig.create(NODE_RESOLVE_OPTIONS)\n );\n const esmResolver = promisify(\n enhancedResolveOrig.create(NODE_ESM_RESOLVE_OPTIONS)\n );\n\n const enhancedResolve = async (context: string, path: string) => {\n try {\n return await esmResolver(context, path);\n } catch (_) {\n return cjsResolver(context, path);\n }\n };\n\n // Pre-compute the normalized side-effect entries set for O(1) lookups.\n const normalizedSideEffectEntries = new Set(\n options.sideEffectEntries?.map((e) => e.replace(/\\\\/g, '/'))\n );\n\n build.onResolve({ filter: /.*/ }, async (args) => {\n if (args.pluginData?.skipSwcPlugin) return null;\n\n if (\n !options.entriesToBundle &&\n normalizedSideEffectEntries.size === 0\n ) {\n return null;\n }\n\n // When only sideEffectEntries is set (no entriesToBundle), we only\n // need to override sideEffects for top-level bare imports — typically\n // from the virtual entry. Skip resolution for transitive imports\n // (dynamic imports, requires, etc.) to avoid unnecessary overhead.\n if (!options.entriesToBundle && args.kind !== 'import-statement') {\n return null;\n }\n\n try {\n const specifier = args.path;\n const specifierIsPath =\n specifier.startsWith('.') || specifier.startsWith('/');\n\n let resolvedPath: string | false | undefined;\n // Path-style specifiers (./foo, ../foo, /abs/path) externalize as\n // relative paths from `outdir`. Bare specifiers (npm packages)\n // externalize as-is so Node can resolve them at runtime.\n const shouldMakeRelative = specifierIsPath;\n\n if (specifierIsPath) {\n resolvedPath = await enhancedResolve(args.resolveDir, specifier);\n } else {\n // Resolve from project root so nested deps aren't externalized\n resolvedPath = await enhancedResolve(\n build.initialOptions.absWorkingDir || process.cwd(),\n specifier\n ).catch(() => undefined); // swallow so esbuild fallback below can try\n\n // Fall back to esbuild for aliases/tsconfig paths.\n //\n // If the specifier resolves to a project-local file via an\n // alias/path mapping (e.g. tsconfig `paths`, esbuild `alias`,\n // self-referencing package names like `@my-pkg/lib/foo`), we\n // bundle it inline rather than externalizing.\n //\n // Externalizing such files is unsafe: we'd emit a relative\n // import to the original source on disk, but that source can\n // contain further alias imports. At runtime, Node's ESM loader\n // doesn't know about tsconfig paths or build-time aliases, so\n // those transitive imports throw `ERR_MODULE_NOT_FOUND` /\n // `Package subpath ... is not defined by \"exports\"`.\n //\n // Bundling inline ensures alias-based imports are resolved at\n // build time (where the alias map is known) and the runtime\n // never sees an unresolvable specifier.\n if (!resolvedPath) {\n const esbuildResult = await build.resolve(specifier, {\n resolveDir: args.resolveDir,\n kind: args.kind,\n pluginData: { skipSwcPlugin: true },\n });\n const didResolve =\n !!esbuildResult.path && !esbuildResult.errors.length;\n const isProjectLocalFile =\n didResolve &&\n !esbuildResult.external &&\n !esbuildResult.path\n .replace(/\\\\/g, '/')\n .includes('/node_modules/');\n if (isProjectLocalFile) {\n // Let esbuild bundle this aliased project-local file inline\n // (return null to defer to esbuild's normal pipeline). The\n // SWC `onLoad` handler will still process it.\n return null;\n } else if (\n options.entriesToBundle &&\n esbuildResult.path?.endsWith('.node')\n ) {\n return {\n external: true,\n path: specifier,\n };\n }\n }\n }\n\n if (!resolvedPath) return null;\n\n // Normalize to forward slashes for cross-platform comparison\n const normalizedResolvedPath = normalizePath(resolvedPath);\n const workingDir =\n build.initialOptions.absWorkingDir || process.cwd();\n const projectRoot = options.projectRoot || workingDir;\n\n if (\n options.entriesToBundle &&\n normalizedResolvedPath.endsWith('.node')\n ) {\n return {\n external: true,\n path: specifier,\n };\n }\n\n // Check if this module is a discovered entry whose SWC-transformed\n // code contains side effects (workflow/step/class registration).\n // Override the package.json \"sideEffects\": false so esbuild does not\n // drop bare imports of these modules.\n const hasSideEffects = normalizedSideEffectEntries.has(\n normalizedResolvedPath\n );\n\n if (options.entriesToBundle) {\n let shouldBundle = false;\n for (const entryToBundle of options.entriesToBundle) {\n const normalizedEntry = entryToBundle.replace(/\\\\/g, '/');\n\n if (normalizedResolvedPath === normalizedEntry) {\n shouldBundle = true;\n break;\n }\n\n // if the current entry imports a child that needs\n // to be bundled then it needs to also be bundled so\n // that the child can have our transform applied\n if (parentHasChild(normalizedResolvedPath, normalizedEntry)) {\n shouldBundle = true;\n break;\n }\n\n // Bundle project-local source files that are imported by a\n // step/serde entry so direct runtime loaders do not see raw TS\n // extensionless imports. Keep package dependencies external\n // unless they are themselves in entriesToBundle or are parents\n // of a discovered workflow/step/serde file via the check above.\n if (\n options.bundleTransitiveLocalStepDependencies &&\n isProjectLocalFile(normalizedResolvedPath, projectRoot) &&\n parentHasChild(normalizedEntry, normalizedResolvedPath)\n ) {\n shouldBundle = true;\n break;\n }\n }\n\n if (shouldBundle) {\n // Let esbuild bundle this entry, but override sideEffects if needed.\n // We must return the resolved `path` alongside `sideEffects` because\n // returning only `{ sideEffects: true }` without a path causes esbuild\n // to fall through to its own resolver, which re-reads the package.json\n // and applies `\"sideEffects\": false` from there.\n return hasSideEffects\n ? { path: resolvedPath, sideEffects: true }\n : null;\n }\n\n let externalPath: string;\n if (shouldMakeRelative) {\n // When the resolved file lives inside node_modules, let\n // esbuild bundle it rather than externalizing with a deeply\n // nested relative path. Downstream bundlers (Rollup/Vite)\n // can't rewrite opaque `__require()` calls in CJS shims, so\n // relative paths computed for `outdir` break once the output\n // is rebundled to a different directory.\n if (normalizedResolvedPath.includes('/node_modules/')) {\n return null; // let esbuild bundle it\n }\n\n externalPath = relative(\n options.outdir || process.cwd(),\n resolvedPath\n ).replace(/\\\\/g, '/');\n\n if (options.rewriteTsExtensions) {\n // Rewrite TypeScript extensions to their JS equivalents so the\n // externalized import is loadable by Node's native ESM loader.\n externalPath = externalPath\n .replace(/\\.tsx?$/, '.js')\n .replace(/\\.mts$/, '.mjs')\n .replace(/\\.cts$/, '.cjs');\n }\n } else {\n externalPath = specifier;\n }\n\n return {\n external: true,\n path: externalPath,\n sideEffects: hasSideEffects || undefined,\n };\n }\n\n // No entriesToBundle — only override sideEffects when needed.\n // We must return the resolved `path` alongside `sideEffects` because\n // returning only `{ sideEffects: true }` without a path causes esbuild\n // to fall through to its own resolver, which re-reads the package.json\n // and applies `\"sideEffects\": false` from there.\n return hasSideEffects\n ? { path: resolvedPath, sideEffects: true }\n : null;\n } catch (_) {}\n return null;\n });\n\n // Handle TypeScript and JavaScript files\n build.onLoad({ filter: jsTsRegex }, async (args) => {\n // Determine if this is a TypeScript file\n try {\n // Determine the loader based on the output\n let loader: 'js' | 'jsx' | 'tsx' = 'js';\n if (args.path.endsWith('.jsx')) {\n loader = 'jsx';\n } else if (args.path.endsWith('.tsx')) {\n loader = 'tsx';\n }\n const source = await readFile(args.path, 'utf8');\n const normalizedSource = source\n .replace(/require\\(\\s*(['\"])server-only\\1\\s*\\)/g, 'void 0')\n .replace(/require\\(\\s*(['\"])client-only\\1\\s*\\)/g, 'void 0');\n\n // Calculate relative path for SWC plugin\n // The filename parameter is used to generate workflowId/stepId, so it must be relative\n const workingDir =\n build.initialOptions.absWorkingDir || process.cwd();\n const projectRoot = options.projectRoot || workingDir;\n // Normalize paths: convert backslashes to forward slashes and remove trailing slashes\n const normalizedWorkingDir = workingDir\n .replace(/\\\\/g, '/')\n .replace(/\\/$/, '');\n const normalizedPath = args.path.replace(/\\\\/g, '/');\n\n // Windows fix: Always do case-insensitive path comparison as the PRIMARY logic\n // to work around node:path.relative() not recognizing paths with different drive\n // letter casing (e.g., D: vs d:) as being in the same tree\n const lowerWd = normalizedWorkingDir.toLowerCase();\n const lowerPath = normalizedPath.toLowerCase();\n\n let relativeFilepath: string;\n if (lowerPath.startsWith(lowerWd + '/')) {\n // File is under working directory - manually calculate relative path\n // This ensures we get a relative path even with drive letter casing issues\n relativeFilepath = normalizedPath.substring(\n normalizedWorkingDir.length + 1\n );\n } else if (lowerPath === lowerWd) {\n // File IS the working directory\n relativeFilepath = '.';\n } else {\n // File is outside working directory - use relative() and strip ../ prefixes if needed\n relativeFilepath = relative(\n normalizedWorkingDir,\n normalizedPath\n ).replace(/\\\\/g, '/');\n\n // Handle files discovered outside the working directory\n // These come back as ../path/to/file, but we want just path/to/file\n if (relativeFilepath.startsWith('../')) {\n const aliasedRelativePath =\n await resolveWorkflowAliasRelativePath(args.path, workingDir);\n if (aliasedRelativePath) {\n relativeFilepath = aliasedRelativePath;\n } else {\n relativeFilepath = relativeFilepath\n .split('/')\n .filter((part) => part !== '..')\n .join('/');\n }\n }\n }\n\n // Final safety check - ensure we never pass an absolute path to SWC\n if (\n relativeFilepath.includes(':') ||\n relativeFilepath.startsWith('/')\n ) {\n // This should never happen, but if it does, use just the filename as last resort\n console.error(\n `[ERROR] relativeFilepath is still absolute: ${relativeFilepath}`\n );\n relativeFilepath = normalizedPath.split('/').pop() || 'unknown.ts';\n }\n\n const { code: transformedCode, workflowManifest } =\n await applySwcTransform(\n relativeFilepath,\n normalizedSource,\n options.mode,\n args.path, // Pass absolute path for module specifier resolution\n projectRoot\n );\n\n if (!options.workflowManifest) {\n options.workflowManifest = {};\n }\n\n options.workflowManifest.workflows = Object.assign(\n options.workflowManifest.workflows || {},\n workflowManifest.workflows\n );\n options.workflowManifest.steps = Object.assign(\n options.workflowManifest.steps || {},\n workflowManifest.steps\n );\n options.workflowManifest.classes = Object.assign(\n options.workflowManifest.classes || {},\n workflowManifest.classes\n );\n\n return {\n contents: transformedCode,\n loader,\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n console.error(\n `❌ SWC transform error in ${args.path}:`,\n errorMessage\n );\n return {\n errors: [\n {\n text: `SWC transform failed: ${errorMessage}`,\n location: { file: args.path, line: 0, column: 0 },\n },\n ],\n };\n }\n });\n },\n };\n}\n\nfunction isProjectLocalFile(filePath: string, projectRoot: string): boolean {\n if (normalizePath(filePath).includes('/node_modules/')) {\n return false;\n }\n\n return (\n resolveModuleSpecifier(filePath, projectRoot).moduleSpecifier === undefined\n );\n}\n"]}
@@ -9,6 +9,7 @@ const { applySwcTransformMock } = vi.hoisted(() => ({
9
9
  vi.mock('./apply-swc-transform.js', () => ({
10
10
  applySwcTransform: applySwcTransformMock,
11
11
  }));
12
+ import { createDiscoverEntriesPlugin, importParents, } from './discover-entries-esbuild-plugin.js';
12
13
  import { createSwcPlugin } from './swc-esbuild-plugin.js';
13
14
  const realTmpdir = realpathSync(tmpdir());
14
15
  function writeFile(path, contents = '') {
@@ -19,6 +20,7 @@ describe('createSwcPlugin externalizeNonSteps', () => {
19
20
  let testRoot;
20
21
  beforeEach(() => {
21
22
  testRoot = mkdtempSync(join(realTmpdir, 'workflow-swc-plugin-'));
23
+ importParents.clear();
22
24
  applySwcTransformMock.mockReset();
23
25
  applySwcTransformMock.mockImplementation(async (_filename, source) => ({
24
26
  code: source,
@@ -26,6 +28,7 @@ describe('createSwcPlugin externalizeNonSteps', () => {
26
28
  }));
27
29
  });
28
30
  afterEach(() => {
31
+ importParents.clear();
29
32
  rmSync(testRoot, { recursive: true, force: true });
30
33
  });
31
34
  it.each([
@@ -61,12 +64,16 @@ describe('createSwcPlugin externalizeNonSteps', () => {
61
64
  expect(output).toContain(`/dep${outputExt}`);
62
65
  expect(output).not.toContain(`/dep${inputExt}`);
63
66
  });
64
- it('rewrites path-aliased imports to relative paths', async () => {
67
+ it('bundles path-aliased project-local imports inline', async () => {
68
+ // Aliased project-local files must be bundled inline (not externalized
69
+ // as relative paths) because their source on disk may contain further
70
+ // alias imports that Node's ESM loader cannot resolve at runtime.
71
+ // See packages/builders/src/swc-esbuild-plugin.ts for full reasoning.
65
72
  const outdir = join(testRoot, 'out');
66
73
  const srcDir = join(testRoot, 'src');
67
74
  const libDir = join(srcDir, 'lib');
68
75
  const stepFile = join(srcDir, 'step.ts');
69
- writeFile(join(libDir, 'config.ts'), 'export const config = {};');
76
+ writeFile(join(libDir, 'config.ts'), 'export const config = { value: "hello-from-config" };');
70
77
  writeFile(stepFile, `import { config } from '@/lib/config';\nconsole.log(config);`);
71
78
  const result = await esbuild.build({
72
79
  entryPoints: [stepFile],
@@ -88,8 +95,63 @@ describe('createSwcPlugin externalizeNonSteps', () => {
88
95
  });
89
96
  expect(result.errors).toHaveLength(0);
90
97
  const output = result.outputFiles[0].text;
91
- expect(output).toContain('/lib/config.js');
98
+ // The aliased helper should be bundled inline (its content is in the
99
+ // output), not externalized as a relative path or left as a bare alias.
100
+ expect(output).toContain('hello-from-config');
92
101
  expect(output).not.toContain('@/lib/config');
102
+ expect(output).not.toMatch(/from\s+["'][^"']*\/lib\/config\.(js|ts)["']/);
103
+ });
104
+ it('bundles transitive aliased imports inside aliased helpers (Mux self-referencing package regression)', async () => {
105
+ // Regression test for https://github.com/muxinc/ai/pull/193.
106
+ //
107
+ // A package self-references its own subpath via tsconfig `paths` (e.g.
108
+ // `@my-pkg/lib/foo` → `src/lib/foo.ts`). A step file imports a helper
109
+ // via the alias, and that helper imports another helper via the alias.
110
+ //
111
+ // Previously the helpers were externalized as relative paths, but their
112
+ // source on disk still contained `import "@my-pkg/lib/..."`. At runtime,
113
+ // Node's ESM loader didn't know about tsconfig paths, fell through to
114
+ // the package's `exports` map, and threw `Package subpath ... is not
115
+ // defined by "exports"`.
116
+ //
117
+ // With the fix, aliased project-local files are bundled inline, so
118
+ // their alias imports are resolved at build time.
119
+ const outdir = join(testRoot, 'out');
120
+ const srcDir = join(testRoot, 'src');
121
+ const libDir = join(srcDir, 'lib');
122
+ const stepFile = join(srcDir, 'step.ts');
123
+ writeFile(join(libDir, 'providers.ts'), 'export const providerName = "anthropic";');
124
+ writeFile(join(libDir, 'client-factory.ts'),
125
+ // Helper uses the same alias to reach a sibling — this is the case
126
+ // that broke the Mux build with workflow >= 4.2.0-beta.78.
127
+ `import { providerName } from '@my-pkg/lib/providers';
128
+ export const client = { provider: providerName };`);
129
+ writeFile(stepFile, `import { client } from '@my-pkg/lib/client-factory';\nconsole.log(client);`);
130
+ const result = await esbuild.build({
131
+ entryPoints: [stepFile],
132
+ absWorkingDir: testRoot,
133
+ outdir,
134
+ bundle: true,
135
+ format: 'esm',
136
+ platform: 'node',
137
+ write: false,
138
+ alias: { '@my-pkg': srcDir },
139
+ plugins: [
140
+ createSwcPlugin({
141
+ mode: 'step',
142
+ entriesToBundle: [stepFile],
143
+ outdir,
144
+ rewriteTsExtensions: true,
145
+ }),
146
+ ],
147
+ });
148
+ expect(result.errors).toHaveLength(0);
149
+ const output = result.outputFiles[0].text;
150
+ // Both helpers should be bundled inline — no aliased specifiers should
151
+ // leak into the output, where Node's ESM loader would choke on them.
152
+ expect(output).toContain('anthropic');
153
+ expect(output).not.toContain('@my-pkg/lib/providers');
154
+ expect(output).not.toContain('@my-pkg/lib/client-factory');
93
155
  });
94
156
  it('does not relativize Node.js builtin imports', async () => {
95
157
  const outdir = join(testRoot, 'out');
@@ -150,6 +212,39 @@ describe('createSwcPlugin externalizeNonSteps', () => {
150
212
  expect(output).toContain('hello');
151
213
  expect(output).not.toMatch(/from\s+["'].*node_modules/);
152
214
  });
215
+ it('externalizes nested bare package imports that only resolve from a bundled package', async () => {
216
+ const outdir = join(testRoot, 'out');
217
+ const srcDir = join(testRoot, 'src');
218
+ const stepFile = join(srcDir, 'step.ts');
219
+ const parentPkgDir = join(testRoot, 'node_modules', 'parent-pkg');
220
+ const parentPkgIndex = join(parentPkgDir, 'index.js');
221
+ const nativePkgDir = join(parentPkgDir, 'node_modules', 'optional-native');
222
+ writeFile(join(parentPkgDir, 'package.json'), JSON.stringify({ name: 'parent-pkg', main: 'index.js' }));
223
+ writeFile(parentPkgIndex, `const native = require('optional-native');\nexports.value = native.value;`);
224
+ writeFile(join(nativePkgDir, 'package.json'), JSON.stringify({ name: 'optional-native', main: 'binding.node' }));
225
+ writeFile(join(nativePkgDir, 'binding.node'), '');
226
+ writeFile(stepFile, `import { value } from 'parent-pkg';\nconsole.log(value);`);
227
+ const result = await esbuild.build({
228
+ entryPoints: [stepFile],
229
+ absWorkingDir: testRoot,
230
+ outdir,
231
+ bundle: true,
232
+ format: 'esm',
233
+ platform: 'node',
234
+ write: false,
235
+ plugins: [
236
+ createSwcPlugin({
237
+ mode: 'step',
238
+ entriesToBundle: [stepFile, parentPkgIndex],
239
+ outdir,
240
+ }),
241
+ ],
242
+ });
243
+ expect(result.errors).toHaveLength(0);
244
+ const output = result.outputFiles[0].text;
245
+ expect(output).toContain('optional-native');
246
+ expect(output).not.toContain('binding.node');
247
+ });
153
248
  it.each([
154
249
  '.ts',
155
250
  '.tsx',
@@ -181,6 +276,187 @@ describe('createSwcPlugin externalizeNonSteps', () => {
181
276
  const output = result.outputFiles[0].text;
182
277
  expect(output).toContain(`/dep${inputExt}`);
183
278
  });
279
+ it('bundles transitive local TypeScript dependencies with extensionless imports', async () => {
280
+ const outdir = join(testRoot, 'out');
281
+ const stepFile = join(testRoot, 'server', 'workflows', 'my-workflow.ts');
282
+ const constantsFile = join(testRoot, 'shared', 'constants.ts');
283
+ const helpersFile = join(testRoot, 'shared', 'helpers.ts');
284
+ writeFile(helpersFile, `export const HELPER_VALUE = "from-helper";`);
285
+ writeFile(constantsFile, `import { HELPER_VALUE } from './helpers';\nexport const CATEGORIES = [HELPER_VALUE];`);
286
+ writeFile(stepFile, `import { CATEGORIES } from '../../shared/constants';\nexport async function myStep() {\n 'use step';\n return CATEGORIES[0];\n}`);
287
+ const state = {
288
+ discoveredSteps: new Set(),
289
+ discoveredWorkflows: new Set(),
290
+ discoveredSerdeFiles: new Set(),
291
+ };
292
+ await esbuild.build({
293
+ entryPoints: [stepFile],
294
+ absWorkingDir: testRoot,
295
+ bundle: true,
296
+ format: 'esm',
297
+ platform: 'node',
298
+ write: false,
299
+ resolveExtensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'],
300
+ plugins: [createDiscoverEntriesPlugin(state, testRoot)],
301
+ });
302
+ const result = await esbuild.build({
303
+ entryPoints: [stepFile],
304
+ absWorkingDir: testRoot,
305
+ outdir,
306
+ bundle: true,
307
+ format: 'esm',
308
+ platform: 'node',
309
+ write: false,
310
+ resolveExtensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'],
311
+ plugins: [
312
+ createSwcPlugin({
313
+ mode: 'step',
314
+ entriesToBundle: [stepFile],
315
+ outdir,
316
+ bundleTransitiveLocalStepDependencies: true,
317
+ }),
318
+ ],
319
+ });
320
+ expect(result.errors).toHaveLength(0);
321
+ const output = result.outputFiles[0].text;
322
+ expect(output).toContain('from-helper');
323
+ expect(output).not.toMatch(/from\s+["'][^"']*shared\/constants/);
324
+ expect(output).not.toMatch(/from\s+["'][^"']*shared\/helpers/);
325
+ });
326
+ it('externalizes transitive local TypeScript dependencies by default', async () => {
327
+ const outdir = join(testRoot, 'out');
328
+ const stepFile = join(testRoot, 'server', 'workflows', 'my-workflow.ts');
329
+ const constantsFile = join(testRoot, 'shared', 'constants.ts');
330
+ const helpersFile = join(testRoot, 'shared', 'helpers.ts');
331
+ writeFile(helpersFile, `export const HELPER_VALUE = "from-helper";`);
332
+ writeFile(constantsFile, `import { HELPER_VALUE } from './helpers';\nexport const CATEGORIES = [HELPER_VALUE];`);
333
+ writeFile(stepFile, `import { CATEGORIES } from '../../shared/constants';\nexport async function myStep() {\n 'use step';\n return CATEGORIES[0];\n}`);
334
+ const state = {
335
+ discoveredSteps: new Set(),
336
+ discoveredWorkflows: new Set(),
337
+ discoveredSerdeFiles: new Set(),
338
+ };
339
+ await esbuild.build({
340
+ entryPoints: [stepFile],
341
+ absWorkingDir: testRoot,
342
+ bundle: true,
343
+ format: 'esm',
344
+ platform: 'node',
345
+ write: false,
346
+ resolveExtensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'],
347
+ plugins: [createDiscoverEntriesPlugin(state, testRoot)],
348
+ });
349
+ const result = await esbuild.build({
350
+ entryPoints: [stepFile],
351
+ absWorkingDir: testRoot,
352
+ outdir,
353
+ bundle: true,
354
+ format: 'esm',
355
+ platform: 'node',
356
+ write: false,
357
+ resolveExtensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'],
358
+ plugins: [
359
+ createSwcPlugin({
360
+ mode: 'step',
361
+ entriesToBundle: [stepFile],
362
+ outdir,
363
+ }),
364
+ ],
365
+ });
366
+ expect(result.errors).toHaveLength(0);
367
+ const output = result.outputFiles[0].text;
368
+ expect(output).not.toContain('from-helper');
369
+ expect(output).toMatch(/from\s+["'][^"']*shared\/constants\.ts["']/);
370
+ });
371
+ it('keeps ordinary package dependencies external when reachable from a bundled step', async () => {
372
+ const outdir = join(testRoot, 'out');
373
+ const stepFile = join(testRoot, 'server', 'workflows', 'my-workflow.ts');
374
+ const pkgDir = join(testRoot, 'node_modules', 'plain-pkg');
375
+ const pkgIndex = join(pkgDir, 'index.js');
376
+ writeFile(join(pkgDir, 'package.json'), JSON.stringify({ name: 'plain-pkg', main: 'index.js' }));
377
+ writeFile(pkgIndex, `export const value = "from-package";`);
378
+ writeFile(stepFile, `import { value } from 'plain-pkg';\nexport async function myStep() {\n 'use step';\n return value;\n}`);
379
+ const state = {
380
+ discoveredSteps: new Set(),
381
+ discoveredWorkflows: new Set(),
382
+ discoveredSerdeFiles: new Set(),
383
+ };
384
+ await esbuild.build({
385
+ entryPoints: [stepFile],
386
+ absWorkingDir: testRoot,
387
+ bundle: true,
388
+ format: 'esm',
389
+ platform: 'node',
390
+ write: false,
391
+ plugins: [createDiscoverEntriesPlugin(state, testRoot)],
392
+ });
393
+ const result = await esbuild.build({
394
+ entryPoints: [stepFile],
395
+ absWorkingDir: testRoot,
396
+ outdir,
397
+ bundle: true,
398
+ format: 'esm',
399
+ platform: 'node',
400
+ write: false,
401
+ plugins: [
402
+ createSwcPlugin({
403
+ mode: 'step',
404
+ entriesToBundle: [stepFile],
405
+ outdir,
406
+ bundleTransitiveLocalStepDependencies: true,
407
+ }),
408
+ ],
409
+ });
410
+ expect(result.errors).toHaveLength(0);
411
+ const output = result.outputFiles[0].text;
412
+ expect(output).toMatch(/from\s+["']plain-pkg["']/);
413
+ expect(output).not.toContain('from-package');
414
+ });
415
+ it('bundles package parents when they lead to discovered workflow-related entries', async () => {
416
+ const outdir = join(testRoot, 'out');
417
+ const stepFile = join(testRoot, 'server', 'workflows', 'my-workflow.ts');
418
+ const pkgDir = join(testRoot, 'node_modules', 'workflow-pkg');
419
+ const pkgIndex = join(pkgDir, 'index.js');
420
+ const pkgSerde = join(pkgDir, 'serde.js');
421
+ writeFile(join(pkgDir, 'package.json'), JSON.stringify({ name: 'workflow-pkg', main: 'index.js' }));
422
+ writeFile(pkgSerde, `export const value = "from-serde";`);
423
+ writeFile(pkgIndex, `export { value } from './serde.js';`);
424
+ writeFile(stepFile, `import { value } from 'workflow-pkg';\nexport async function myStep() {\n 'use step';\n return value;\n}`);
425
+ const state = {
426
+ discoveredSteps: new Set(),
427
+ discoveredWorkflows: new Set(),
428
+ discoveredSerdeFiles: new Set(),
429
+ };
430
+ await esbuild.build({
431
+ entryPoints: [stepFile],
432
+ absWorkingDir: testRoot,
433
+ bundle: true,
434
+ format: 'esm',
435
+ platform: 'node',
436
+ write: false,
437
+ plugins: [createDiscoverEntriesPlugin(state, testRoot)],
438
+ });
439
+ const result = await esbuild.build({
440
+ entryPoints: [stepFile],
441
+ absWorkingDir: testRoot,
442
+ outdir,
443
+ bundle: true,
444
+ format: 'esm',
445
+ platform: 'node',
446
+ write: false,
447
+ plugins: [
448
+ createSwcPlugin({
449
+ mode: 'step',
450
+ entriesToBundle: [stepFile, pkgSerde],
451
+ outdir,
452
+ }),
453
+ ],
454
+ });
455
+ expect(result.errors).toHaveLength(0);
456
+ const output = result.outputFiles[0].text;
457
+ expect(output).toContain('from-serde');
458
+ expect(output).not.toMatch(/from\s+["']workflow-pkg["']/);
459
+ });
184
460
  });
185
461
  describe('createSwcPlugin sideEffectEntries', () => {
186
462
  let testRoot;