diff --git a/.projenrc.ts b/.projenrc.ts
index beeb4509..59fb1f45 100644
--- a/.projenrc.ts
+++ b/.projenrc.ts
@@ -9,6 +9,7 @@ import { InternalConsoleOptions } from 'projen/lib/vscode';
import { SourceFile } from 'ts-morph';
import { tagOnNpm } from './projenrc/release';
import { TypeScriptSourceFile } from './projenrc/TypeScriptSourceFile';
+import { Esbuild } from './src/esbuild-source';
const project = new awscdk.AwsCdkConstructLibrary({
projenrcTs: true,
@@ -89,7 +90,7 @@ const project = new awscdk.AwsCdkConstructLibrary({
devDeps: [
'@aws-cdk/aws-synthetics-alpha@2.0.0-alpha.11',
'@types/eslint',
- 'esbuild@^0.15.0',
+ Esbuild.spec,
'jest-mock',
'ts-morph',
],
@@ -252,7 +253,7 @@ launchConfig?.addOverride('configurations.0.cwd', '${workspaceFolder}');
// esbuild
project.tryFindObjectFile('package.json')?.addOverride('optionalDependencies', {
- esbuild: '^0.15.0',
+ [Esbuild.name]: Esbuild.version,
});
new TypeScriptSourceFile(project, 'src/esbuild-types.ts', {
diff --git a/API.md b/API.md
index 1ece4ab7..7340610d 100644
--- a/API.md
+++ b/API.md
@@ -214,9 +214,9 @@ public readonly esbuildModulePath: string;
- *Type:* `string`
- *Default:* `CDK_ESBUILD_MODULE_PATH` or package resolution (see above)
-Path used to import the esbuild module.
+Absolute path to the esbuild module JS file.
-Python, Go, .NET and Java should use an absolute path, because the jsii execution environment uses a temporary working directory.
+E.g. "/home/user/.npm/node_modules/esbuild/lib/main.js"
If not set, the module path will be determined in the following order:
@@ -1088,9 +1088,9 @@ public readonly esbuildModulePath: string;
- *Type:* `string`
- *Default:* `CDK_ESBUILD_MODULE_PATH` or package resolution (see above)
-Path used to import the esbuild module.
+Absolute path to the esbuild module JS file.
-Python, Go, .NET and Java should use an absolute path, because the jsii execution environment uses a temporary working directory.
+E.g. "/home/user/.npm/node_modules/esbuild/lib/main.js"
If not set, the module path will be determined in the following order:
@@ -1256,9 +1256,9 @@ public readonly esbuildModulePath: string;
- *Type:* `string`
- *Default:* `CDK_ESBUILD_MODULE_PATH` or package resolution (see above)
-Path used to import the esbuild module.
+Absolute path to the esbuild module JS file.
-Python, Go, .NET and Java should use an absolute path, because the jsii execution environment uses a temporary working directory.
+E.g. "/home/user/.npm/node_modules/esbuild/lib/main.js"
If not set, the module path will be determined in the following order:
@@ -1389,9 +1389,9 @@ public readonly esbuildModulePath: string;
- *Type:* `string`
- *Default:* `CDK_ESBUILD_MODULE_PATH` or package resolution (see above)
-Path used to import the esbuild module.
+Absolute path to the esbuild module JS file.
-Python, Go, .NET and Java should use an absolute path, because the jsii execution environment uses a temporary working directory.
+E.g. "/home/user/.npm/node_modules/esbuild/lib/main.js"
If not set, the module path will be determined in the following order:
@@ -1453,9 +1453,9 @@ public readonly esbuildModulePath: string;
- *Type:* `string`
- *Default:* `CDK_ESBUILD_MODULE_PATH` or package resolution (see above)
-Path used to import the esbuild module.
+Absolute path to the esbuild module JS file.
-Python, Go, .NET and Java should use an absolute path, because the jsii execution environment uses a temporary working directory.
+E.g. "/home/user/.npm/node_modules/esbuild/lib/main.js"
If not set, the module path will be determined in the following order:
@@ -2058,9 +2058,9 @@ public readonly esbuildModulePath: string;
- *Type:* `string`
- *Default:* `CDK_ESBUILD_MODULE_PATH` or package resolution (see above)
-Path used to import the esbuild module.
+Absolute path to the esbuild module JS file.
-Python, Go, .NET and Java should use an absolute path, because the jsii execution environment uses a temporary working directory.
+E.g. "/home/user/.npm/node_modules/esbuild/lib/main.js"
If not set, the module path will be determined in the following order:
@@ -2191,9 +2191,9 @@ public readonly esbuildModulePath: string;
- *Type:* `string`
- *Default:* `CDK_ESBUILD_MODULE_PATH` or package resolution (see above)
-Path used to import the esbuild module.
+Absolute path to the esbuild module JS file.
-Python, Go, .NET and Java should use an absolute path, because the jsii execution environment uses a temporary working directory.
+E.g. "/home/user/.npm/node_modules/esbuild/lib/main.js"
If not set, the module path will be determined in the following order:
@@ -2448,6 +2448,105 @@ Determines whether this Code is inline code or not.
---
+### EsbuildSource
+
+#### Initializers
+
+```typescript
+import { EsbuildSource } from '@mrgrain/cdk-esbuild'
+
+new EsbuildSource()
+```
+
+
+
+#### Properties
+
+##### `auto`Required
+
+```typescript
+public readonly auto: string;
+```
+
+- *Type:* `string`
+
+First try to find to module, then install it to a temporary location.
+
+---
+
+##### `install`Required
+
+```typescript
+public readonly install: string;
+```
+
+- *Type:* `string`
+
+Install the module to a temporary location.
+
+---
+
+##### `nodeJs`Required
+
+```typescript
+public readonly nodeJs: string;
+```
+
+- *Type:* `string`
+
+Require module by name, do not attempt to find it anywhere else.
+
+---
+
+##### `platformDefault`Required
+
+```typescript
+public readonly platformDefault: string;
+```
+
+- *Type:* `string`
+
+`EsbuildSource.nodeJs` for NodeJs, `EsbuildSource.auto` for all other languages.
+
+---
+
+##### `anywhere`Optional
+
+```typescript
+public readonly anywhere: string;
+```
+
+- *Type:* `string`
+
+Try to find the module in most common paths.
+
+---
+
+##### `globalPaths`Optional
+
+```typescript
+public readonly globalPaths: string;
+```
+
+- *Type:* `string`
+
+Try to find the module in common global installation paths.
+
+---
+
+##### `default`Optional
+
+```typescript
+public readonly default: string;
+```
+
+- *Type:* `string`
+
+Set the default mechanism to find the module The current default to find the module.
+
+---
+
+
### InlineJavaScriptCode
An implementation of `lambda.InlineCode` using the esbuild Transform API. Inline function code is limited to 4 KiB after transformation.
diff --git a/examples/python-app/python_app/python_app_stack.py b/examples/python-app/python_app/python_app_stack.py
index 6c2ea7d1..c2a136f9 100644
--- a/examples/python-app/python_app/python_app_stack.py
+++ b/examples/python-app/python_app/python_app_stack.py
@@ -8,6 +8,8 @@
InlineTypeScriptCode,
TypeScriptCode,
TypeScriptSource,
+ TransformerProps,
+ EsbuildSource,
)
@@ -15,6 +17,8 @@ class PythonAppStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
+ EsbuildSource.default = EsbuildSource.global_paths
+
s3_deployment.BucketDeployment(
self,
"Website",
@@ -22,6 +26,7 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
TypeScriptSource(
"lambda-handler/index.ts",
copy_dir="lambda-handler",
+ esbuild_module_path=None, # Use default
)
],
destination_bucket=s3.Bucket(
@@ -38,11 +43,13 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
code=TypeScriptCode(
"lambda-handler/index.ts",
build_options=BuildOptions(
- format="esm", outfile="index.mjs", external=["aws-sdk"]
+ format="esm",
+ outfile="index.mjs",
+ external=["aws-sdk"],
+ log_level="verbose",
),
- # Override the global setting with a specific path per Construct
- # This can be useful if a Construct requires a different version of esbuild
- esbuild_module_path="/project/node_modules/esbuild@13",
+ # Override the default setting with a specific path per Construct
+ esbuild_module_path=EsbuildSource.install,
),
)
@@ -57,6 +64,10 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
export function handler() {
console.log(hello);
}
- """
+ """,
+ props=TransformerProps(
+ # Try to find the package anywhere, but don't install it
+ esbuild_module_path=EsbuildSource.anywhere
+ ),
),
)
diff --git a/src/bundler.ts b/src/bundler.ts
index c0da476d..ecbc59e7 100644
--- a/src/bundler.ts
+++ b/src/bundler.ts
@@ -6,8 +6,8 @@ import {
FileSystem,
ILocalBundling,
} from 'aws-cdk-lib';
+import { EsbuildProvider } from './esbuild-provider';
import { BuildOptions } from './esbuild-types';
-import { detectEsbuildModulePath, esbuild, wrapWithEsbuildBinaryPath } from './esbuild-wrapper';
import { errorHasCode } from './utils';
/**
@@ -99,9 +99,9 @@ export interface BundlerProps {
readonly esbuildBinaryPath?: string;
/**
- * Path used to import the esbuild module.
+ * Absolute path to the esbuild module JS file.
*
- * Python, Go, .NET and Java should use an absolute path, because the jsii execution environment uses a temporary working directory.
+ * E.g. "/home/user/.npm/node_modules/esbuild/lib/main.js"
*
* If not set, the module path will be determined in the following order:
*
@@ -195,8 +195,10 @@ export class EsbuildBundler {
}
try {
- const { buildFn = esbuild(detectEsbuildModulePath(props.esbuildModulePath)).buildSync } = this.props;
- wrapWithEsbuildBinaryPath(buildFn, this.props.esbuildBinaryPath)({
+ const buildFn = this.props.buildFn ?? EsbuildProvider.require(props.esbuildModulePath).buildSync;
+ const buildSync = EsbuildProvider.withEsbuildBinaryPath(buildFn, this.props.esbuildBinaryPath);
+
+ buildSync({
entryPoints,
color: process.env.NO_COLOR ? Boolean(process.env.NO_COLOR) : undefined,
...(this.props?.buildOptions || {}),
diff --git a/src/dynamic-package.ts b/src/dynamic-package.ts
new file mode 100644
index 00000000..b6102d53
--- /dev/null
+++ b/src/dynamic-package.ts
@@ -0,0 +1,152 @@
+import { execFileSync } from 'child_process';
+import { mkdtempSync } from 'fs';
+import { tmpdir } from 'os';
+import { join, resolve } from 'path';
+import { Lazy } from 'aws-cdk-lib';
+
+export interface DynamicPackageProps {
+ /**
+ * If the package is installed, install into this directory
+ *
+ * @default - a temporary directory
+ */
+ readonly installPath?: string;
+
+ /**
+ * Additional paths to search for an existing package installation
+ *
+ * @default - a temporary directory
+ */
+ readonly searchPaths?: string[];
+}
+
+export class DynamicPackage {
+ public readonly name: string;
+
+ public readonly version?: string;
+
+ public readonly installPath: string;
+
+ public readonly searchPaths: string[];
+
+ public get spec(): string {
+ if (!this.version) {
+ return this.name;
+ }
+
+ return `${this.name}@${this.version}`;
+ }
+
+ public constructor(
+ /**
+ * Name of the npm package
+ * Version to install, or version constraint
+ *
+ * @default - no version constraint, install the latest version
+ */
+ packageSpec: string,
+ props: DynamicPackageProps = {},
+ ) {
+ const { name, version } = this.parsePackageSpec(packageSpec);
+
+ this.name = name;
+ this.version = version;
+ this.installPath =
+ props.installPath ||
+ mkdtempSync(join(tmpdir(), `cdk-dynamic-${this.spec}-`));
+ this.searchPaths = props.searchPaths || [];
+ }
+
+ protected tryResolve(paths?: string[]): string | undefined {
+ try {
+ return require.resolve(this.name, paths ? { paths } : undefined);
+ } catch (_) {
+ return;
+ }
+ }
+
+ public auto() {
+ return this.tryResolve() || this.findInPaths() || this.install();
+ }
+
+ public nodeJs() {
+ return this.name;
+ }
+
+ public findIn(paths: string[]) {
+ return this.tryResolve([...paths].filter(Boolean) as string[]);
+ }
+
+ public findInPaths() {
+ return (
+ this.findInSearchPaths() ||
+ this.findInLocalPaths() ||
+ this.findInGlobalPaths()
+ );
+ }
+
+ public findInSearchPaths() {
+ return this.findIn(this.searchPaths);
+ }
+
+ public findInLocalPaths() {
+ this.findIn([process.cwd(), process.env.PWD].filter(Boolean) as string[]);
+ }
+
+ public findInGlobalPaths() {
+ return this.findIn([
+ process.execPath,
+ resolve(process.execPath, '../..'),
+ resolve(process.execPath, '../../lib'),
+ resolve(process.execPath, '../../node_modules'),
+ resolve(process.execPath, '../../lib/node_modules'),
+ ]);
+ }
+
+ private static installedPackagePath = new Map();
+ public install() {
+ return Lazy.string({
+ produce: () => {
+ if (!DynamicPackage.installedPackagePath.has(this.spec)) {
+ const args = [
+ 'install',
+ this.spec,
+ '--no-save',
+ '--prefix',
+ this.installPath,
+ ];
+
+ DynamicPackage.log(`Dynamically installing ${this.spec} into "${this.installPath}"...`, 'info');
+ execFileSync('npm', args);
+
+ DynamicPackage.installedPackagePath.set(
+ this.spec,
+ require.resolve(this.name, {
+ paths: [this.installPath],
+ }),
+ );
+ }
+
+ return DynamicPackage.installedPackagePath.get(this.spec);
+ },
+ });
+ }
+
+ protected static log(message: string, _level: string = 'info') {
+ process.stderr.write(`⬥ ${message}\n`);
+ }
+
+ private parsePackageSpec(spec: string) {
+ const hasScope = spec.startsWith('@');
+ if (hasScope) {
+ spec = spec.substring(1);
+ }
+ const [module, ...version] = spec.split('@');
+ const name = hasScope ? `@${module}` : module;
+ if (version.length == 0) {
+ return { name };
+ }
+
+ return { name, version: version?.join('@') };
+ }
+}
diff --git a/src/esbuild-provider.ts b/src/esbuild-provider.ts
new file mode 100644
index 00000000..d2372a4f
--- /dev/null
+++ b/src/esbuild-provider.ts
@@ -0,0 +1,73 @@
+import { DefaultTokenResolver, StringConcat, Token, Tokenization } from 'aws-cdk-lib';
+import { Construct } from 'constructs';
+import { Esbuild, EsbuildSource } from './esbuild-source';
+import { analyzeMetafileSync, buildSync, transformSync, version } from './esbuild-types';
+
+interface Esbuild {
+ buildSync: typeof buildSync;
+ transformSync: typeof transformSync;
+ analyzeMetafileSync: typeof analyzeMetafileSync;
+ version: typeof version;
+}
+
+export class EsbuildProvider {
+ /**
+ * Load the esbuild module according to defined rules.
+ */
+ public static require(path?: string): Esbuild {
+ const module = path || process.env.CDK_ESBUILD_MODULE_PATH || EsbuildSource.default || Esbuild.name;
+
+ return this._require(this.resolve(module));
+ }
+
+ /**
+ * @internal
+ */
+ public static _require(path: string): Esbuild {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ return require(path);
+ }
+
+ /**
+ * Invoke a function with a specific `process.env.ESBUILD_BINARY_PATH`
+ * and restore the env var afterwards.
+ */
+ public static withEsbuildBinaryPath(fn: T, esbuildBinaryPath?: string) {
+ if (!esbuildBinaryPath) {
+ return fn;
+ }
+
+ return (...args: unknown[]) => {
+ const originalEsbuildBinaryPath = process.env.ESBUILD_BINARY_PATH;
+ if (esbuildBinaryPath) {
+ process.env.ESBUILD_BINARY_PATH = esbuildBinaryPath;
+ }
+
+ const result = fn(...args);
+
+ /**
+ * only reset `ESBUILD_BINARY_PATH` if it was explicitly set via the construct props
+ * since `esbuild` itself sometimes sets it (eg. when running in yarn 2 plug&play)
+ */
+ if (esbuildBinaryPath) {
+ process.env.ESBUILD_BINARY_PATH = originalEsbuildBinaryPath;
+ }
+
+ return result;
+ };
+ }
+
+ /**
+ * Resolve a token without context
+ */
+ private static resolve(token: string): string {
+ if (!Token.isUnresolved(token)) {
+ return token;
+ }
+
+ return Tokenization.resolve(token, {
+ scope: new Construct(undefined as any, ''),
+ resolver: new DefaultTokenResolver(new StringConcat()),
+ });
+ }
+}
diff --git a/src/esbuild-source.ts b/src/esbuild-source.ts
new file mode 100644
index 00000000..85ac8436
--- /dev/null
+++ b/src/esbuild-source.ts
@@ -0,0 +1,74 @@
+import { DynamicPackage } from './dynamic-package';
+
+const dynamicEsbuild = new DynamicPackage('esbuild@^0.15.0');
+
+export const Esbuild = {
+ name: dynamicEsbuild.name,
+ version: dynamicEsbuild.version,
+ spec: dynamicEsbuild.spec,
+};
+
+export class EsbuildSource {
+ private static dynamicPackage = dynamicEsbuild;
+ private static _default?: string;
+
+ /**
+ * Set the default mechanism to find the module
+ */
+ public static set default(path: string | undefined) {
+ this._default = path;
+ }
+
+ /**
+ * The current default to find the module
+ */
+ public static get default(): string | undefined {
+ return this._default ?? this.platformDefault;
+ }
+
+ /**
+ * `EsbuildSource.nodeJs` for NodeJs, `EsbuildSource.auto` for all other languages
+ */
+ public static get platformDefault() {
+ if (Boolean(process.env.JSII_AGENT)) {
+ return this.auto;
+ }
+
+ return this.nodeJs;
+ }
+
+ /**
+ * Try to find the module in most common paths.
+ */
+ public static get anywhere() {
+ return this.dynamicPackage.findInPaths();
+ }
+
+ /**
+ * Try to find the module in common global installation paths.
+ */
+ public static get globalPaths() {
+ return this.dynamicPackage.findInGlobalPaths();
+ }
+
+ /**
+ * Require module by name, do not attempt to find it anywhere else.
+ */
+ public static get nodeJs() {
+ return this.dynamicPackage.nodeJs();
+ }
+
+ /**
+ * Install the module to a temporary location.
+ */
+ public static get install() {
+ return this.dynamicPackage.install();
+ }
+
+ /**
+ * First try to find to module, then install it to a temporary location.
+ */
+ public static get auto() {
+ return this.dynamicPackage.auto();
+ }
+}
diff --git a/src/esbuild-wrapper.ts b/src/esbuild-wrapper.ts
deleted file mode 100644
index 08253ce4..00000000
--- a/src/esbuild-wrapper.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { analyzeMetafileSync, buildSync, transformSync, version } from './esbuild-types';
-
-export function esbuild(modulePath: string = 'esbuild'): {
- buildSync: typeof buildSync;
- transformSync: typeof transformSync;
- analyzeMetafileSync: typeof analyzeMetafileSync;
- version: typeof version;
-} {
- // eslint-disable-next-line @typescript-eslint/no-require-imports
- return require(modulePath);
-}
-
-export function wrapWithEsbuildBinaryPath(fn: T, esbuildBinaryPath?: string) {
- if (!esbuildBinaryPath) {
- return fn;
- }
-
- return (...args: unknown[]) => {
- const originalEsbuildBinaryPath = process.env.ESBUILD_BINARY_PATH;
- if (esbuildBinaryPath) {
- process.env.ESBUILD_BINARY_PATH = esbuildBinaryPath;
- }
-
- const result = fn(...args);
-
- /**
- * only reset `ESBUILD_BINARY_PATH` if it was explicitly set via the construct props
- * since `esbuild` itself sometimes sets it (eg. when running in yarn 2 plug&play)
- */
- if (esbuildBinaryPath) {
- process.env.ESBUILD_BINARY_PATH = originalEsbuildBinaryPath;
- }
-
- return result;
- };
-}
-
-export function detectEsbuildModulePath(esbuildBinaryPath?: string) {
- return esbuildBinaryPath || process.env.CDK_ESBUILD_MODULE_PATH || 'esbuild';
-}
diff --git a/src/index.ts b/src/index.ts
index 9e2807e2..35850b80 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,6 +4,10 @@ export {
TransformOptions,
} from './esbuild-types';
+export {
+ EsbuildSource,
+} from './esbuild-source';
+
export {
EsbuildBundler,
BundlerProps,
diff --git a/src/inline-code.ts b/src/inline-code.ts
index 2835597a..7810cae0 100644
--- a/src/inline-code.ts
+++ b/src/inline-code.ts
@@ -1,8 +1,8 @@
import { Lazy, Stack } from 'aws-cdk-lib';
import { CodeConfig, InlineCode } from 'aws-cdk-lib/aws-lambda';
import { Construct, Node } from 'constructs';
+import { EsbuildProvider } from './esbuild-provider';
import { TransformOptions, Loader } from './esbuild-types';
-import { detectEsbuildModulePath, esbuild, wrapWithEsbuildBinaryPath } from './esbuild-wrapper';
import { errorHasCode } from './utils';
/**
@@ -40,9 +40,9 @@ export interface TransformerProps {
readonly esbuildBinaryPath?: string;
/**
- * Path used to import the esbuild module.
+ * Absolute path to the esbuild module JS file.
*
- * Python, Go, .NET and Java should use an absolute path, because the jsii execution environment uses a temporary working directory.
+ * E.g. "/home/user/.npm/node_modules/esbuild/lib/main.js"
*
* If not set, the module path will be determined in the following order:
*
@@ -72,16 +72,13 @@ abstract class BaseInlineCode extends InlineCode {
this.inlineCode = Lazy.string({
produce: () => {
try {
- const {
- transformFn = esbuild(detectEsbuildModulePath(props.esbuildModulePath)).transformSync,
- transformOptions = {},
- esbuildBinaryPath,
- } = props;
+ const transformFn = props.transformFn ?? EsbuildProvider.require(props.esbuildModulePath).transformSync;
+ const transformSync = EsbuildProvider.withEsbuildBinaryPath(transformFn, props.esbuildBinaryPath);
- const transformedCode = wrapWithEsbuildBinaryPath(transformFn, esbuildBinaryPath)(code, {
+ const transformedCode = transformSync(code, {
color: process.env.NO_COLOR ? Boolean(process.env.NO_COLOR) : undefined,
logLevel: 'warning',
- ...transformOptions,
+ ...(props.transformOptions || {}),
});
return transformedCode.code;
diff --git a/test/bundler.test.ts b/test/bundler.test.ts
index c8f1d90b..ee36f64e 100644
--- a/test/bundler.test.ts
+++ b/test/bundler.test.ts
@@ -1,15 +1,13 @@
import { FileSystem } from 'aws-cdk-lib';
+import * as esbuild from 'esbuild';
import { mocked } from 'jest-mock';
import { EsbuildBundler } from '../src/bundler';
+import { EsbuildProvider } from '../src/esbuild-provider';
import { BuildOptions, BuildResult } from '../src/esbuild-types';
-import { esbuild } from '../src/esbuild-wrapper';
-jest.mock('esbuild', () => ({
- buildSync: jest.fn(),
-}));
-
-const buildSync = esbuild().buildSync;
-const realEsbuild = jest.requireActual('esbuild');
+const providerSpy = jest.spyOn(EsbuildProvider, '_require');
+const buildSync = jest.fn();
+providerSpy.mockReturnValue({ buildSync } as any);
describe('bundling', () => {
describe('Given a project root path', () => {
@@ -69,9 +67,7 @@ describe('bundling', () => {
describe('Given an outdir and outfile', () => {
beforeEach(() => {
mocked(buildSync).mockImplementationOnce(
- (options: BuildOptions): BuildResult => {
- return realEsbuild.buildSync(options);
- },
+ (options: BuildOptions): BuildResult => esbuild.buildSync(options),
);
});
afterEach(() => {
diff --git a/test/code.test.ts b/test/code.test.ts
index fb7abcab..db2ee028 100644
--- a/test/code.test.ts
+++ b/test/code.test.ts
@@ -6,13 +6,13 @@ import {
} from '@aws-cdk/aws-synthetics-alpha';
import { Stack } from 'aws-cdk-lib';
import { Function, Runtime as LambdaRuntime } from 'aws-cdk-lib/aws-lambda';
+import * as esbuild from 'esbuild';
import { mocked } from 'jest-mock';
import { JavaScriptCode, TypeScriptCode } from '../src/code';
+import { EsbuildProvider } from '../src/esbuild-provider';
import { BuildOptions } from '../src/esbuild-types';
-import * as provider from '../src/esbuild-wrapper';
-const esbuildSpy = jest.spyOn(provider, 'esbuild');
-const buildSync = provider.esbuild().buildSync;
+const providerSpy = jest.spyOn(EsbuildProvider, '_require');
describe('code', () => {
describe('entrypoint is an absolute path', () => {
@@ -36,7 +36,7 @@ describe('code', () => {
describe('within the esbuild working dir', () => {
it('should be fine and rewrite the entrypoint', () => {
- const customBuild = jest.fn(buildSync);
+ const customBuild = jest.fn(esbuild.buildSync);
expect(() => {
const stack = new Stack();
@@ -123,7 +123,7 @@ describe('code', () => {
describe('Given a custom build function', () => {
it('should call my build function', () => {
- const customBuild = jest.fn(buildSync);
+ const customBuild = jest.fn(esbuild.buildSync);
expect(() => {
const stack = new Stack();
@@ -154,7 +154,7 @@ describe('given a custom esbuildBinaryPath', () => {
const mockLogger = jest.fn();
const customBuild = (options: BuildOptions) => {
mockLogger(process.env.ESBUILD_BINARY_PATH);
- return buildSync(options);
+ return esbuild.buildSync(options);
};
expect(() => {
@@ -181,11 +181,12 @@ describe('given a custom esbuildBinaryPath', () => {
describe('with an esbuild module path from', () => {
let stack: Stack;
beforeEach(() => {
- esbuildSpy.mockClear();
+ providerSpy.mockClear();
+ providerSpy.mockReturnValue(esbuild);
stack = new Stack();
});
afterAll(() => {
- esbuildSpy.mockRestore();
+ providerSpy.mockRestore();
});
describe('the default', () => {
@@ -200,8 +201,8 @@ describe('with an esbuild module path from', () => {
code,
});
- expect(esbuildSpy).toHaveBeenCalledTimes(1);
- expect(esbuildSpy).toHaveBeenCalledWith('esbuild');
+ expect(providerSpy).toHaveBeenCalledTimes(1);
+ expect(providerSpy).toHaveBeenCalledWith('esbuild');
});
});
@@ -209,7 +210,7 @@ describe('with an esbuild module path from', () => {
it('should use the path from the prop', () => {
const code = new TypeScriptCode('fixtures/handlers/ts-handler.ts', {
buildOptions: { absWorkingDir: resolve(__dirname) },
- esbuildModulePath: '../node_modules/esbuild',
+ esbuildModulePath: '/path/provided/by/prop',
});
new Function(stack, 'MyFunction', {
@@ -218,14 +219,14 @@ describe('with an esbuild module path from', () => {
code,
});
- expect(esbuildSpy).toHaveBeenCalledTimes(1);
- expect(esbuildSpy).toHaveBeenCalledWith('../node_modules/esbuild');
+ expect(providerSpy).toHaveBeenCalledTimes(1);
+ expect(providerSpy).toHaveBeenCalledWith('/path/provided/by/prop');
});
});
describe('`CDK_ESBUILD_MODULE_PATH` env var', () => {
beforeEach(() => {
- process.env.CDK_ESBUILD_MODULE_PATH = '../node_modules/esbuild';
+ process.env.CDK_ESBUILD_MODULE_PATH = '/path/provided/by/env/var';
});
afterEach(() => {
delete process.env.CDK_ESBUILD_MODULE_PATH;
@@ -242,15 +243,15 @@ describe('with an esbuild module path from', () => {
code,
});
- expect(esbuildSpy).toHaveBeenCalledTimes(1);
- expect(esbuildSpy).toHaveBeenCalledWith('../node_modules/esbuild');
+ expect(providerSpy).toHaveBeenCalledTimes(1);
+ expect(providerSpy).toHaveBeenCalledWith('/path/provided/by/env/var');
});
describe('and `esbuildModulePath` prop', () => {
it('should prefer the path from prop', () => {
const code = new TypeScriptCode('fixtures/handlers/ts-handler.ts', {
buildOptions: { absWorkingDir: resolve(__dirname) },
- esbuildModulePath: '../test/../node_modules/esbuild',
+ esbuildModulePath: '/path/provided/by/prop',
});
new Function(stack, 'MyFunction', {
@@ -259,8 +260,8 @@ describe('with an esbuild module path from', () => {
code,
});
- expect(esbuildSpy).toHaveBeenCalledTimes(1);
- expect(esbuildSpy).toHaveBeenCalledWith('../test/../node_modules/esbuild');
+ expect(providerSpy).toHaveBeenCalledTimes(1);
+ expect(providerSpy).toHaveBeenCalledWith('/path/provided/by/prop');
});
});
});
diff --git a/test/inline-code.test.ts b/test/inline-code.test.ts
index f68a43bc..d35bde5e 100644
--- a/test/inline-code.test.ts
+++ b/test/inline-code.test.ts
@@ -1,5 +1,6 @@
import { App, Stack } from 'aws-cdk-lib';
import { Function, Runtime } from 'aws-cdk-lib/aws-lambda';
+import * as esbuild from 'esbuild';
import { mocked } from 'jest-mock';
import {
InlineJavaScriptCode,
@@ -7,10 +8,9 @@ import {
InlineTsxCode,
InlineTypeScriptCode,
} from '../src';
-import * as provider from '../src/esbuild-wrapper';
+import { EsbuildProvider } from '../src/esbuild-provider';
-const esbuildSpy = jest.spyOn(provider, 'esbuild');
-const transformSync = provider.esbuild().transformSync;
+const providerSpy = jest.spyOn(EsbuildProvider, '_require');
describe('using transformOptions', () => {
describe('given a banner code', () => {
@@ -85,7 +85,7 @@ describe('using transformerProps', () => {
it('should not do the work twice', () => {
const processStdErrWriteSpy = jest.spyOn(process.stderr, 'write');
- const transformFn = jest.fn(transformSync);
+ const transformFn = jest.fn(esbuild.transformSync);
const stack = new Stack(new App(), 'Stack');
const code = new InlineTypeScriptCode('let x: number = 1', {
@@ -214,10 +214,11 @@ describe('using transformerProps', () => {
describe('with an esbuild module path from', () => {
beforeEach(() => {
- esbuildSpy.mockClear();
+ providerSpy.mockClear();
+ providerSpy.mockReturnValue(esbuild);
});
afterAll(() => {
- esbuildSpy.mockRestore();
+ providerSpy.mockRestore();
});
describe('the default', () => {
@@ -225,26 +226,26 @@ describe('using transformerProps', () => {
const code = new InlineTypeScriptCode('let x: number = 1');
code.bind(new Stack());
- expect(esbuildSpy).toHaveBeenCalledTimes(1);
- expect(esbuildSpy).toHaveBeenCalledWith('esbuild');
+ expect(providerSpy).toHaveBeenCalledTimes(1);
+ expect(providerSpy).toHaveBeenCalledWith('esbuild');
});
});
describe('`esbuildModulePath` prop', () => {
it('should use the path from the prop', () => {
const code = new InlineTypeScriptCode('let x: number = 1', {
- esbuildModulePath: '../node_modules/esbuild',
+ esbuildModulePath: '/expected/path/from/prop',
});
code.bind(new Stack());
- expect(esbuildSpy).toHaveBeenCalledTimes(1);
- expect(esbuildSpy).toHaveBeenCalledWith('../node_modules/esbuild');
+ expect(providerSpy).toHaveBeenCalledTimes(1);
+ expect(providerSpy).toHaveBeenCalledWith('/expected/path/from/prop');
});
});
describe('`CDK_ESBUILD_MODULE_PATH` env var', () => {
beforeEach(() => {
- process.env.CDK_ESBUILD_MODULE_PATH = '../node_modules/esbuild';
+ process.env.CDK_ESBUILD_MODULE_PATH = '/expected/path/from/env/var';
});
afterEach(() => {
delete process.env.CDK_ESBUILD_MODULE_PATH;
@@ -254,19 +255,19 @@ describe('using transformerProps', () => {
const code = new InlineTypeScriptCode('let x: number = 1');
code.bind(new Stack());
- expect(esbuildSpy).toHaveBeenCalledTimes(1);
- expect(esbuildSpy).toHaveBeenCalledWith('../node_modules/esbuild');
+ expect(providerSpy).toHaveBeenCalledTimes(1);
+ expect(providerSpy).toHaveBeenCalledWith('/expected/path/from/env/var');
});
describe('and `esbuildModulePath` prop', () => {
it('should prefer the path from prop', () => {
const code = new InlineTypeScriptCode('let x: number = 1', {
- esbuildModulePath: '../test/../node_modules/esbuild',
+ esbuildModulePath: '/expected/path/from/prop',
});
code.bind(new Stack());
- expect(esbuildSpy).toHaveBeenCalledTimes(1);
- expect(esbuildSpy).toHaveBeenCalledWith('../test/../node_modules/esbuild');
+ expect(providerSpy).toHaveBeenCalledTimes(1);
+ expect(providerSpy).toHaveBeenCalledWith('/expected/path/from/prop');
});
});
});
@@ -275,7 +276,7 @@ describe('using transformerProps', () => {
describe('with logLevel', () => {
describe('not provided', () => {
it('should default to "warning"', () => {
- const transformFn = jest.fn(transformSync);
+ const transformFn = jest.fn(esbuild.transformSync);
const code = new InlineJavaScriptCode("const fruit = 'banana';", {
transformFn,
});
@@ -289,7 +290,7 @@ describe('using transformerProps', () => {
describe('provided', () => {
it('should use the provided logLevel', () => {
- const transformFn = jest.fn(transformSync);
+ const transformFn = jest.fn(esbuild.transformSync);
const code = new InlineJavaScriptCode("const fruit = 'banana';", {
transformFn,
transformOptions: {
@@ -323,7 +324,7 @@ describe('using transformerProps', () => {
});
it(`should set the color option to "${derivedColor}"`, () => {
- const transformFn = jest.fn(transformSync);
+ const transformFn = jest.fn(esbuild.transformSync);
const code = new InlineTypeScriptCode('let x: number = 1', {
transformFn,
@@ -339,7 +340,7 @@ describe('using transformerProps', () => {
});
it('should respect an explicit option', () => {
- const transformFn = jest.fn(transformSync);
+ const transformFn = jest.fn(esbuild.transformSync);
const code = new InlineTypeScriptCode('let x: number = 1', {
transformFn,
diff --git a/test/source.test.ts b/test/source.test.ts
index 751e29f8..4cfa26c5 100644
--- a/test/source.test.ts
+++ b/test/source.test.ts
@@ -3,10 +3,10 @@ import { RemovalPolicy, Stack } from 'aws-cdk-lib';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { BucketDeployment } from 'aws-cdk-lib/aws-s3-deployment';
import { mocked } from 'jest-mock';
-import { esbuild } from '../src/esbuild-wrapper';
+import { EsbuildProvider } from '../src/esbuild-provider';
import { JavaScriptSource, TypeScriptSource } from '../src/source';
-const buildSync = esbuild().buildSync;
+const buildSync = EsbuildProvider.require().buildSync;
describe('source', () => {
describe('entrypoint is an absolute path', () => {