Skip to content
This repository has been archived by the owner on May 22, 2024. It is now read-only.

feat: support CommonJS syntax in V2 functions #1585

Merged
merged 17 commits into from
Oct 9, 2023

Conversation

eduardoboucas
Copy link
Member

@eduardoboucas eduardoboucas commented Oct 4, 2023

Summary

Adds support for CommonJS syntax when authoring V2 functions, including support for the config object in CJS.

It also adds a new inputModuleFormat property that contains cjs or esm based on the syntax used to author the function (which may differ from the module format that is actually generated, represented by outputModuleFormat from #1589).

Most of the PR is actually a cleanup around ISC and the static analysis code. I've added some inline comments that will hopefully make it easier to review, but feel free to ask anything that isn't clear.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 4, 2023

⏱ Benchmark results

Comparing with 1ea5222

largeDepsEsbuild: 2.5s

⬇️ 2.05% decrease vs. 1ea5222

^                                                           3.7s                                          
│                                                           ┌──┐                                          
│                                                           |  |                                          
│                                                   3.1s    |  |                                          
│                                                   ┌──┐    |  |                                          
│   2.8s    2.8s            2.7s    2.8s            |  |    |  |    2.7s            2.7s                  
│ ──┌──┐────┌──┐────2.6s────┌──┐────┌──┐────2.6s────┼──┼────┼──┼────┌──┐────────────┌──┐────2.6s────2.5s──
│   |  |    |  |    ┌──┐    |  |    |  |    ┌──┐    |  |    |  |    |  |    2.4s    |  |    ┌──┐    ┌──┐  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    ┌──┐    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
└───┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴──>
    T-12    T-11    T-10    T-9     T-8     T-7     T-6     T-5     T-4     T-3     T-2     T-1      T    
Legend

largeDepsNft: 7.6s

⬇️ 2.12% decrease vs. 1ea5222

^                                                          12.1s                                          
│                                                           ┌──┐                                          
│                                                           |  |                                          
│                                                  10.1s    |  |                                          
│                                                   ┌──┐    |  |                                          
│                                                   |  |    |  |                                          
│   8.3s    8.7s            8.5s    8.3s            |  |    |  |    8.3s                                  
│ ──┌──┐────┌──┐────7.6s────┌──┐────┌──┐────8.1s────┼──┼────┼──┼────┌──┐─────────────8s─────7.7s────7.6s──
│   |  |    |  |    ┌──┐    |  |    |  |    ┌──┐    |  |    |  |    |  |    7.5s    ┌──┐    ┌──┐    ┌──┐  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    ┌──┐    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
└───┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴──>
    T-12    T-11    T-10    T-9     T-8     T-7     T-6     T-5     T-4     T-3     T-2     T-1      T    
Legend

largeDepsZisi: 15.3s

⬆️ 1.03% increase vs. 1ea5222

^                                                          22.4s                                          
│                                                           ┌──┐                                          
│                                                           |  |                                          
│                                                  19.3s    |  |                                          
│                                                   ┌──┐    |  |                                          
│          16.4s                                    |  |    |  |                                          
│ ─16.1s────┌──┐───────────16.1s───15.9s───15.5s────┼──┼────┼──┼───15.7s───────────15.7s───15.1s───15.3s──
│   ┌──┐    |  |   14.8s    ┌──┐    ┌──┐    ┌──┐    |  |    |  |    ┌──┐   14.4s    ┌──┐    ┌──┐    ┌──┐  
│   |  |    |  |    ┌──┐    |  |    |  |    |  |    |  |    |  |    |  |    ┌──┐    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
│   |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |  |    |▒▒|  
└───┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴────┴──┴──>
    T-12    T-11    T-10    T-9     T-8     T-7     T-6     T-5     T-4     T-3     T-2     T-1      T    
Legend

schedule?: string
methods?: string[]
}

