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

Commit

Permalink
feat: convert Runtime, NodeBundler and ModuleFormat to enums (#1179)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: When using zip-it-and-ship-it with TS the types for the config and some return
types changed. `nodeBundler` in the config, `moduleFormat` and `runtime` in return objects are
now also enums. These const enums are exported in the main entry point for your use.

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
danez and kodiakhq[bot] committed Aug 17, 2022
1 parent 850404d commit b472a84
Show file tree
Hide file tree
Showing 23 changed files with 117 additions and 61 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.cjs
Expand Up @@ -30,6 +30,9 @@ module.exports = {
// in imports
'import/extensions': 'off',
'import/no-namespace': 'off',
// https://github.com/typescript-eslint/typescript-eslint/issues/2483
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
},
},
{
Expand Down
4 changes: 2 additions & 2 deletions src/config.ts
@@ -1,7 +1,7 @@
import mergeOptions from 'merge-options'

import { FunctionSource } from './function.js'
import type { NodeBundlerName } from './runtimes/node/bundlers/types.js'
import type { NodeBundlerType } from './runtimes/node/bundlers/types.js'
import type { NodeVersionString } from './runtimes/node/index.js'
import { minimatch } from './utils/matching.js'

Expand All @@ -10,7 +10,7 @@ export interface FunctionConfig {
includedFiles?: string[]
includedFilesBasePath?: string
ignoredNodeModules?: string[]
nodeBundler?: NodeBundlerName
nodeBundler?: NodeBundlerType
nodeSourcemap?: boolean
nodeVersion?: NodeVersionString
processDynamicNodeImports?: boolean
Expand Down
10 changes: 7 additions & 3 deletions src/main.ts
Expand Up @@ -5,15 +5,19 @@ import { FeatureFlags, getFlags } from './feature_flags.js'
import { FunctionSource } from './function.js'
import { getFunctionFromPath, getFunctionsFromPaths } from './runtimes/index.js'
import { findISCDeclarationsInPath, ISCValues } from './runtimes/node/in_source_config/index.js'
import { GetSrcFilesFunction, RuntimeName } from './runtimes/runtime.js'
import { GetSrcFilesFunction, RuntimeType } from './runtimes/runtime.js'
import { listFunctionsDirectories, resolveFunctionsDirectories } from './utils/fs.js'

export { zipFunction, zipFunctions } from './zip.js'

export { NodeBundlerType } from './runtimes/node/bundlers/types.js'
export { RuntimeType } from './runtimes/runtime.js'
export { ModuleFormat } from './runtimes/node/utils/module_format.js'

interface ListedFunction {
name: string
mainFile: string
runtime: RuntimeName
runtime: RuntimeType
extension: string
schedule?: string
}
Expand All @@ -35,7 +39,7 @@ interface AugmentedFunctionSource extends FunctionSource {

const augmentWithISC = async (func: FunctionSource): Promise<AugmentedFunctionSource> => {
// ISC is currently only supported in JavaScript and TypeScript functions.
if (func.runtime.name !== 'js') {
if (func.runtime.name !== RuntimeType.JAVASCRIPT) {
return func
}

Expand Down
14 changes: 7 additions & 7 deletions src/runtimes/detect_runtime.ts
@@ -1,10 +1,10 @@
import type { Buffer } from 'buffer'

import { detect, Runtime, Arch, Platform, BinaryInfo } from '@netlify/binary-info'
import { detect, Runtime as BinaryRuntime, Arch, Platform, BinaryInfo } from '@netlify/binary-info'

import { cachedReadFile, FsCache } from '../utils/fs.js'

import type { RuntimeName } from './runtime.js'
import { RuntimeType } from './runtime.js'

const isValidFunctionBinary = (info: BinaryInfo) => info.arch === Arch.Amd64 && info.platform === Platform.Linux

Expand All @@ -27,7 +27,7 @@ export const detectBinaryRuntime = async function ({
}: {
fsCache: FsCache
path: string
}): Promise<RuntimeName | undefined> {
}): Promise<RuntimeType | undefined> {
try {
const buffer = await cachedReadFile(fsCache, path)

Expand All @@ -41,10 +41,10 @@ export const detectBinaryRuntime = async function ({
}

switch (binaryInfo.runtime) {
case Runtime.Go:
return 'go'
case Runtime.Rust:
return 'rs'
case BinaryRuntime.Go:
return RuntimeType.GO
case BinaryRuntime.Rust:
return RuntimeType.RUST
default:
return undefined
}
Expand Down
3 changes: 2 additions & 1 deletion src/runtimes/go/builder.ts
Expand Up @@ -3,6 +3,7 @@ import { basename } from 'path'

import { FunctionBundlingUserError } from '../../utils/error.js'
import { shellUtils } from '../../utils/shell.js'
import { RuntimeType } from '../runtime.js'

export const build = async ({ destPath, mainFile, srcDir }: { destPath: string; mainFile: string; srcDir: string }) => {
const functionName = basename(srcDir)
Expand All @@ -19,7 +20,7 @@ export const build = async ({ destPath, mainFile, srcDir }: { destPath: string;
} catch (error) {
console.error(`Could not compile Go function ${functionName}:\n`)

throw FunctionBundlingUserError.addCustomErrorInfo(error, { functionName, runtime: 'go' })
throw FunctionBundlingUserError.addCustomErrorInfo(error, { functionName, runtime: RuntimeType.GO })
}

const stat = await fs.lstat(destPath)
Expand Down
12 changes: 9 additions & 3 deletions src/runtimes/go/index.ts
Expand Up @@ -8,7 +8,13 @@ import { cachedLstat, cachedReaddir, FsCache } from '../../utils/fs.js'
import { nonNullable } from '../../utils/non_nullable.js'
import { zipBinary } from '../../zip_binary.js'
import { detectBinaryRuntime } from '../detect_runtime.js'
import { FindFunctionInPathFunction, FindFunctionsInPathsFunction, Runtime, ZipFunction } from '../runtime.js'
import {
FindFunctionInPathFunction,
FindFunctionsInPathsFunction,
Runtime,
RuntimeType,
ZipFunction,
} from '../runtime.js'

import { build } from './builder.js'

Expand Down Expand Up @@ -47,7 +53,7 @@ const findFunctionsInPaths: FindFunctionsInPathsFunction = async function ({ fea
const findFunctionInPath: FindFunctionInPathFunction = async function ({ fsCache, path }) {
const runtime = await detectBinaryRuntime({ fsCache, path })

if (runtime === 'go') {
if (runtime === RuntimeType.GO) {
return processBinary({ fsCache, path })
}

Expand Down Expand Up @@ -149,6 +155,6 @@ const zipFunction: ZipFunction = async function ({ config, destFolder, filename,
return { config, path: destPath }
}

const runtime: Runtime = { findFunctionsInPaths, findFunctionInPath, name: 'go', zipFunction }
const runtime: Runtime = { findFunctionsInPaths, findFunctionInPath, name: RuntimeType.GO, zipFunction }

export default runtime
8 changes: 7 additions & 1 deletion src/runtimes/node/bundlers/esbuild/bundler.ts
Expand Up @@ -7,6 +7,8 @@ import type { FunctionConfig } from '../../../../config.js'
import { FeatureFlags } from '../../../../feature_flags.js'
import { FunctionBundlingUserError } from '../../../../utils/error.js'
import { getPathWithExtension, safeUnlink } from '../../../../utils/fs.js'
import { RuntimeType } from '../../../runtime.js'
import { NodeBundlerType } from '../types.js'

import { getBundlerTarget, getModuleFormat } from './bundler_target.js'
import { getDynamicImportsPlugin } from './plugin_dynamic_imports.js'
Expand Down Expand Up @@ -133,7 +135,11 @@ export const bundleJsFile = async function ({
warnings,
}
} catch (error) {
throw FunctionBundlingUserError.addCustomErrorInfo(error, { functionName: name, runtime: 'js', bundler: 'esbuild' })
throw FunctionBundlingUserError.addCustomErrorInfo(error, {
functionName: name,
runtime: RuntimeType.JAVASCRIPT,
bundler: NodeBundlerType.ESBUILD,
})
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/runtimes/node/bundlers/esbuild/bundler_target.ts
Expand Up @@ -39,13 +39,13 @@ const getModuleFormat = async (
if (featureFlags.zisi_pure_esm && packageJsonFile?.contents.type === 'module' && nodeSupport.esm) {
return {
includedFiles: [packageJsonFile.path],
moduleFormat: 'esm',
moduleFormat: ModuleFormat.ESM,
}
}

return {
includedFiles: [],
moduleFormat: 'cjs',
moduleFormat: ModuleFormat.COMMONJS,
}
}

Expand Down
20 changes: 10 additions & 10 deletions src/runtimes/node/bundlers/index.ts
Expand Up @@ -3,19 +3,19 @@ import { detectEsModule } from '../utils/detect_es_module.js'

import esbuildBundler from './esbuild/index.js'
import nftBundler from './nft/index.js'
import type { NodeBundler, NodeBundlerName } from './types.js'
import { NodeBundler, NodeBundlerType } from './types.js'
import zisiBundler from './zisi/index.js'

export const getBundler = (name: NodeBundlerName): NodeBundler => {
export const getBundler = (name: NodeBundlerType): NodeBundler => {
switch (name) {
case 'esbuild':
case 'esbuild_zisi':
case NodeBundlerType.ESBUILD:
case NodeBundlerType.ESBUILD_ZISI:
return esbuildBundler

case 'nft':
case NodeBundlerType.NFT:
return nftBundler

case 'zisi':
case NodeBundlerType.ZISI:
return zisiBundler

default:
Expand All @@ -33,16 +33,16 @@ export const getDefaultBundler = async ({
extension: string
mainFile: string
featureFlags: FeatureFlags
}): Promise<NodeBundlerName> => {
}): Promise<NodeBundlerType> => {
if (['.mjs', '.ts'].includes(extension)) {
return 'esbuild'
return NodeBundlerType.ESBUILD
}

if (featureFlags.traceWithNft) {
return 'nft'
return NodeBundlerType.NFT
}

const functionIsESM = await detectEsModule({ mainFile })

return functionIsESM ? 'nft' : 'zisi'
return functionIsESM ? NodeBundlerType.NFT : NodeBundlerType.ZISI
}
6 changes: 3 additions & 3 deletions src/runtimes/node/bundlers/nft/es_modules.ts
Expand Up @@ -71,7 +71,7 @@ export const processESM = async ({

if (!entrypointIsESM) {
return {
moduleFormat: 'cjs',
moduleFormat: ModuleFormat.COMMONJS,
}
}

Expand All @@ -80,14 +80,14 @@ export const processESM = async ({

if (featureFlags.zisi_pure_esm && packageJson.type === 'module' && nodeSupport.esm) {
return {
moduleFormat: 'esm',
moduleFormat: ModuleFormat.ESM,
}
}

const rewrites = await transpileESM({ basePath, config, esmPaths, fsCache, reasons, name })

return {
moduleFormat: 'cjs',
moduleFormat: ModuleFormat.COMMONJS,
rewrites,
}
}
Expand Down
11 changes: 9 additions & 2 deletions src/runtimes/node/bundlers/nft/transpile.ts
Expand Up @@ -2,7 +2,10 @@ import { build } from '@netlify/esbuild'

import type { FunctionConfig } from '../../../../config.js'
import { FunctionBundlingUserError } from '../../../../utils/error.js'
import { RuntimeType } from '../../../runtime.js'
import { ModuleFormat } from '../../utils/module_format.js'
import { getBundlerTarget } from '../esbuild/bundler_target.js'
import { NodeBundlerType } from '../types.js'

export const transpile = async (path: string, config: FunctionConfig, functionName: string) => {
// The version of ECMAScript to use as the build target. This will determine
Expand All @@ -13,7 +16,7 @@ export const transpile = async (path: string, config: FunctionConfig, functionNa
const transpiled = await build({
bundle: false,
entryPoints: [path],
format: 'cjs',
format: ModuleFormat.COMMONJS,
logLevel: 'error',
platform: 'node',
sourcemap: Boolean(config.nodeSourcemap),
Expand All @@ -23,6 +26,10 @@ export const transpile = async (path: string, config: FunctionConfig, functionNa

return transpiled.outputFiles[0].text
} catch (error) {
throw FunctionBundlingUserError.addCustomErrorInfo(error, { functionName, runtime: 'js', bundler: 'nft' })
throw FunctionBundlingUserError.addCustomErrorInfo(error, {
functionName,
runtime: RuntimeType.JAVASCRIPT,
bundler: NodeBundlerType.NFT,
})
}
}
8 changes: 7 additions & 1 deletion src/runtimes/node/bundlers/types.ts
Expand Up @@ -5,7 +5,13 @@ import type { FeatureFlag, FeatureFlags } from '../../../feature_flags.js'
import type { FunctionSource } from '../../../function.js'
import type { ModuleFormat } from '../utils/module_format.js'

export type NodeBundlerName = 'esbuild' | 'esbuild_zisi' | 'nft' | 'zisi'
export const enum NodeBundlerType {
ESBUILD = 'esbuild',
ESBUILD_ZISI = 'esbuild_zisi',
NFT = 'nft',
ZISI = 'zisi',
SKIP = 'skip',
}

// TODO: Create a generic warning type
type BundlerWarning = Message
Expand Down
3 changes: 2 additions & 1 deletion src/runtimes/node/bundlers/zisi/index.ts
@@ -1,6 +1,7 @@
import { dirname, normalize } from 'path'

import { getBasePath } from '../../utils/base_path.js'
import { ModuleFormat } from '../../utils/module_format.js'
import type { BundleFunction } from '../types.js'

import { getSrcFiles } from './src_files.js'
Expand Down Expand Up @@ -43,7 +44,7 @@ const bundle: BundleFunction = async ({
includedFiles,
inputs: srcFiles,
mainFile,
moduleFormat: 'cjs',
moduleFormat: ModuleFormat.COMMONJS,
srcFiles,
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/runtimes/node/bundlers/zisi/list_imports.ts
Expand Up @@ -4,6 +4,8 @@ import { tmpName } from 'tmp-promise'

import { FunctionBundlingUserError } from '../../../../utils/error.js'
import { safeUnlink } from '../../../../utils/fs.js'
import { RuntimeType } from '../../../runtime.js'
import { NodeBundlerType } from '../types.js'

// Maximum number of log messages that an esbuild instance will produce. This
// limit is important to avoid out-of-memory errors due to too much data being
Expand Down Expand Up @@ -57,7 +59,11 @@ export const listImports = async ({
target: 'esnext',
})
} catch (error) {
throw FunctionBundlingUserError.addCustomErrorInfo(error, { functionName, runtime: 'js', bundler: 'zisi' })
throw FunctionBundlingUserError.addCustomErrorInfo(error, {
functionName,
runtime: RuntimeType.JAVASCRIPT,
bundler: NodeBundlerType.ZISI,
})
} finally {
await safeUnlink(targetPath)
}
Expand Down
5 changes: 3 additions & 2 deletions src/runtimes/node/in_source_config/index.ts
Expand Up @@ -2,6 +2,7 @@ import { ArgumentPlaceholder, Expression, SpreadElement, JSXNamespacedName } fro

import { FunctionBundlingUserError } from '../../../utils/error.js'
import { nonNullable } from '../../../utils/non_nullable.js'
import { RuntimeType } from '../../runtime.js'
import { createBindingsMethod } from '../parser/bindings.js'
import { getMainExport } from '../parser/exports.js'
import { getImports } from '../parser/imports.js'
Expand All @@ -17,14 +18,14 @@ const validateScheduleFunction = (functionFound: boolean, scheduleFound: boolean
if (!functionFound) {
throw new FunctionBundlingUserError(
"The `schedule` helper was imported but we couldn't find any usages. If you meant to schedule a function, please check that `schedule` is invoked and `handler` correctly exported.",
{ functionName, runtime: 'js' },
{ functionName, runtime: RuntimeType.JAVASCRIPT },
)
}

if (!scheduleFound) {
throw new FunctionBundlingUserError(
'Unable to find cron expression for scheduled function. The cron expression (first argument) for the `schedule` helper needs to be accessible inside the file and cannot be imported.',
{ functionName, runtime: 'js' },
{ functionName, runtime: RuntimeType.JAVASCRIPT },
)
}
}
Expand Down

1 comment on commit b472a84

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

⏱ Benchmark results

largeDepsEsbuild: 7.1s

largeDepsNft: 35.5s

largeDepsZisi: 53.1s

Please sign in to comment.