Skip to content

Commit 79af5bf

Browse files
authored
feat: Turn esbuild into its own workflow (#320)
* Create new esbuild workflow * Fix esbuild workflow capability
1 parent 8bd869d commit 79af5bf

File tree

41 files changed

+625
-503
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+625
-503
lines changed

aws_lambda_builders/workflows/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
import aws_lambda_builders.workflows.java_maven
1212
import aws_lambda_builders.workflows.dotnet_clipackage
1313
import aws_lambda_builders.workflows.custom_make
14+
import aws_lambda_builders.workflows.nodejs_npm_esbuild

aws_lambda_builders/workflows/nodejs_npm/DESIGN.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ directly.
323323
}
324324
```
325325

326-
For a full example, see the [`with-deps-esbuild`](../../../tests/integration/workflows/nodejs_npm/testdata/with-deps-esbuild/) test project.
326+
For a full example, see the [`with-deps-esbuild`](../../../tests/integration/workflows/nodejs_npm_esbuild/testdata/with-deps-esbuild/) test project.
327327

328328
#### Building typescript
329329

@@ -350,7 +350,7 @@ as in the example below. There is no transpiling process needed upfront.
350350
}
351351
```
352352

353-
For a full example, see the [`with-deps-esbuild-typescript`](../../../tests/integration/workflows/nodejs_npm/testdata/with-deps-esbuild-typescript/) test project.
353+
For a full example, see the [`with-deps-esbuild-typescript`](../../../tests/integration/workflows/nodejs_npm_esbuild/testdata/with-deps-esbuild-typescript/) test project.
354354

355355
**important note:** esbuild does not perform type checking, so users wanting to ensure type-checks need to run the `tsc` process as part of their
356356
testing flow before invoking `sam build`. For additional typescript caveats with esbuild, check out <https://esbuild.github.io/content-types/#typescript>.

aws_lambda_builders/workflows/nodejs_npm/actions.py

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
from aws_lambda_builders.actions import BaseAction, Purpose, ActionFailedError
88
from .npm import NpmExecutionError
9-
from .esbuild import EsbuildExecutionError
109

1110
LOG = logging.getLogger(__name__)
1211

@@ -287,79 +286,3 @@ def execute(self):
287286

288287
except OSError as ex:
289288
raise ActionFailedError(str(ex))
290-
291-
292-
class EsbuildBundleAction(BaseAction):
293-
294-
"""
295-
A Lambda Builder Action that packages a Node.js package using esbuild into a single file
296-
optionally transpiling TypeScript
297-
"""
298-
299-
NAME = "EsbuildBundle"
300-
DESCRIPTION = "Packaging source using Esbuild"
301-
PURPOSE = Purpose.COPY_SOURCE
302-
303-
def __init__(self, source_dir, artifacts_dir, bundler_config, osutils, subprocess_esbuild):
304-
"""
305-
:type source_dir: str
306-
:param source_dir: an existing (readable) directory containing source files
307-
308-
309-
:type artifacts_dir: str
310-
:param artifacts_dir: an existing (writable) directory where to store the output.
311-
Note that the actual result will be in the 'package' subdirectory here.
312-
313-
:type osutils: aws_lambda_builders.workflows.nodejs_npm.utils.OSUtils
314-
:param osutils: An instance of OS Utilities for file manipulation
315-
316-
:type subprocess_esbuild: aws_lambda_builders.workflows.nodejs_npm.npm.SubprocessEsbuild
317-
:param subprocess_esbuild: An instance of the Esbuild process wrapper
318-
"""
319-
super(EsbuildBundleAction, self).__init__()
320-
self.source_dir = source_dir
321-
self.artifacts_dir = artifacts_dir
322-
self.bundler_config = bundler_config
323-
self.osutils = osutils
324-
self.subprocess_esbuild = subprocess_esbuild
325-
326-
def execute(self):
327-
"""
328-
Runs the action.
329-
330-
:raises lambda_builders.actions.ActionFailedError: when esbuild packaging fails
331-
"""
332-
333-
if "entry_points" not in self.bundler_config:
334-
raise ActionFailedError("entry_points not set ({})".format(self.bundler_config))
335-
336-
entry_points = self.bundler_config["entry_points"]
337-
338-
if not isinstance(entry_points, list):
339-
raise ActionFailedError("entry_points must be a list ({})".format(self.bundler_config))
340-
341-
if not entry_points:
342-
raise ActionFailedError("entry_points must not be empty ({})".format(self.bundler_config))
343-
344-
entry_paths = [self.osutils.joinpath(self.source_dir, entry_point) for entry_point in entry_points]
345-
346-
LOG.debug("NODEJS building %s using esbuild to %s", entry_paths, self.artifacts_dir)
347-
348-
for entry_point in entry_paths:
349-
if not self.osutils.file_exists(entry_point):
350-
raise ActionFailedError("entry point {} does not exist".format(entry_point))
351-
352-
args = entry_points + ["--bundle", "--platform=node", "--format=cjs"]
353-
minify = self.bundler_config.get("minify", True)
354-
sourcemap = self.bundler_config.get("sourcemap", True)
355-
target = self.bundler_config.get("target", "es2020")
356-
if minify:
357-
args.append("--minify")
358-
if sourcemap:
359-
args.append("--sourcemap")
360-
args.append("--target={}".format(target))
361-
args.append("--outdir={}".format(self.artifacts_dir))
362-
try:
363-
self.subprocess_esbuild.run(args, cwd=self.source_dir)
364-
except EsbuildExecutionError as ex:
365-
raise ActionFailedError(str(ex))

aws_lambda_builders/workflows/nodejs_npm/utils.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
import shutil
1010
import json
1111

12-
EXPERIMENTAL_FLAG_ESBUILD = "experimentalEsbuild"
13-
1412

1513
class OSUtils(object):
1614

@@ -55,10 +53,3 @@ def is_windows(self):
5553
def parse_json(self, path):
5654
with open(path) as json_file:
5755
return json.load(json_file)
58-
59-
60-
def is_experimental_esbuild_scope(experimental_flags):
61-
"""
62-
A function which will determine if experimental esbuild scope is active
63-
"""
64-
return bool(experimental_flags) and EXPERIMENTAL_FLAG_ESBUILD in experimental_flags

aws_lambda_builders/workflows/nodejs_npm/workflow.py

Lines changed: 5 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
"""
44

55
import logging
6-
import json
7-
from typing import List
86

97
from aws_lambda_builders.path_resolver import PathResolver
108
from aws_lambda_builders.workflow import BaseWorkflow, Capability
@@ -13,22 +11,17 @@
1311
CleanUpAction,
1412
CopyDependenciesAction,
1513
MoveDependenciesAction,
16-
BaseAction,
1714
)
18-
from aws_lambda_builders.utils import which
19-
from aws_lambda_builders.exceptions import WorkflowFailedError
15+
2016
from .actions import (
2117
NodejsNpmPackAction,
2218
NodejsNpmLockFileCleanUpAction,
2319
NodejsNpmInstallAction,
2420
NodejsNpmrcCopyAction,
2521
NodejsNpmrcCleanUpAction,
26-
NodejsNpmCIAction,
27-
EsbuildBundleAction,
2822
)
29-
from .utils import OSUtils, is_experimental_esbuild_scope
23+
from .utils import OSUtils
3024
from .npm import SubprocessNpm
31-
from .esbuild import SubprocessEsbuild
3225

3326
LOG = logging.getLogger(__name__)
3427

@@ -64,16 +57,9 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim
6457

6558
subprocess_npm = SubprocessNpm(osutils)
6659

67-
manifest_config = self.get_manifest_config(osutils, manifest_path)
68-
69-
if manifest_config["bundler"] == "esbuild" and is_experimental_esbuild_scope(self.experimental_flags):
70-
self.actions = self.actions_with_bundler(
71-
source_dir, artifacts_dir, manifest_config, osutils, subprocess_npm
72-
)
73-
else:
74-
self.actions = self.actions_without_bundler(
75-
source_dir, artifacts_dir, scratch_dir, manifest_path, osutils, subprocess_npm
76-
)
60+
self.actions = self.actions_without_bundler(
61+
source_dir, artifacts_dir, scratch_dir, manifest_path, osutils, subprocess_npm
62+
)
7763

7864
def actions_without_bundler(self, source_dir, artifacts_dir, scratch_dir, manifest_path, osutils, subprocess_npm):
7965
"""
@@ -147,67 +133,6 @@ def actions_without_bundler(self, source_dir, artifacts_dir, scratch_dir, manife
147133

148134
return actions
149135

150-
def actions_with_bundler(self, source_dir, artifacts_dir, bundler_config, osutils, subprocess_npm):
151-
"""
152-
Generate a list of Nodejs build actions with a bundler
153-
154-
:type source_dir: str
155-
:param source_dir: an existing (readable) directory containing source files
156-
157-
:type artifacts_dir: str
158-
:param artifacts_dir: an existing (writable) directory where to store the output.
159-
160-
:type bundler_config: dict
161-
:param bundler_config: configurations for the bundler action
162-
163-
:type osutils: aws_lambda_builders.workflows.nodejs_npm.utils.OSUtils
164-
:param osutils: An instance of OS Utilities for file manipulation
165-
166-
:type subprocess_npm: aws_lambda_builders.workflows.nodejs_npm.npm.SubprocessNpm
167-
:param subprocess_npm: An instance of the NPM process wrapper
168-
169-
:rtype: list
170-
:return: List of build actions to execute
171-
"""
172-
lockfile_path = osutils.joinpath(source_dir, "package-lock.json")
173-
shrinkwrap_path = osutils.joinpath(source_dir, "npm-shrinkwrap.json")
174-
npm_bin_path = subprocess_npm.run(["bin"], cwd=source_dir)
175-
executable_search_paths = [npm_bin_path]
176-
if self.executable_search_paths is not None:
177-
executable_search_paths = executable_search_paths + self.executable_search_paths
178-
subprocess_esbuild = SubprocessEsbuild(osutils, executable_search_paths, which=which)
179-
180-
if osutils.file_exists(lockfile_path) or osutils.file_exists(shrinkwrap_path):
181-
install_action = NodejsNpmCIAction(source_dir, subprocess_npm=subprocess_npm)
182-
else:
183-
install_action = NodejsNpmInstallAction(source_dir, subprocess_npm=subprocess_npm, is_production=False)
184-
185-
esbuild_action = EsbuildBundleAction(source_dir, artifacts_dir, bundler_config, osutils, subprocess_esbuild)
186-
return [install_action, esbuild_action]
187-
188-
def get_manifest_config(self, osutils, manifest_path):
189-
"""
190-
Get the aws_sam specific properties from the manifest, if they exist.
191-
192-
:type osutils: aws_lambda_builders.workflows.nodejs_npm.utils.OSUtils
193-
:param osutils: An instance of OS Utilities for file manipulation
194-
195-
:type manifest_path: str
196-
:param manifest_path: Path to the manifest file
197-
198-
:rtype: dict
199-
:return: Dict with aws_sam specific bundler configs
200-
"""
201-
LOG.debug("NODEJS reading manifest from %s", manifest_path)
202-
try:
203-
manifest = osutils.parse_json(manifest_path)
204-
if self.CONFIG_PROPERTY in manifest and isinstance(manifest[self.CONFIG_PROPERTY], dict):
205-
return manifest[self.CONFIG_PROPERTY]
206-
else:
207-
return {"bundler": ""}
208-
except (OSError, json.decoder.JSONDecodeError) as ex:
209-
raise WorkflowFailedError(workflow_name=self.NAME, action_name="ParseManifest", reason=str(ex))
210-
211136
def get_resolvers(self):
212137
"""
213138
specialized path resolver that just returns the list of executable for the runtime on the path.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""
2+
Builds NodeJS Lambda functions using NPM dependency manager with esbuild bundler
3+
"""
4+
5+
from .workflow import NodejsNpmEsbuildWorkflow
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""
2+
Actions specific to the esbuild bundler
3+
"""
4+
import logging
5+
from aws_lambda_builders.actions import BaseAction, Purpose, ActionFailedError
6+
from .esbuild import EsbuildExecutionError
7+
8+
LOG = logging.getLogger(__name__)
9+
10+
11+
class EsbuildBundleAction(BaseAction):
12+
13+
"""
14+
A Lambda Builder Action that packages a Node.js package using esbuild into a single file
15+
optionally transpiling TypeScript
16+
"""
17+
18+
NAME = "EsbuildBundle"
19+
DESCRIPTION = "Packaging source using Esbuild"
20+
PURPOSE = Purpose.COPY_SOURCE
21+
22+
def __init__(self, source_dir, artifacts_dir, bundler_config, osutils, subprocess_esbuild):
23+
"""
24+
:type source_dir: str
25+
:param source_dir: an existing (readable) directory containing source files
26+
27+
28+
:type artifacts_dir: str
29+
:param artifacts_dir: an existing (writable) directory where to store the output.
30+
Note that the actual result will be in the 'package' subdirectory here.
31+
32+
:type osutils: aws_lambda_builders.workflows.nodejs_npm.utils.OSUtils
33+
:param osutils: An instance of OS Utilities for file manipulation
34+
35+
:type subprocess_esbuild: aws_lambda_builders.workflows.nodejs_npm.npm.SubprocessEsbuild
36+
:param subprocess_esbuild: An instance of the Esbuild process wrapper
37+
"""
38+
super(EsbuildBundleAction, self).__init__()
39+
self.source_dir = source_dir
40+
self.artifacts_dir = artifacts_dir
41+
self.bundler_config = bundler_config
42+
self.osutils = osutils
43+
self.subprocess_esbuild = subprocess_esbuild
44+
45+
def execute(self):
46+
"""
47+
Runs the action.
48+
49+
:raises lambda_builders.actions.ActionFailedError: when esbuild packaging fails
50+
"""
51+
52+
if "entry_points" not in self.bundler_config:
53+
raise ActionFailedError("entry_points not set ({})".format(self.bundler_config))
54+
55+
entry_points = self.bundler_config["entry_points"]
56+
57+
if not isinstance(entry_points, list):
58+
raise ActionFailedError("entry_points must be a list ({})".format(self.bundler_config))
59+
60+
if not entry_points:
61+
raise ActionFailedError("entry_points must not be empty ({})".format(self.bundler_config))
62+
63+
entry_paths = [self.osutils.joinpath(self.source_dir, entry_point) for entry_point in entry_points]
64+
65+
LOG.debug("NODEJS building %s using esbuild to %s", entry_paths, self.artifacts_dir)
66+
67+
for entry_point in entry_paths:
68+
if not self.osutils.file_exists(entry_point):
69+
raise ActionFailedError("entry point {} does not exist".format(entry_point))
70+
71+
args = entry_points + ["--bundle", "--platform=node", "--format=cjs"]
72+
minify = self.bundler_config.get("minify", True)
73+
sourcemap = self.bundler_config.get("sourcemap", True)
74+
target = self.bundler_config.get("target", "es2020")
75+
if minify:
76+
args.append("--minify")
77+
if sourcemap:
78+
args.append("--sourcemap")
79+
args.append("--target={}".format(target))
80+
args.append("--outdir={}".format(self.artifacts_dir))
81+
try:
82+
self.subprocess_esbuild.run(args, cwd=self.source_dir)
83+
except EsbuildExecutionError as ex:
84+
raise ActionFailedError(str(ex))
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""
2+
esbuild specific utilities and feature flag
3+
"""
4+
5+
EXPERIMENTAL_FLAG_ESBUILD = "experimentalEsbuild"
6+
7+
8+
def is_experimental_esbuild_scope(experimental_flags):
9+
"""
10+
A function which will determine if experimental esbuild scope is active
11+
"""
12+
return bool(experimental_flags) and EXPERIMENTAL_FLAG_ESBUILD in experimental_flags

0 commit comments

Comments
 (0)