CDK constructs for esbuild, an extremely fast JavaScript bundler
Project description
CDK constructs for esbuild, an extremely fast JavaScript bundler
Getting started | Documentation | API Reference | FAQ
Why?
esbuild is an extremely fast bundler and minifier for Typescript and JavaScript. This package makes esbuild available to deploy AWS Lambda Functions, static websites and publish assets for use.
AWS CDK supports esbuild for AWS Lambda Functions, but the implementation cannot be used with other Constructs and doesn't expose all of esbuild's API.
Production readiness
This package is stable and ready to be used in production, as many do. However esbuild has not yet released a version 1.0.0 and its API is still in active development. Please read the guide on esbuild's production readiness.
Esbuild minor version upgrades are introduced in minor versions of this package and inherit breaking changes from esbuild.
Getting started
Install cdk-esbuild
for Node.js with your favorite package manager:
# npm
npm install @mrgrain/cdk-esbuild@4
# Yarn
yarn add @mrgrain/cdk-esbuild@4
# pnpm
pnpm add @mrgrain/cdk-esbuild@4
For Python and Dotnet, use these commands:
# Python
pip install streamlink-serverless
# Dotnet
dotnet add package StreamlinkServerless
AWS Lambda: Serverless function
💡 See Lambda Function for a complete working example of a how to deploy a lambda function.
Use TypeScriptCode
as the code
of a lambda function:
# Example automatically generated from non-compiling source. May contain errors.
bundled_code = TypeScriptCode("src/handler.ts")
fn = lambda_.Function(stack, "MyFunction",
runtime=lambda_.Runtime.NODEJS_18_X,
handler="index.handler",
code=bundled_code
)
AWS S3: Static Website
💡 See Static Website with React for a complete working example of a how to deploy a React app to S3.
Use TypeScriptSource
as one of the sources
of a static website deployment:
website_bundle = TypeScriptSource("src/index.tsx")
website_bucket = s3.Bucket(stack, "WebsiteBucket",
auto_delete_objects=True,
public_read_access=True,
removal_policy=cdk.RemovalPolicy.DESTROY,
website_index_document="index.html"
)
s3deploy.BucketDeployment(stack, "DeployWebsite",
destination_bucket=website_bucket,
sources=[website_bundle]
)
Amazon CloudWatch Synthetics: Canary monitoring
💡 See Monitored Website for a complete working example of a deployed and monitored website.
Synthetics runs a canary to produce traffic to an application for monitoring purposes. Use TypeScriptCode
as the code
of a Canary test:
ℹ️ This feature depends on the
@aws-cdk/aws-synthetics-alpha
package which is a developer preview. You may need to update your source code when upgrading to a newer version of this package.npm i @aws-cdk/aws-synthetics-alpha
bundled_code = TypeScriptCode("src/canary.ts",
build_options=BuildOptions(
outdir="nodejs/node_modules"
)
)
canary = synthetics.Canary(stack, "MyCanary",
runtime=synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_2,
test=synthetics.Test.custom(
code=bundled_code,
handler="index.handler"
)
)
Documentation
The package exports various different constructs for use with existing CDK features. A major guiding design principal for this package is to extend, don't replace. Expect constructs that you can provide as props, not complete replacements.
For use in Lambda Functions and Synthetic Canaries, the following classes implement lambda.Code
(reference) and synthetics.Code
(reference):
TypeScriptCode
&JavaScriptCode
Inline code is only supported by Lambda:
InlineTypeScriptCode
&InlineJavaScriptCode
For use with S3 bucket deployments, classes implementing s3deploy.ISource
(reference):
TypeScriptSource
&JavaScriptSource
Code and Source constructs seamlessly plug-in to other high-level CDK constructs. They share the same set of parameters, props and build options.
The following classes power the other features. You normally won't have to use them, but they are there if you need them:
TypeScriptAsset
&JavaScriptAsset
implementss3.Asset
(reference)
creates an asset uploaded to S3 which can be referenced by other constructsEsbuildBundler
implementscore.BundlingOptions
(reference)
provides an interface for a esbuild bundler wherever neededEsbuildProvider
implementsIBuildProvider
andITransformProvider
provides the default esbuild API implementation and can be replaced with a custom implementation
API Reference
Auto-generated reference for Classes and Structs. This information is also available as part of your IDE's code completion.
Python and Dotnet
Because esbuild requires a platform and architecture specific binary, it currently has to be installed using npm (or any other Node.js package manager).
When cdk-esbuild
is used with Python or Dotnet, it will automatically detect a local or global installation of the esbuild npm package - or fallback to dynamically installing a copy into a temporary location.
The exact algorithm of this mechanism must be treated as an implementation detail and should not be relied on.
It can however be configured to a certain extent.
See the examples below for more details.
While this "best effort" approach makes it easy to get started, it is not always desirable. For example in environments with limited network access or when guaranteed repeatability of builds is a concern. You have several options to opt-out of this behavior:
-
Recommended - Install esbuild as a local package
The recommended approach is to manage an additional Node.js project in the root of your AWS CDK project. esbuild should then be added to thepackage.json
file and it is your responsibility to ensure the required setup steps are run in every environment (development machines & CI/CD systems). The esbuild package will then be detected automatically. -
Install esbuild as a global package
Instead of installing the package in a local project, it can also be installed globally withnpm install -g esbuild
or a similar command. The esbuild package will be detected automatically from the global installation. This approach might be preferred if a build container is prepared ahead of time, thus avoiding repeated package installation. -
Set
CDK_ESBUILD_MODULE_PATH
env variable
If an installed esbuild module cannot be reliably detected by the algorithm, you can provide the absolute path to the module as an environment variable. This approach has the advantage that the location of the module can be different on different systems. While it can be combined with either installation approach, it is usually used with a global installation of the esbuild package. -
Set
esbuildModulePath
prop
Similar to setting the module path via env variable, it can also be passed as theesbuildModulePath
prop to aEsbuildProvider
:EsbuildProvider( esbuild_module_path="/home/user/node_modules/esbuild/lib/main.js" )
-
Override the default implementation provider
Using the previous approach, but setting it for every usage:custom_module = EsbuildProvider( esbuild_module_path="/home/user/node_modules/esbuild/lib/main.js" ) EsbuildProvider.override_default_provider(custom_module)
The esbuildModulePath
can be provided as a known, absolute or relative path.
When using the programmatic interface, this package additionally offers some helper methods to influence to fine-tune to automatic detection algorithm:
# Example automatically generated from non-compiling source. May contain errors.
# This will force installation to a temporary location
EsbuildProvider(
esbuild_module_path=EsbuildSource.install()
)
Please see the EsbuildSource
reference for a list of available methods.
Customizing the Esbuild API
This package uses the esbuild JavaScript API. For most use cases the default configuration will be suitable.
In some cases it might be useful to configure esbuild differently or even provide a completely custom implementation. Common examples for this are:
- To use a pre-installed version of esbuild with Python and Dotnet
- If features not supported by the synchronous API are required, e.g. support for plugins
- If the default version constraints for esbuild are not suitable
- To use a version of esbuild that is installed by any other means than
npm
, including Docker
For these scenarios, this package offers customization options and an interface to provide a custom implementation:
# my_custom_build_provider: IBuildProvider
# my_custom_transform_provider: ITransformProvider
TypeScriptCode("src/handler.ts",
build_provider=my_custom_build_provider
)
InlineTypeScriptCode("let x: number = 1",
transform_provider=my_custom_transform_provider
)
Esbuild binary path
It is possible to override the binary used by esbuild by setting a property on EsbuildProvider
.
This is the same as setting the ESBUILD_BINARY_PATH
environment variable.
Defining the esbuildBinaryPath
prop takes precedence.
build_provider = EsbuildProvider(
esbuild_binary_path="path/to/esbuild/binary"
)
# This will use a different esbuild binary
TypeScriptCode("src/handler.ts", build_provider=build_provider)
Esbuild module path
The Node.js module discovery algorithm will normally be used to find the esbuild package. It can be useful to use specify a different module path, for example if a globally installed package should be used instead of a local version.
build_provider = EsbuildProvider(
esbuild_module_path="/home/user/node_modules/esbuild/lib/main.js"
)
# This will use a different esbuild module
TypeScriptCode("src/handler.ts", build_provider=build_provider)
Alternatively supported by setting the CDK_ESBUILD_MODULE_PATH
environment variable, which will apply to all uses.
Defining the esbuildModulePath
prop takes precedence.
Custom Build and Transform API implementations
💡 See Using esbuild with plugins for a complete working example of a custom Build API implementation.
A custom implementation can be provided by implementing IBuildProvider
or ITransformProvider
:
@jsii.implements(IBuildProvider)
@jsii.implements(ITransformProvider)
class CustomEsbuild:
def build_sync(self, *, bundle=None, splitting=None, preserveSymlinks=None, outfile=None, metafile=None, outdir=None, outbase=None, external=None, packages=None, alias=None, loader=None, resolveExtensions=None, mainFields=None, conditions=None, write=None, allowOverwrite=None, tsconfig=None, outExtension=None, publicPath=None, entryNames=None, chunkNames=None, assetNames=None, inject=None, banner=None, footer=None, incremental=None, absWorkingDir=None, nodePaths=None, sourcemap=None, legalComments=None, sourceRoot=None, sourcesContent=None, format=None, globalName=None, target=None, supported=None, platform=None, mangleProps=None, reserveProps=None, mangleQuoted=None, mangleCache=None, drop=None, minify=None, minifyWhitespace=None, minifyIdentifiers=None, minifySyntax=None, charset=None, treeShaking=None, ignoreAnnotations=None, jsx=None, jsxFactory=None, jsxFragment=None, jsxImportSource=None, jsxDev=None, jsxSideEffects=None, define=None, pure=None, keepNames=None, color=None, logLevel=None, logLimit=None, logOverride=None):
pass
def transform_sync(self, code, *, tsconfigRaw=None, sourcefile=None, loader=None, banner=None, footer=None, sourcemap=None, legalComments=None, sourceRoot=None, sourcesContent=None, format=None, globalName=None, target=None, supported=None, platform=None, mangleProps=None, reserveProps=None, mangleQuoted=None, mangleCache=None, drop=None, minify=None, minifyWhitespace=None, minifyIdentifiers=None, minifySyntax=None, charset=None, treeShaking=None, ignoreAnnotations=None, jsx=None, jsxFactory=None, jsxFragment=None, jsxImportSource=None, jsxDev=None, jsxSideEffects=None, define=None, pure=None, keepNames=None, color=None, logLevel=None, logLimit=None, logOverride=None):
# custom implementation goes here, return transformed code
return "transformed code"
# These will use the custom implementation
TypeScriptCode("src/handler.ts",
build_provider=CustomEsbuild()
)
InlineTypeScriptCode("let x: number = 1",
transform_provider=CustomEsbuild()
)
Instead of esbuild, the custom methods will be invoked with all computed options.
The custom implementation can amend, change or discard any of these.
However with IBuildProvider
the integration with CDK relies heavily on the outdir
/outfile
values and it's usually required to use them unchanged.
ITransformProvider
must return the transformed code as a string.
Failures and warnings should be printed to stderr and thrown as the respective esbuild error.
Overriding the default implementation providers
It is also possible to change the default Esbuild API implementation for all usages of this package. You can change the default for both APIs or set a different implementation for each of them.
my_custom_esbuild_provider = MyCustomEsbuildProvider()
EsbuildProvider.override_default_provider(my_custom_esbuild_provider)
EsbuildProvider.override_default_build_provider(my_custom_esbuild_provider)
EsbuildProvider.override_default_transformation_provider(my_custom_esbuild_provider)
# This will use the custom provider without the need to define it as a prop
TypeScriptCode("src/handler.ts")
Roadmap & Contributions
The project's roadmap is available on GitHub. Please submit feature requests as issues to the repository.
All contributions are welcome, no matter if they are for already planned or completely new features.
FAQ
How do I upgrade from @mrgrain/cdk-esbuild
v3?
Please refer to the v4 release notes for a list of backwards incompatible changes and respective upgrade instructions.
[TS/JS] Why am I getting the error Cannot find module 'esbuild'
?
This package depends on esbuild as an optional dependencies. If optional dependencies are not installed automatically on your system (e.g. when using npm v4-6), install esbuild explicitly:
npm install esbuild
[TS/JS] How can I use a different version of esbuild?
Use the override instructions for your package manager to force a specific version, for example:
{
"overrides": {
"esbuild": "0.14.7"
}
}
Build and Transform interfaces are relatively stable across esbuild versions.
However if any incompatibilities occur, buildOptions
/ transformOptions
can be cast to any
:
bundled_code = TypeScriptCode("src/handler.ts",
build_options={
"unsupported_option": "value"
}
)
[Python/Dotnet] How can I use a different version of esbuild?
Install the desired version of esbuild locally or globally as described in the documentation above.
Build and Transform interfaces are relatively stable across esbuild versions.
However if any incompatibilities occur, use the appropriate language features to cast any incompatible buildOptions
/ transformOptions
to the correct types.
Can I use this package in my published AWS CDK Construct?
It is possible to use cdk-esbuild
in a published AWS CDK Construct library, but not recommended. Always prefer to ship a compiled .js
file or even bundle a zip archive in your package. For AWS Lambda Functions, projen provides an excellent solution.
If you do end up consuming cdk-esbuild
, you will have to set buildOptions.absWorkingDir
. The easiest way to do this, is to resolve the path based on the directory name of the calling file:
// file: node_modules/construct-library/src/index.ts
const props = {
buildOptions: {
absWorkingDir: path.resolve(__dirname, ".."),
// now: /user/local-app/node_modules/construct-library
},
};
This will dynamically resolve to the correct path, wherever the package is installed. Please open an issue if you encounter any difficulties.
Can I use this package with AWS CDK v1?
Yes, the 2.x.x
line of cdk-esbuild
is compatible with AWS CDK v1. You can find the documentation for it on the v2 branch.
However, in line with AWS CDK v2, this version release now only receives security updates and critical bug fixes and support will fully end on June 1, 2023.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for mrgrain.cdk-esbuild-4.0.0b0.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 29bbbf8a7d3af0b2f9af36373c95efb025eced9ff81ca330f0e00bd58dc134fe |
|
MD5 | af53eb65c2e1caad4e752e73c3633842 |
|
BLAKE2b-256 | 566ed7d100a1b1a20da55f711aa702f39c3a7b29ac914efaaecf8b0494ee18b4 |
Hashes for mrgrain.cdk_esbuild-4.0.0b0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 78275322cbe37d7198091b10af7023f37e59db4be9ff1bd5d4a83800018ed323 |
|
MD5 | 3652cf349b92df0f9976209af873119a |
|
BLAKE2b-256 | 694312fa5d6d22701aa516636a083506cd3a19c3ea94b89fd17837af7ba00215 |