From aacfac1b71f1de78fbfdc1161c6e8cf32fd0629e Mon Sep 17 00:00:00 2001 From: Momo Kornher Date: Thu, 25 Nov 2021 19:17:55 +0000 Subject: [PATCH] feat: escape hatch to provide a custom build or transform function (#141) * feat: escape hatch to provide a custom build or transform function * docs: explain escape hatches --- .eslintrc.json | 1 + .gitignore | 1 + .projenrc.ts | 6 ++ API.md | 184 ++++++++++++++++++++++++++++++++++----- README.md | 50 +++++++++++ src/asset.ts | 2 + src/bundler.ts | 18 +++- src/index.ts | 1 + src/inline-code.ts | 101 ++++++++++++++++----- test/bundler.test.ts | 27 ++++++ test/code.test.ts | 29 ++++++ test/inline-code.test.ts | 61 ++++++++++++- 12 files changed, 434 insertions(+), 47 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 90c53cab..a3c7ad95 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -36,6 +36,7 @@ "*.js", "*.d.ts", "node_modules/", + "examples/", "*.generated.ts", "coverage", "!.projenrc.ts" diff --git a/.gitignore b/.gitignore index bf7ac9ed..5ebdb7d1 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ junit.xml .jsii tsconfig.json !/API.md +!/examples/** !/.vscode/extensions.json !/.vscode/settings.json !/.vscode/launch.json diff --git a/.projenrc.ts b/.projenrc.ts index 86070148..64e8c7aa 100644 --- a/.projenrc.ts +++ b/.projenrc.ts @@ -1,5 +1,6 @@ import { AwsCdkConstructLibrary, + IgnoreFile, JsonFile, NodePackageManager, release, @@ -126,11 +127,16 @@ eslintRc?.addOverride('ignorePatterns', [ '*.js', '*.d.ts', 'node_modules/', + 'examples/', '*.generated.ts', 'coverage', '!.projenrc.ts', ]); +(project.tryFindFile('.gitignore') as IgnoreFile).addPatterns( + '!/examples/**', +); + new JsonFile(project, '.vscode/extensions.json', { readonly: false, marker: false, diff --git a/API.md b/API.md index 9d956593..b0acc874 100644 --- a/API.md +++ b/API.md @@ -13,6 +13,22 @@ import { AssetProps } from '@mrgrain/cdk-esbuild' const assetProps: AssetProps = { ... } ``` +##### `buildFn`Optional + +```typescript +public readonly buildFn: any; +``` + +- *Type:* `any` +- *Default:* esbuild.buildSync + +Escape hatch to provide the bundler with a custom build function. + +The function will receive the computed options from the bundler. It can use with these options as it wishes, however `outdir`/`outfile` must be respected to integrate with CDK. +Must throw a `BuildFailure` on failure to correctly inform the bundler. + +--- + ##### `buildOptions`Optional ```typescript @@ -703,6 +719,22 @@ import { BundlerProps } from '@mrgrain/cdk-esbuild' const bundlerProps: BundlerProps = { ... } ``` +##### `buildFn`Optional + +```typescript +public readonly buildFn: any; +``` + +- *Type:* `any` +- *Default:* esbuild.buildSync + +Escape hatch to provide the bundler with a custom build function. + +The function will receive the computed options from the bundler. It can use with these options as it wishes, however `outdir`/`outfile` must be respected to integrate with CDK. +Must throw a `BuildFailure` on failure to correctly inform the bundler. + +--- + ##### `buildOptions`Optional ```typescript @@ -775,6 +807,22 @@ import { JavaScriptCodeProps } from '@mrgrain/cdk-esbuild' const javaScriptCodeProps: JavaScriptCodeProps = { ... } ``` +##### `buildFn`Optional + +```typescript +public readonly buildFn: any; +``` + +- *Type:* `any` +- *Default:* esbuild.buildSync + +Escape hatch to provide the bundler with a custom build function. + +The function will receive the computed options from the bundler. It can use with these options as it wishes, however `outdir`/`outfile` must be respected to integrate with CDK. +Must throw a `BuildFailure` on failure to correctly inform the bundler. + +--- + ##### `buildOptions`Optional ```typescript @@ -841,6 +889,22 @@ import { JavaScriptSourceProps } from '@mrgrain/cdk-esbuild' const javaScriptSourceProps: JavaScriptSourceProps = { ... } ``` +##### `buildFn`Optional + +```typescript +public readonly buildFn: any; +``` + +- *Type:* `any` +- *Default:* esbuild.buildSync + +Escape hatch to provide the bundler with a custom build function. + +The function will receive the computed options from the bundler. It can use with these options as it wishes, however `outdir`/`outfile` must be respected to integrate with CDK. +Must throw a `BuildFailure` on failure to correctly inform the bundler. + +--- + ##### `buildOptions`Optional ```typescript @@ -897,6 +961,48 @@ Defaults to a hash of all files in the resulting bundle. --- +### TransformerProps + +#### Initializer + +```typescript +import { TransformerProps } from '@mrgrain/cdk-esbuild' + +const transformerProps: TransformerProps = { ... } +``` + +##### `transformFn`Optional + +```typescript +public readonly transformFn: any; +``` + +- *Type:* `any` +- *Default:* esbuild.transformSync + +Escape hatch to provide the bundler with a custom transform function. + +The function will receive the computed options from the bundler. It can use with these options as it wishes, however a TransformResult must be returned to integrate with CDK. +Must throw a `TransformFailure` on failure to correctly inform the bundler. + +--- + +##### `transformOptions`Optional + +```typescript +public readonly transformOptions: TransformOptions; +``` + +- *Type:* [`@mrgrain/cdk-esbuild.TransformOptions`](#@mrgrain/cdk-esbuild.TransformOptions) + +Transform options passed on to esbuild. + +Please refer to the esbuild Transform API docs for details. + +> https://esbuild.github.io/api/#transform-api + +--- + ### TransformOptions #### Initializer @@ -1243,6 +1349,22 @@ import { TypeScriptCodeProps } from '@mrgrain/cdk-esbuild' const typeScriptCodeProps: TypeScriptCodeProps = { ... } ``` +##### `buildFn`Optional + +```typescript +public readonly buildFn: any; +``` + +- *Type:* `any` +- *Default:* esbuild.buildSync + +Escape hatch to provide the bundler with a custom build function. + +The function will receive the computed options from the bundler. It can use with these options as it wishes, however `outdir`/`outfile` must be respected to integrate with CDK. +Must throw a `BuildFailure` on failure to correctly inform the bundler. + +--- + ##### `buildOptions`Optional ```typescript @@ -1309,6 +1431,22 @@ import { TypeScriptSourceProps } from '@mrgrain/cdk-esbuild' const typeScriptSourceProps: TypeScriptSourceProps = { ... } ``` +##### `buildFn`Optional + +```typescript +public readonly buildFn: any; +``` + +- *Type:* `any` +- *Default:* esbuild.buildSync + +Escape hatch to provide the bundler with a custom build function. + +The function will receive the computed options from the bundler. It can use with these options as it wishes, however `outdir`/`outfile` must be respected to integrate with CDK. +Must throw a `BuildFailure` on failure to correctly inform the bundler. + +--- + ##### `buildOptions`Optional ```typescript @@ -1463,7 +1601,7 @@ An implementation of `lambda.InlineCode` using the esbuild Transform API. Inline ```typescript import { InlineJavaScriptCode } from '@mrgrain/cdk-esbuild' -new InlineJavaScriptCode(code: string, transformOptions?: TransformOptions) +new InlineJavaScriptCode(code: string, props?: TransformOptions | TransformerProps) ``` ##### `code`Required @@ -1474,14 +1612,15 @@ The inline code to be transformed. --- -##### `transformOptions`Optional +##### `props`Optional -- *Type:* [`@mrgrain/cdk-esbuild.TransformOptions`](#@mrgrain/cdk-esbuild.TransformOptions) +- *Type:* [`@mrgrain/cdk-esbuild.TransformOptions`](#@mrgrain/cdk-esbuild.TransformOptions) | [`@mrgrain/cdk-esbuild.TransformerProps`](#@mrgrain/cdk-esbuild.TransformerProps) -Transform options passed on to esbuild. +Support for `TransformOptions` is deprecated. Please provide `TransformerProps`! -Please refer to the esbuild Transform API docs for details. \ -Default values for `transformOptions`: +Props to change the behaviour of the transformer. + +Default values for `props.transformOptions`: - `loader='js'` > https://esbuild.github.io/api/#transform-api @@ -1501,7 +1640,7 @@ An implementation of `lambda.InlineCode` using the esbuild Transform API. Inline ```typescript import { InlineJsxCode } from '@mrgrain/cdk-esbuild' -new InlineJsxCode(code: string, transformOptions?: TransformOptions) +new InlineJsxCode(code: string, props?: TransformOptions | TransformerProps) ``` ##### `code`Required @@ -1512,13 +1651,14 @@ The inline code to be transformed. --- -##### `transformOptions`Optional +##### `props`Optional -- *Type:* [`@mrgrain/cdk-esbuild.TransformOptions`](#@mrgrain/cdk-esbuild.TransformOptions) +- *Type:* [`@mrgrain/cdk-esbuild.TransformOptions`](#@mrgrain/cdk-esbuild.TransformOptions) | [`@mrgrain/cdk-esbuild.TransformerProps`](#@mrgrain/cdk-esbuild.TransformerProps) -Transform options passed on to esbuild. +Support for `TransformOptions` is deprecated. Please provide `TransformerProps`! + +Props to change the behaviour of the transformer. -Please refer to the esbuild Transform API docs for details. \ Default values for `transformOptions`: - `loader='jsx'` @@ -1539,7 +1679,7 @@ An implementation of `lambda.InlineCode` using the esbuild Transform API. Inline ```typescript import { InlineTsxCode } from '@mrgrain/cdk-esbuild' -new InlineTsxCode(code: string, transformOptions?: TransformOptions) +new InlineTsxCode(code: string, props?: TransformOptions | TransformerProps) ``` ##### `code`Required @@ -1550,13 +1690,14 @@ The inline code to be transformed. --- -##### `transformOptions`Optional +##### `props`Optional -- *Type:* [`@mrgrain/cdk-esbuild.TransformOptions`](#@mrgrain/cdk-esbuild.TransformOptions) +- *Type:* [`@mrgrain/cdk-esbuild.TransformOptions`](#@mrgrain/cdk-esbuild.TransformOptions) | [`@mrgrain/cdk-esbuild.TransformerProps`](#@mrgrain/cdk-esbuild.TransformerProps) -Transform options passed on to esbuild. +Support for `TransformOptions` is deprecated. Please provide `TransformerProps`! + +Props to change the behaviour of the transformer. -Please refer to the esbuild Transform API docs for details. \ Default values for `transformOptions`: - `loader='tsx'` @@ -1577,7 +1718,7 @@ An implementation of `lambda.InlineCode` using the esbuild Transform API. Inline ```typescript import { InlineTypeScriptCode } from '@mrgrain/cdk-esbuild' -new InlineTypeScriptCode(code: string, transformOptions?: TransformOptions) +new InlineTypeScriptCode(code: string, props?: TransformOptions | TransformerProps) ``` ##### `code`Required @@ -1588,13 +1729,14 @@ The inline code to be transformed. --- -##### `transformOptions`Optional +##### `props`Optional -- *Type:* [`@mrgrain/cdk-esbuild.TransformOptions`](#@mrgrain/cdk-esbuild.TransformOptions) +- *Type:* [`@mrgrain/cdk-esbuild.TransformOptions`](#@mrgrain/cdk-esbuild.TransformOptions) | [`@mrgrain/cdk-esbuild.TransformerProps`](#@mrgrain/cdk-esbuild.TransformerProps) -Transform options passed on to esbuild. +Support for `TransformOptions` is deprecated. Please provide `TransformerProps`! + +Props to change the behaviour of the transformer. -Please refer to the esbuild Transform API docs for details. \ Default values for `transformOptions`: - `loader='ts'` diff --git a/README.md b/README.md index 0c345cae..df951132 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,56 @@ Underlying classes power the other features. You normally won't have to use them Auto-generated reference for classes and structs. This information is also available within the code completion of your IDE. +## Escape hatches + +It's possible that you want to use a implementation of esbuild that's different to the default one. Common reasons are: + +- The current version constraints for esbuild are not suitable +- To use version of esbuild that is installed by any other means than `npm`, including Docker +- Plugin support is needed for the building + +For these situations, this package offers an escape hatch to bypass regular the implementation and provide a custom build and transform function. + +### Custom build function + +Constructs that result in starting a build, take a `buildFn` as optional prop. While the defined type for this function is `any`, it must implement the same signature as esbuild's `buildSync` function. + +```ts +new TypeScriptCode("fixtures/handlers/ts-handler.ts", { + buildFn: (options: BuildOptions): BuildResult => { + try { + // custom implementation returning BuildResult + } catch (error) { + // throw BuildFailure exception here + } + }, +}); +``` + +Instead of esbuild, the provided function will be invoked with the calculated build options. The custom build function can amend, change or discard any of these. However integration with CDK relies heavily on the values `outdir`/`outfile` are set to and it's usually required to use them unchanged. + +Failures have to cause a `BuildFailure` exception in order to be fully handled. + +### Custom transform function + +Constructs that result in starting a transformation, take a `transformFn` as optional prop. While the defined type for this function is `any`, it must implement the same signature as esbuild's `transformSync` function. + +```ts +new InlineTypeScriptCode("let x: number = 1", { + transformFn: (options: TransformOptions): TransformResult => { + try { + // custom implementation returning TransformResult + } catch (error) { + // throw TransformFailure exception here + } + },, +}); +``` + +Instead of esbuild, the provided function will be invoked with the calculated transform options. The custom transform function can amend, change or discard any of these. + +Failures have to cause a `TransformFailure` exception in order to be fully handled. + ## Migrating to v2 The main changes in cdk-esbuild v2 are: diff --git a/src/asset.ts b/src/asset.ts index bd105308..09ef59d4 100644 --- a/src/asset.ts +++ b/src/asset.ts @@ -47,6 +47,7 @@ abstract class Asset extends S3Asset { assetHash, copyDir, buildOptions: options = {}, + buildFn, } = props; const entryPoints: string[] | Record = typeof props.entryPoints === 'string' ? [props.entryPoints] : props.entryPoints; @@ -78,6 +79,7 @@ abstract class Asset extends S3Asset { { buildOptions, copyDir, + buildFn, }, ), }); diff --git a/src/bundler.ts b/src/bundler.ts index f71c9e15..ed607ab4 100644 --- a/src/bundler.ts +++ b/src/bundler.ts @@ -47,6 +47,20 @@ export interface BundlerProps { * @stability stable */ readonly copyDir?: string; + + + /** + * Escape hatch to provide the bundler with a custom build function. + * The function will receive the computed options from the bundler. It can use with these options as it wishes, however `outdir`/`outfile` must be respected to integrate with CDK. + * Must throw a `BuildFailure` on failure to correctly inform the bundler. + * + * @stability experimental + * @type esbuild.buildSync + * @returns esbuild.BuildResult + * @throws esbuild.BuildFailure + * @default esbuild.buildSync + */ + readonly buildFn?: any; } /** @@ -93,6 +107,8 @@ export class EsbuildBundler { throw new Error('Cannot use both "outfile" and "outdir"'); } + const { buildFn = buildSync } = this.props; + this.local = { tryBundle: (outputDir: string, _options: BundlingOptions): boolean => { try { @@ -106,7 +122,7 @@ export class EsbuildBundler { ); } - const buildResult: BuildResult = buildSync({ + const buildResult: BuildResult = buildFn({ entryPoints, ...(this.props?.buildOptions || {}), ...this.getOutputOptions(outputDir, { normalize, join }), diff --git a/src/index.ts b/src/index.ts index ae5ddd11..057de761 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ export { } from './code'; export { + TransformerProps, InlineJavaScriptCode, InlineJsxCode, InlineTsxCode, diff --git a/src/inline-code.ts b/src/inline-code.ts index 7f047bc7..50a33063 100644 --- a/src/inline-code.ts +++ b/src/inline-code.ts @@ -1,30 +1,80 @@ import { InlineCode } from '@aws-cdk/aws-lambda'; -import { TransformOptions, Loader, BuildFailure } from './esbuild-types'; +import { TransformOptions, Loader, TransformFailure } from './esbuild-types'; import { transformSync } from './esbuild-wrapper'; import { printBuildMessages } from './formatMessages'; +/** + * @stability experimental + */ +export interface TransformerProps { + /** + * Transform options passed on to esbuild. Please refer to the esbuild Transform API docs for details. + * + * @see https://esbuild.github.io/api/#transform-api + * @stability stable + */ + readonly transformOptions?: TransformOptions; + + /** + * Escape hatch to provide the bundler with a custom transform function. + * The function will receive the computed options from the bundler. It can use with these options as it wishes, however a TransformResult must be returned to integrate with CDK. + * Must throw a `TransformFailure` on failure to correctly inform the bundler. + * + * @stability experimental + * @type esbuild.transformSync + * @returns esbuild.TransformResult + * @throws esbuild.TransformFailure + * @default esbuild.transformSync + */ + readonly transformFn?: any; +} + abstract class BaseInlineCode extends InlineCode { public constructor( code: string, - loader: Loader, - transformOptions: TransformOptions = {}, + props: TransformerProps, ) { + + const { transformFn = transformSync, transformOptions = {} } = props; + try { - const transformedCode = transformSync(code, { - loader, + const transformedCode = transformFn(code, { ...transformOptions, }); printBuildMessages(transformedCode, { prefix: 'Transform ' }); super(transformedCode.code); } catch (error) { - printBuildMessages(error as BuildFailure, { prefix: 'Transform ' }); + printBuildMessages(error as TransformFailure, { prefix: 'Transform ' }); throw new Error('Failed to transform InlineCode'); } } } +function instanceOfTransformerProps(object: any): object is TransformerProps { + return 'transformOptions' in object || 'transformFn' in object; +} + +function transformerProps(loader: Loader, props?: TransformerProps | TransformOptions): TransformerProps { + if (!props) { + return { transformOptions: { loader } }; + } + + if (!instanceOfTransformerProps(props) ) { + return { transformOptions: { loader, ...props } }; + } + + return { + ...props, + transformOptions: { + loader, + ...props.transformOptions, + }, + }; +} + + /** * An implementation of `lambda.InlineCode` using the esbuild Transform API. Inline function code is limited to 4 KiB after transformation. * @@ -39,18 +89,20 @@ export class InlineJavaScriptCode extends BaseInlineCode { */ code: string, /** - * Transform options passed on to esbuild. + * Support for `TransformOptions` is deprecated. Please provide `TransformerProps`! * - * Please refer to the esbuild Transform API docs for details. \ - * Default values for `transformOptions`: + * Props to change the behaviour of the transformer. + * + * Default values for `props.transformOptions`: * - `loader='js'` * * @see https://esbuild.github.io/api/#transform-api * @stability experimental */ - transformOptions?: TransformOptions, + props?: TransformerProps | TransformOptions, ) { - super(code, 'js', transformOptions); + + super(code, transformerProps('js', props)); } } @@ -68,18 +120,19 @@ export class InlineJsxCode extends BaseInlineCode { */ code: string, /** - * Transform options passed on to esbuild. + * Support for `TransformOptions` is deprecated. Please provide `TransformerProps`! + * + * Props to change the behaviour of the transformer. * - * Please refer to the esbuild Transform API docs for details. \ * Default values for `transformOptions`: * - `loader='jsx'` * * @see https://esbuild.github.io/api/#transform-api * @stability experimental */ - transformOptions?: TransformOptions, + props?: TransformerProps | TransformOptions, ) { - super(code, 'jsx', transformOptions); + super(code, transformerProps('jsx', props)); } } @@ -97,18 +150,19 @@ export class InlineTypeScriptCode extends BaseInlineCode { */ code: string, /** - * Transform options passed on to esbuild. + * Support for `TransformOptions` is deprecated. Please provide `TransformerProps`! + * + * Props to change the behaviour of the transformer. * - * Please refer to the esbuild Transform API docs for details. \ * Default values for `transformOptions`: * - `loader='ts'` * * @see https://esbuild.github.io/api/#transform-api * @stability experimental */ - transformOptions?: TransformOptions, + props?: TransformerProps | TransformOptions, ) { - super(code, 'ts', transformOptions); + super(code, transformerProps('ts', props)); } } @@ -126,17 +180,18 @@ export class InlineTsxCode extends BaseInlineCode { */ code: string, /** - * Transform options passed on to esbuild. + * Support for `TransformOptions` is deprecated. Please provide `TransformerProps`! + * + * Props to change the behaviour of the transformer. * - * Please refer to the esbuild Transform API docs for details. \ * Default values for `transformOptions`: * - `loader='tsx'` * * @see https://esbuild.github.io/api/#transform-api * @stability experimental */ - transformOptions?: TransformOptions, + props?: TransformerProps | TransformOptions, ) { - super(code, 'tsx', transformOptions); + super(code, transformerProps('tsx', props)); } } diff --git a/test/bundler.test.ts b/test/bundler.test.ts index 27a8e1a9..06bc55ff 100644 --- a/test/bundler.test.ts +++ b/test/bundler.test.ts @@ -101,4 +101,31 @@ describe('bundling', () => { }).toThrowError('Cannot use both "outfile" and "outdir"'); }); }); + + + describe('Given a custom build function', () => { + it('should call my build function', () => { + const customBuild = jest.fn().mockImplementation(() => ({ + errors: [], + warnings: [], + })); + + + const bundler = new EsbuildBundler( + ['index.ts'], + { + buildOptions: { absWorkingDir: '/project', outdir: 'js' }, + buildFn: customBuild, + }, + ); + + bundler.local?.tryBundle('cdk.out/123456', bundler); + + expect(mocked(customBuild)).toHaveBeenCalledWith( + expect.objectContaining({ + outdir: 'cdk.out/123456/js', + }), + ); + }); + }); }); diff --git a/test/code.test.ts b/test/code.test.ts index 6622995a..45a0fd97 100644 --- a/test/code.test.ts +++ b/test/code.test.ts @@ -7,7 +7,9 @@ import { Test, } from '@aws-cdk/aws-synthetics'; import { Stack } from '@aws-cdk/core'; +import { mocked } from 'ts-jest/utils'; import { JavaScriptCode, TypeScriptCode } from '../src/code'; +import { buildSync } from '../src/esbuild-wrapper'; describe('code', () => { describe('entry is an absolute path', () => { @@ -86,6 +88,33 @@ describe('code', () => { }).not.toThrow(); }); }); + + describe('Given a custom build function', () => { + it('should call my build function', () => { + const customBuild = jest.fn(buildSync); + + expect(() => { + const stack = new Stack(); + + const code = new TypeScriptCode('fixtures/handlers/ts-handler.ts', { + buildOptions: { absWorkingDir: resolve(__dirname) }, + buildFn: customBuild, + }); + + new Function(stack, 'MyFunction', { + runtime: LambdaRuntime.NODEJS_14_X, + handler: 'index.handler', + code, + }); + }).not.toThrow(); + + expect(mocked(customBuild)).toHaveBeenCalledWith( + expect.objectContaining({ + entryPoints: ['fixtures/handlers/ts-handler.ts'], + }), + ); + }); + }); }); describe('AWS Lambda', () => { diff --git a/test/inline-code.test.ts b/test/inline-code.test.ts index e0f3fcc2..1fc706d5 100644 --- a/test/inline-code.test.ts +++ b/test/inline-code.test.ts @@ -12,7 +12,28 @@ jest.mock('../src/formatMessages', () => ({ printBuildMessages: jest.fn(), })); -describe('inline-code', () => { +describe('using transformOptions', () => { + beforeEach(() => { + mocked(printBuildMessages).mockReset(); + }); + + describe('given a banner code', () => { + it('should add the banner before the code', () => { + const code = new InlineJavaScriptCode( + "const banana = 'fruit' ?? 'vegetable'", + { + banner: '/** BANNER */', + }, + ); + + const { inlineCode } = code.bind(new Stack()); + + expect(inlineCode).toBe('/** BANNER */\nconst banana = "fruit";\n'); + }); + }); +}); + +describe('using transformerProps', () => { beforeEach(() => { mocked(printBuildMessages).mockReset(); }); @@ -77,4 +98,40 @@ describe('inline-code', () => { expect(mocked(printBuildMessages)).toHaveBeenCalledTimes(1); }); }); -}); + + describe('given a banner code', () => { + it('should add the banner before the code', () => { + const code = new InlineJavaScriptCode( + "const banana = 'fruit' ?? 'vegetable'", + { + transformOptions: { banner: '/** BANNER */' }, + }, + ); + + const { inlineCode } = code.bind(new Stack()); + + expect(inlineCode).toBe('/** BANNER */\nconst banana = "fruit";\n'); + }); + }); + + describe('given a custom transform function', () => { + it('should call my transform function', () => { + const customTransform = jest.fn().mockImplementation(() => ({ + code: 'console.log("test");', + map: '', + warnings: [], + })); + + const code = new InlineTypeScriptCode('let x: number = 1', { + transformFn: customTransform, + }); + const { inlineCode } = code.bind(new Stack()); + + expect(inlineCode).toBe('console.log("test");'); + expect(mocked(customTransform)).toHaveBeenCalledWith( + expect.stringContaining('let x: number = 1'), + expect.anything(), + ); + }); + }); +}); \ No newline at end of file