Skip to content

Commit

Permalink
feat: lazy evaluate InlineCode and show message before transforming (
Browse files Browse the repository at this point in the history
  • Loading branch information
mrgrain authored Aug 14, 2022
1 parent 1abd267 commit 98f7d1d
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 28 deletions.
104 changes: 104 additions & 0 deletions API.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 37 additions & 18 deletions src/inline-code.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { InlineCode } from 'aws-cdk-lib/aws-lambda';
import { Lazy, Stack } from 'aws-cdk-lib';
import { CodeConfig, InlineCode } from 'aws-cdk-lib/aws-lambda';
import { Construct, Node } from 'constructs';
import { TransformOptions, Loader } from './esbuild-types';
import { transformSync, wrapWithEsbuildBinaryPath } from './esbuild-wrapper';

Expand Down Expand Up @@ -38,28 +40,45 @@ export interface TransformerProps {
}

abstract class BaseInlineCode extends InlineCode {
public readonly isInline = true;
private readonly inlineCode: string;

public constructor(
code: string,
props: TransformerProps,
) {
super(code);

this.inlineCode = Lazy.string({
produce: () => {
try {
const {
transformFn = transformSync,
transformOptions = {},
esbuildBinaryPath,
} = props;

const transformedCode = wrapWithEsbuildBinaryPath(transformFn, esbuildBinaryPath)(code, {
color: process.env.NO_COLOR ? Boolean(process.env.NO_COLOR) : undefined,
logLevel: 'warning',
...transformOptions,
});

return transformedCode.code;
} catch (error) {
throw new Error(`Failed to transform ${this.constructor.name}`);
}
},
});
}

public bind(scope: Construct): CodeConfig {
const name = scope.node.path + Node.PATH_SEP + this.constructor.name;
process.stderr.write(`Transforming inline code ${name}...\n`);

const {
transformFn = transformSync,
transformOptions = {},
esbuildBinaryPath,
} = props;

try {
const transformedCode = wrapWithEsbuildBinaryPath(transformFn, esbuildBinaryPath)(code, {
color: process.env.NO_COLOR ? Boolean(process.env.NO_COLOR) : undefined,
logLevel: 'warning',
...transformOptions,
});

super(transformedCode.code);
} catch (error) {
throw new Error('Failed to transform InlineCode');
}
return {
inlineCode: Stack.of(scope).resolve(this.inlineCode),
};
}
}

Expand Down
70 changes: 60 additions & 10 deletions test/inline-code.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Stack } from 'aws-cdk-lib';
import { App, Stack } from 'aws-cdk-lib';
import { Function, Runtime } from 'aws-cdk-lib/aws-lambda';
import { mocked } from 'jest-mock';
import { transformSync } from '../lib/esbuild-wrapper';
import {
Expand Down Expand Up @@ -32,9 +33,10 @@ describe('using transformerProps', () => {
"const banana = 'fruit' ?? 'vegetable'",
);

const { inlineCode } = code.bind(new Stack());
const stack = new Stack();
const { inlineCode } = code.bind(stack);

expect(inlineCode).toBe('const banana = "fruit";\n');
expect(stack.resolve(inlineCode)).toBe('const banana = "fruit";\n');
});
});

Expand All @@ -60,6 +62,54 @@ describe('using transformerProps', () => {

expect(inlineCode).toBe('let x = 1;\n');
});

it('should announce the transforming step', () => {
const processStdErrWriteSpy = jest.spyOn(process.stderr, 'write');
const stack = new Stack(new App(), 'Stack');
const code = new InlineTypeScriptCode('let x: number = 1');

new Function(stack, 'MyFunction', {
runtime: Runtime.NODEJS_14_X,
handler: 'index.handler',
code,
});

expect(processStdErrWriteSpy).toHaveBeenCalledWith(
'Transforming inline code Stack/MyFunction/InlineTypeScriptCode...\n',
);
processStdErrWriteSpy.mockRestore();
});

it('should not do the work twice', () => {
const processStdErrWriteSpy = jest.spyOn(process.stderr, 'write');
const transformFn = jest.fn(transformSync);

const stack = new Stack(new App(), 'Stack');
const code = new InlineTypeScriptCode('let x: number = 1', {
transformFn,
});

new Function(stack, 'One', {
runtime: Runtime.NODEJS_14_X,
handler: 'index.handler',
code,
});

new Function(stack, 'Two', {
runtime: Runtime.NODEJS_14_X,
handler: 'index.handler',
code,
});

expect(transformFn).toHaveBeenCalledTimes(1);
expect(processStdErrWriteSpy).toHaveBeenCalledWith(
'Transforming inline code Stack/One/InlineTypeScriptCode...\n',
);
expect(processStdErrWriteSpy).toHaveBeenCalledWith(
'Transforming inline code Stack/Two/InlineTypeScriptCode...\n',
);
processStdErrWriteSpy.mockRestore();
});
});

describe('given some tsx code', () => {
Expand All @@ -81,23 +131,22 @@ describe('using transformerProps', () => {
expect(() => {
const code = new InlineTypeScriptCode('let : d ===== 1');
code.bind(new Stack());
}).toThrowError('Failed to transform InlineCode');
}).toThrowError('Failed to transform InlineTypeScriptCode');
});

// Currently no way to capture esbuild output,
// See https://github.com/evanw/esbuild/issues/2466
it.skip('should display an error', () => {
const originalConsole = console.error;
console.error = jest.fn();
const processStdErrWriteSpy = jest.spyOn(process.stderr, 'write');

expect(() => {
const code = new InlineTypeScriptCode('let : d ===== 1');
code.bind(new Stack());
}).toThrowError('Failed to transform InlineCode');
}).toThrowError('Failed to transform InlineTypeScriptCode');

expect(console.error).toBeCalledWith(expect.stringContaining('Unexpected "=="'));
expect(processStdErrWriteSpy).toBeCalledWith(expect.stringContaining('Unexpected "=="'));

console.error = originalConsole;
processStdErrWriteSpy.mockRestore();
});
});

Expand Down Expand Up @@ -149,10 +198,11 @@ describe('using transformerProps', () => {
};
};

new InlineTypeScriptCode('let x: number = 1', {
const code = new InlineTypeScriptCode('let x: number = 1', {
transformFn: customTransform,
esbuildBinaryPath: 'dummy-binary',
});
code.bind(new Stack());

expect(mockLogger).toHaveBeenCalledTimes(1);
expect(mockLogger).toHaveBeenCalledWith('dummy-binary');
Expand Down

0 comments on commit 98f7d1d

Please sign in to comment.