export interface StaticAnalysisResult extends ISCValues {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were using the term "ISC" to contain properties that are not part of the in-source configuration spec, like invocationMode or runtimeAPIVersion. They were grouped together because we extract both those properties and the ISC properties using the same static analysis step, but functionally they are different things.

So now we have a ISCValues type that holds just the in-source configuration properties, and a StaticAnalysisResult that holds those plus some additional metadata properties we'll extract using the static analysis mechanism.

A big part of this diff is just renaming ISC-related terms to a more generic "static analysis result".

@eduardoboucas eduardoboucas marked this pull request as ready for review October 4, 2023 18:37
@eduardoboucas eduardoboucas requested a review from a team as a code owner October 4, 2023 18:37
lukasholzer
lukasholzer previously approved these changes Oct 5, 2023
Copy link
Contributor

@lukasholzer lukasholzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me – hard to understand the full context often as the codebase lacks a lot of documentation sadly on what properties are or what they should do

// NFT cache, which should not be used in zisi and only supplied to NFT
// this cache shares the file cache with zisi
nftCache: NFTCache

// Used by `get-tsconfig` for caching the retrieval of `tsconfig.json` files
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: class properties should always be annotated with JSDoc to benefit from this documentation in the IntelliSense when you use it with this.tsConfigCache

* 4. The module format syntax used in the file: if any `import` or `export`
* declarations are found, this is ESM; if not, this is CJS
*/
export const traverseNodes = (nodes: Statement[], getAllBindings: BindingMethod) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

node.type === 'ImportDeclaration' ||
node.type === 'ExportNamedDeclaration' ||
node.type === 'ExportDefaultDeclaration' ||
node.type === 'ExportAllDeclaration'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, this looks a little bit brittle to me is this the AST from esbuild? – according to the naming I think it is an acorn AST. Are there enums for the values?

But can we define the inputModule format to ESM based on that? Whats about typescript or is this already the transpiled code we are parsing here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, this looks a little bit brittle to me is this the AST from esbuild? – according to the naming I think it is an acorn AST. Are there enums for the values?

esbuild doesn't expose an AST. This is from Babel, as you can see here:

import { parse } from '@babel/parser'
.

Can you elaborate on why you think this is brittle? The nodes are fully typed, so if there's a mismatch in this or in a future version of the AST lexicon, we'll know?

But can we define the inputModule format to ESM based on that? Whats about typescript or is this already the transpiled code we are parsing here?

As I explained in the PR body, we're not trying to infer the module type that we'll emit. We're simply trying to see what kind of syntax was used when authoring the function. If we see an import or an export, we know it's ESM syntax.

For TypeScript functions this won't matter, but we can use this information to detect when people are writing a .js function with ESM without a type: "module". That's the main purpose of surfacing this information.

@eduardoboucas
Copy link
Member Author

hard to understand the full context often as the codebase lacks a lot of documentation sadly on what properties are or what they should do

What parts of the codebase do you feel are lacking documentation?

@eduardoboucas
Copy link
Member Author

After discussing with @lukasholzer, we're not sure this is still needed in its current form. I still think some of the cleaning up we're doing here would be great to land, so I'm moving to draft until I have time to pick this up again.

@eduardoboucas eduardoboucas marked this pull request as draft October 5, 2023 12:36
@eduardoboucas eduardoboucas changed the base branch from main to feat/cjs-v2 October 7, 2023 14:45
@eduardoboucas eduardoboucas changed the title feat: add tsConfig property to listFunctions output feat: support CommonJS syntax in V2 functions Oct 7, 2023
* Loads a file at a given path, parses it into an AST, and returns a series of
* data points, such as in-source configuration properties and other metadata.
*/
export const parseFile = async (
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was renamed from findISCDeclarationsInPath. No functionality has changed.

@@ -101,27 +112,28 @@ export const findISCDeclarations = (
let scheduleFound = false

const getAllBindings = createBindingsMethod(ast.body)
const { configExport, defaultExport, handlerExports } = getExports(ast.body, getAllBindings)
const isV2API = handlerExports.length === 0 && defaultExport !== undefined
const { configExport, handlerExports, hasDefaultExport, inputModuleFormat } = traverseNodes(ast.body, getAllBindings)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were not using the contents of defaultExport for anything, just checking whether it exists or not. So I've changed it to a boolean that is now true if ESM or CJS default exports are found.

}

// Finds the main handler export in a CJS AST.
const getMainExportFromCJS = (node: Statement) => {
const getCJSExports = (node: Statement, name: string) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now generic so it can look for exports with different names.

* with a variable declaration (`export const foo = "bar"`), but also resolve
* bindings and find things like `const baz = "1"; export { baz as foo }`.
*/
const getNamedESMExport = (node: Statement, name: string, getAllBindings: BindingMethod) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed from getMainExportFromESM. This is now generic so it can look for exports with different names.

* - `export { handler }`, or
* - `export { x as "handler" }`
*/
const isNamedExport = (node: ExportNamedDeclaration['specifiers'][number], name: string): node is ExportSpecifier => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed from isHandlerExport. This is now generic so it can look for exports with different names.

Base automatically changed from feat/cjs-v2 to main October 9, 2023 08:23
@eduardoboucas eduardoboucas marked this pull request as ready for review October 9, 2023 08:27
Copy link
Contributor

@lukasholzer lukasholzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a test for the default export with a object literal

name: string,
getAllBindings: BindingMethod,
) => {
const specifier = specifiers.find((node) => isNamedExport(node, name))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here you get only the named exports what about doing an object literal export over a default?

const handler = () => {}

export default {
  handler
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same would apply to the cjs where it's:

const handler = () => {}

exports.default {
  handler
}

@eduardoboucas eduardoboucas merged commit 4e2fe8b into main Oct 9, 2023
@eduardoboucas eduardoboucas deleted the feat/function-metadata branch October 9, 2023 09:29
Skn0tt pushed a commit to netlify/build that referenced this pull request May 21, 2024
…p-it#1585)

* feat: add `inputModuleFormat` property

* feat: add `tsConfig` to `listFunctions` output

* chore: update tests

* chore: update tests

* chore: update snapshots

* chore: update snapshot

* refactor: rename methods

* feat: return `jsModuleFormat` property

* feat: add bundled paths to `inputs`

* feat: force bundler to NFT in V2 functions

* chore: fix test

* refactor: remove `tsconfig` property

* refactor: remove traces of `tsconfig`

* feat: support CJS in V2 functions

* refactor: tidy up
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants