From 822338103f95fafc4857655e19e455b1d0e5dfec Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Wed, 22 Jul 2026 17:20:51 +0100 Subject: [PATCH 1/4] Change how ensureExports gets fed into the compiler Let the CLI only the transforms rather than toggling off options --- packages/cli/src/compile/compile.ts | 11 +++++------ packages/compiler/src/compile.ts | 7 ++++--- packages/compiler/src/index.ts | 1 + packages/compiler/src/transform.ts | 2 -- packages/compiler/src/transforms/exports-only.ts | 8 +------- packages/compiler/src/transforms/index.ts | 9 +++++++++ 6 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 packages/compiler/src/transforms/index.ts diff --git a/packages/cli/src/compile/compile.ts b/packages/cli/src/compile/compile.ts index 30c9959f1..24ae8ffde 100644 --- a/packages/cli/src/compile/compile.ts +++ b/packages/cli/src/compile/compile.ts @@ -5,6 +5,7 @@ import compile, { preloadAdaptorExports, Options, getExports, + transformers as t, } from '@openfn/compiler'; import { getModulePath, type ExecutionPlan, type Job } from '@openfn/runtime'; import type { SourceMapWithOperations } from '@openfn/lexicon'; @@ -56,7 +57,11 @@ const compileJob = async ( jobName?: string ): Promise => { try { + let transformers; const compilerOptions: Options = await loadTransformOptions(opts, log); + if (opts.exportsOnly) { + transformers = [t.exportsOnly, t.lazyState, t.promises, t.addImports]; + } if (jobName) { compilerOptions.name = jobName; } @@ -141,12 +146,6 @@ export const loadTransformOptions = async ( trace: opts.trace, }; - if (opts.exportsOnly) { - options['exports-only'] = true; - // ensure-exports and top-level-operations produce output incompatible with exports-only mode - options['ensure-exports'] = false; - options['top-level-operations'] = false; - } if (opts.adaptors?.length && opts.ignoreImports != true) { const adaptorsConfig = []; for (const adaptorInput of opts.adaptors) { diff --git a/packages/compiler/src/compile.ts b/packages/compiler/src/compile.ts index 297682cbe..9899c08c3 100644 --- a/packages/compiler/src/compile.ts +++ b/packages/compiler/src/compile.ts @@ -2,7 +2,7 @@ import { print } from 'recast'; import createLogger, { Logger } from '@openfn/logger'; import parse from './parse'; -import transform, { TransformOptions } from './transform'; +import transform, { TransformOptions, Transformer } from './transform'; import { isPath, loadFile } from './util'; import type { SourceMapWithOperations } from '@openfn/lexicon'; @@ -23,7 +23,8 @@ export type Options = TransformOptions & { export default function compile( pathOrSource: string, - options: Options = {} + options: Options = {}, + transformers?: Transformer[] ): { code: string; map?: SourceMapWithOperations; @@ -42,7 +43,7 @@ export default function compile( const name = options.name ?? 'src'; const trace = options.trace; const ast = parse(source, { logger, name, trace }); - const transformedAst = transform(ast, undefined, options); + const transformedAst = transform(ast, transformers, options); const { code, map } = print(transformedAst, { sourceMapName: `${name}.map.js`, diff --git a/packages/compiler/src/index.ts b/packages/compiler/src/index.ts index 401388960..00b94ed5a 100644 --- a/packages/compiler/src/index.ts +++ b/packages/compiler/src/index.ts @@ -2,6 +2,7 @@ import compile from './compile'; export { default as getExports } from './get-exports'; export * from './util'; +export * as transformers from './transforms/index'; export type { TransformOptions } from './transform'; export type { Options } from './compile'; export default compile; diff --git a/packages/compiler/src/transform.ts b/packages/compiler/src/transform.ts index bac5e4f69..367b9fa53 100644 --- a/packages/compiler/src/transform.ts +++ b/packages/compiler/src/transform.ts @@ -5,7 +5,6 @@ import createLogger, { Logger } from '@openfn/logger'; import addImports, { AddImportsOptions } from './transforms/add-imports'; import ensureExports from './transforms/ensure-exports'; -import exportsOnly from './transforms/exports-only'; import lazyState from './transforms/lazy-state'; import promises from './transforms/promises'; import topLevelOps, { @@ -63,7 +62,6 @@ export default function transform( if (!transformers) { transformers = [ - exportsOnly, lazyState, promises, ensureExports, diff --git a/packages/compiler/src/transforms/exports-only.ts b/packages/compiler/src/transforms/exports-only.ts index 6a51df5b6..c07e81024 100644 --- a/packages/compiler/src/transforms/exports-only.ts +++ b/packages/compiler/src/transforms/exports-only.ts @@ -40,13 +40,7 @@ const declaresName = (node: any, names: Set) => { return false; }; -function visitor( - programPath: NodePath, - _logger: any, - options: boolean | {} = {} -) { - if (options !== true) return; - +function visitor(programPath: NodePath) { const { body } = programPath.node; const exportListNames = findExportListNames(body); diff --git a/packages/compiler/src/transforms/index.ts b/packages/compiler/src/transforms/index.ts new file mode 100644 index 000000000..765e80e99 --- /dev/null +++ b/packages/compiler/src/transforms/index.ts @@ -0,0 +1,9 @@ +export { default as addImports } from './add-imports'; +export { default as ensureExports } from './ensure-exports'; +export { default as exportsOnly } from './exports-only'; +export { default as lazyState } from './lazy-state'; +export { default as promises } from './promises'; +export { default as topLevelOps } from './top-level-operations'; + +export type { AddImportsOptions } from './add-imports'; +export type { TopLevelOpsOptions } from './top-level-operations'; From 0376650d5942e233f7f448cace3d230c1e737146 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Wed, 22 Jul 2026 17:33:47 +0100 Subject: [PATCH 2/4] update tests --- packages/compiler/test/compile.test.ts | 34 ++----------------- .../test/transforms/exports-only.test.ts | 10 ------ 2 files changed, 3 insertions(+), 41 deletions(-) diff --git a/packages/compiler/test/compile.test.ts b/packages/compiler/test/compile.test.ts index 0f44148f5..529f3889f 100644 --- a/packages/compiler/test/compile.test.ts +++ b/packages/compiler/test/compile.test.ts @@ -2,6 +2,7 @@ import test from 'ava'; import fs from 'node:fs/promises'; import path from 'node:path'; import compile from '../src/compile'; +import exportsOnly from '../src/transforms/exports-only'; // Not doing deep testing on this because recast does the heavy lifting // This is just to ensure the map is actually generated @@ -285,46 +286,17 @@ const exportsOnlyOpts = { 'top-level-operations': false, } as const; -test('exports-only: removes top-level operations, keeps exported JS', (t) => { +test('only run transformers that are passed in', (t) => { const source = [ 'export const formatDate = (d) => d.toISOString();', 'get("/api");', 'fn(state => ({ ...state, date: formatDate(state.data.date) }));', ].join('\n'); - const { code: result } = compile(source, exportsOnlyOpts); + const { code: result } = compile(source, exportsOnlyOpts, [exportsOnly]); t.true(result.includes('export const formatDate')); t.false(result.includes('get(')); t.false(result.includes('fn(state')); t.false(result.includes('export default []')); }); - -test('exports-only: removes non-exported declarations', (t) => { - const source = [ - 'const formatDate = (d) => d.toISOString();', - 'get("/api");', - ].join('\n'); - - const { code: result } = compile(source, exportsOnlyOpts); - - // non-exported const is dropped; no export default [] - t.false(result.includes('const formatDate')); - t.false(result.includes('get(')); - t.false(result.includes('export default []')); -}); - -test('exports-only: keeps import statements', (t) => { - const source = [ - 'import { dateFns } from "@openfn/language-common";', - 'export const formatDate = (d) => dateFns.format(d);', - 'get("/api");', - 'export default [];', - ].join('\n'); - - const { code: result } = compile(source, exportsOnlyOpts); - - t.true(result.includes('import { dateFns }')); - t.true(result.includes('export const formatDate')); - t.false(result.includes('get(')); -}); diff --git a/packages/compiler/test/transforms/exports-only.test.ts b/packages/compiler/test/transforms/exports-only.test.ts index cc9b1c64a..49302cddb 100644 --- a/packages/compiler/test/transforms/exports-only.test.ts +++ b/packages/compiler/test/transforms/exports-only.test.ts @@ -27,16 +27,6 @@ fn();`; t.is(after, before); }); -test('is a no-op if no options are passed', (t) => { - const before = `const x = 42; -fn();`; - const ast = parse(before); - const transformed = transform(ast, [visitors]); - const after = print(transformed).code; - - t.is(after, before); -}); - test('strips operation calls', (t) => { const before = `get(); fn();`; From 315a5f04d32f0648e9e1f3ba39ee2a69e2d54d15 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Wed, 22 Jul 2026 17:36:01 +0100 Subject: [PATCH 3/4] fix types --- packages/cli/src/compile/compile.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/compile/compile.ts b/packages/cli/src/compile/compile.ts index 24ae8ffde..01c98837b 100644 --- a/packages/cli/src/compile/compile.ts +++ b/packages/cli/src/compile/compile.ts @@ -57,7 +57,7 @@ const compileJob = async ( jobName?: string ): Promise => { try { - let transformers; + let transformers: any = undefined; const compilerOptions: Options = await loadTransformOptions(opts, log); if (opts.exportsOnly) { transformers = [t.exportsOnly, t.lazyState, t.promises, t.addImports]; @@ -65,7 +65,7 @@ const compileJob = async ( if (jobName) { compilerOptions.name = jobName; } - return compile(job, compilerOptions); + return compile(job, compilerOptions, transformers); } catch (e: any) { abort( log, From ce29445772f68053b245460e01b8d0b24a9ba925 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Wed, 22 Jul 2026 17:40:59 +0100 Subject: [PATCH 4/4] remove test --- packages/cli/test/compile/compile.test.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/cli/test/compile/compile.test.ts b/packages/cli/test/compile/compile.test.ts index da1c7c9c9..cab8e333b 100644 --- a/packages/cli/test/compile/compile.test.ts +++ b/packages/cli/test/compile/compile.test.ts @@ -495,17 +495,6 @@ test.serial('loadTransformOptions: ignore some imports', async (t) => { // TODO test exception if the module can't be found -test.serial( - 'loadTransformOptions: --exports-only enables exports-only transformer', - async (t) => { - const opts = { exportsOnly: true } as CompileOptions; - const result = await loadTransformOptions(opts, mockLog); - t.is(result['exports-only'], true); - t.is(result['ensure-exports'], false); - t.is(result['top-level-operations'], false); - } -); - test.serial( 'loadTransformOptions: --exports-only does not affect other options', async (t) => {