cdk-esbuild icon indicating copy to clipboard operation
cdk-esbuild copied to clipboard

Support for Cloudfront Functions

Open mrgrain opened this issue 3 years ago • 16 comments

Cloudfront supports small inline JavaScript functions. They have some limitations, mainly regarding size and used memory.

We should evaluate if the existing inline code works with it, or add support and document the usage.

mrgrain avatar Oct 08 '21 22:10 mrgrain

They have some peculiar limitations.

Docs: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html

The CloudFront Functions JavaScript runtime environment is compliant with ECMAScript (ES) version 5.1 and also supports some features of ES versions 6 through 9. It also provides some nonstandard methods that are not part of the ES specifications. The following topics list all the supported language features.

The const and let statements are not supported.

Restricted features: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html#writing-functions-javascript-features-restricted-features

  • Dynamic code evaluation
  • Timers
  • Date and timestamps
  • File system access
  • Network access

moltar avatar Oct 09 '21 10:10 moltar

Interesting! Thanks for the reading links. I guess with esbuild we would be able to set the compile target to es5. The other limitations will be harder to enforce, and probably have to be left to the user to ensure compatibility. 🤔

mrgrain avatar Oct 09 '21 13:10 mrgrain

The other limitations will be harder to enforce, and probably have to be left to the user to ensure compatibility.

Would probably be a lot of work, and difficult to configure, but maybe eslint rules?

Probably outside the scope of this package though.

moltar avatar Oct 09 '21 13:10 moltar

This issue is now marked as stale because it hasn't seen activity for a while. Add a comment or it will be closed soon.

github-actions[bot] avatar Dec 17 '21 02:12 github-actions[bot]

This issue is now marked as stale because it hasn't seen activity for a while. Add a comment or it will be closed soon. If you wish to exclude this issue from being marked as stale, add the "backlog" label.

github-actions[bot] avatar Feb 16 '22 02:02 github-actions[bot]

Cloudfront Functions requires ES5 which esbuild cannot target right now. See: https://github.com/evanw/esbuild/issues/297

mrgrain avatar Mar 14 '22 14:03 mrgrain

Definitely having eslint rules for Cloudfront Functions would be amazing

gunta avatar May 24 '22 09:05 gunta

I've actually had decent luck with this esbuild config:

build({
    entryPoints: [join(cloudFrontFunction, "src", "index.ts")],
    outdir: join(cloudFrontFunction, "dist"),

    // Make compatible with CloudFront Functions limited ES5 JavaScript runtime
    // https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html#writing-functions-javascript-features-core
    format: "cjs",
    target: "es5",
    platform: "neutral",
    treeShaking: true,
    banner: {
      js: "var module = {};",  // allows exporting functions from TS files to unit test
    },
    minifyIdentifiers: false,
    supported: {
      "const-and-let": false,  // throws a build-time error if you use `const` or `let`, but at least warns you
      "exponent-operator": true,
      "template-literal": true,
      arrow: true,
      "rest-argument": true,
      "regexp-named-capture-groups": true,
    },
  });

It definitely doesn't transpile everything, but it works pretty well!

blimmer avatar Sep 15 '23 23:09 blimmer

Cool, than you! I might pull this config out into a Cloudfront Function Construct.

mrgrain avatar Sep 16 '23 15:09 mrgrain

Sounds good - I'd be happy to collab on that PR if it would be helpful. For me, it has been great to write my CloudFront functions in TS, write tests in jest, and then deploy the ES5-ish compatible version to AWS.

There are some intricacies we'd probably need to document. For instance, with the config I posted and this very simple example:

import type { CloudFrontFunctionsEvent } from "aws-lambda";

export function handler(event: CloudFrontFunctionsEvent) {
  const request = event.request;
  return request;
}

You get this error:

✘ [ERROR] Transforming const to the configured target environment ("es5" + 6 overrides) is not supported yet

    src/stacks/cloudfront-functions/content-preview/src/index.ts:4:2:
      4 │   const request = event.request;
        ╵   ~~~~~

/Users/blimmer/code/company/stacks/node_modules/esbuild/lib/main.js:1650
  let error = new Error(text);
              ^

Error: Build failed with 1 error:
src/stacks/cloudfront-functions/content-preview/src/index.ts:4:2: ERROR: Transforming const to the configured target environment ("es5" + 6 overrides) is not supported yet
    at failureErrorWithLog (/Users/blimmer/code/company/stacks/node_modules/esbuild/lib/main.js:1650:15)
    at /Users/blimmer/code/company/stacks/node_modules/esbuild/lib/main.js:1059:25
    at /Users/blimmer/code/company/stacks/node_modules/esbuild/lib/main.js:1004:52
    at buildResponseToResult (/Users/blimmer/code/company/stacks/node_modules/esbuild/lib/main.js:1057:7)
    at /Users/blimmer/code/company/stacks/node_modules/esbuild/lib/main.js:1086:16
    at responseCallbacks.<computed> (/Users/blimmer/code/company/stacks/node_modules/esbuild/lib/main.js:703:9)
    at handleIncomingPacket (/Users/blimmer/code/company/stacks/node_modules/esbuild/lib/main.js:763:9)
    at Socket.readFromStdout (/Users/blimmer/code/company/stacks/node_modules/esbuild/lib/main.js:679:7)
    at Socket.emit (node:events:514:28)
    at addChunk (node:internal/streams/readable:324:12) {
  errors: [Getter/Setter],
  warnings: [Getter/Setter]
}

Node.js v18.17.1

so you have to write:

import type { CloudFrontFunctionsEvent } from "aws-lambda";

export function handler(event: CloudFrontFunctionsEvent) {
  var request = event.request;
  return request;
}

which is surprising.

Also, writing console.info anywhere doesn't throw an esbuild error, so you wouldn't find out about issues until runtime. So, the experience isn't killer, but it's better than nothing.

blimmer avatar Sep 17 '23 00:09 blimmer

Also, writing console.info anywhere doesn't throw an esbuild error, so you wouldn't find out about issues until runtime. So, the experience isn't killer, but it's better than nothing.

Maybe it'd be possible to achieve this via esbuild plugins?

Or ESLint rules?

moltar avatar Sep 17 '23 08:09 moltar

Disabling features in esbuild and causing a build failure seems like the earliest possible point that fits the remit of this construct.

Ideally, we would have eslint rules and plugins available. Maybe a types package that is used instead of @types/node would help as well. If any of these come about, I'd be happy to include it here however it makes sense (code or docs).

mrgrain avatar Sep 17 '23 15:09 mrgrain

I wonder if this is as relevant anymore now that runtime 2.0 is out: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html

blimmer avatar May 10 '24 19:05 blimmer

There are still many restricted features: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html#writing-functions-javascript-features-restricted-features-20

moltar avatar May 10 '24 19:05 moltar

Found this today, but did not try yet: https://www.npmjs.com/package/esbuild-cf-functions-plugin

moltar avatar Jul 02 '24 21:07 moltar

Nice find! Using a plugin is going to be annoying, but the settings might be easy enough to adapt. Although from looking at the source, it seems to do fairly basic stuff.

mrgrain avatar Jul 02 '24 21:07 mrgrain