serverless-esbuild
serverless-esbuild copied to clipboard
Esbuild plugins do not work. Serverless exists with error: Plugin is missing a setup function
Describe the bug
When attempting to use any esbuild plugin, the following error occurs:
node_modules/esbuild/lib/main.js:786:16: ERROR: [plugin: plugin:copy] Plugin is missing a setup function
To Reproduce
This error occurs with any plugin.
Here's a git repo that demonstrates the problem in both javascript and typescript: plugin-missing-setup-function
Example:
app.ts
export const api = () => {
console.log('Hello from the custom Lambda!')
}
helloPlugin.ts
export const helloPlugin = {
name: 'env',
setup(build) {
console.log('Hello World')
},
}
serverless.ts
import type { Serverless } from 'serverless/aws';
import { helloPlugin } from './helloPlugin'
const serverlessConfiguration: Partial<Serverless> = <Serverless>{
frameworkVersion: '3',
service: 'thing',
plugins: [
'serverless-esbuild',
'serverless-offline',
],
package: {
individually: true,
},
custom: {
esbuild: {
entryPoints: ['app.js'],
bundle: true,
outfile: 'out.js',
plugins: [helloPlugin],
},
'serverless-offline': {
host: "",
httpPort: "",
},
},
provider: {
name: 'aws',
runtime: 'nodejs16.x',
stage: 'staging',
},
functions: {
'hello-world': {
handler: 'app.js',
}
}
}
module.exports = serverlessConfiguration;
package.json
{
"name": "plugin-missing-setup-function",
"version": "0.0.1",
"description": "",
"main": "app.js",
"license": "ISC",
"devDependencies": {
"@types/aws-lambda": "^8.10.44",
"@types/serverless": "3.12.7",
"esbuild-plugin-tsc": "^0.3.1",
"serverless": "^3.21.0",
"serverless-esbuild": "^1.32.5",
"serverless-offline": "^8.8.1"
}
}
Independent esbuild.config.ts
import { build, BuildOptions } from 'esbuild'
import { helloPlugin } from './helloPlugin'
const options = {
entryPoints: ['app.ts'],
bundle: true,
outfile: 'out.js',
plugins: [helloPlugin],
} as BuildOptions
(async () => {
const res = await build(options as BuildOptions)
})()
✗ ts-node esbuild.config.ts [22/08/14| 4:35PM]
Hello World
✗ serverless offline start --verbose
Running "serverless" from node_modules
Compiling to node16 bundle with esbuild...
Compiling with concurrency: Infinity
✘ [ERROR] [plugin env] Plugin is missing a setup function
.../app/node_modules/esbuild/lib/main.js:798:16:
798 │ throw new Error(`Plugin is missing a setup function`);
╵ ^
at handlePlugins (.../node_modules/esbuild/lib/main.js:798:17)
at Object.buildOrServe (.../node_modules/esbuild/lib/main.js:1149:7)
at .../node_modules/esbuild/lib/main.js:2110:17
at new Promise (<anonymous>)
at Object.build (.../node_modules/esbuild/lib/main.js:2109:14)
at build (.../node_modules/esbuild/lib/main.js:1956:51)
at bundleMapper (.../node_modules/serverless-esbuild/dist/bundle.js:69:50)
at .../node_modules/serverless-esbuild/node_modules/p-map/index.js:57:28
Environment: darwin, node 12.22.12, framework 3.21.0 (local) 3.21.0v (global), plugin 6.2.2, SDK 4.3.2
Docs: docs.serverless.com
Support: forum.serverless.com
Bugs: github.com/serverless/serverless/issues
Error:
Error: Build failed with 1 error:
.../node_modules/esbuild/lib/main.js:798:16: ERROR: [plugin: env] Plugin is missing a setup function
at failureErrorWithLog (.../node_modules/esbuild/lib/main.js:1624:15)
at .../node_modules/esbuild/lib/main.js:1143:18
at .../node_modules/esbuild/lib/main.js:1138:9
at .../node_modules/esbuild/lib/main.js:678:9
at handleIncomingPacket (...node_modules/esbuild/lib/main.js:775:9)
at Socket.readFromStdout (.../node_modules/esbuild/lib/main.js:644:7)
at Socket.emit (events.js:314:20)
at Socket.EventEmitter.emit (domain.js:483:12)
at addChunk (_stream_readable.js:297:12)
at readableAddChunk (_stream_readable.js:272:9)
at Socket.Readable.push (_stream_readable.js:213:10)
at Pipe.onStreamRead (internal/stream_base_commons.js:188:23)
Expected behavior
Expect serverless to build and run using esbuild with plugins and not crash.
Versions (please complete the following information):
- OS: Mac
- Serverless Framework Version:
Framework Core: 3.21.0 (local) 3.21.0 (global)
Plugin: 6.2.2
SDK: 4.3.2
- Plugin Version: [e.g. 1.25.0]
"serverless-esbuild": "^1.32.5",
Additional context
It looks like any function passed into the esbuild config fails.
For instance, if you pass a function to the watch option, it is removed.
Is there some sort of marshalling/un-marshalling happening where functions are inadvertently deleted?
probably because of ESM/CJS issues. Think esbuild plugins may be expected in CJS
Interesting idea... Experimenting around with ESM/CJS and changing the helloPlugin.ts CJS style also fails:
module.exports = {
name: 'hello',
setup(build: any) {
console.log('Hello World')
},
}
with serverless.ts:
const helloPlugin = require('./helloPlugin')
const serverlessConfiguration = {
...
This still results in:
ERROR: [plugin: hello] Plugin is missing a setup function
Not Working Serverless Javascript Configuration:
helloPlugin.js
const helloPlugin = {
name: 'hello',
setup(config) {
console.log('Hello World')
},
}
exports.helloPlugin = helloPlugin;
serverless.js
const { helloPlugin } = require('./helloPlugin')
const serverlessConfiguration = {
frameworkVersion: '3',
service: 'hello-world',
plugins: [
'serverless-esbuild',
'serverless-offline',
],
custom: {
esbuild: {
entryPoints: ['app.js'],
platform: 'node',
target: 'node16',
bundle: true,
outdir: '.esbuild/.build',
plugins: [helloPlugin],
},
'serverless-offline': {
host: "localhost",
httpPort: 3005
},
},
provider: {
name: 'aws',
runtime: 'nodejs16.x',
},
functions: {
'hello-world': {
handler: 'app.api',
events: [{
http: {
path: 'hello',
method: 'get',
},
}],
}
}
}
module.exports = serverlessConfiguration
serverless offline start
✘ [ERROR] [plugin hello] Plugin is missing a setup function
Working ESBuild configuration
esbuild.config.js
const { helloPlugin } = require('./helloPlugin.js')
const { build } = require('esbuild');
const options = {
entryPoints: ['app.js'],
platform: 'node',
target: 'node16',
bundle: true,
outdir: '.esbuild/.build',
plugins: [helloPlugin],
}
build(options)
.then(result => {
console.log('Esbuild result:', result);
})
.catch(error => {
console.log('Esbuild error:', error);
})
node esbuild.config.js
Hello World
Esbuild result: { errors: [], warnings: [] }
If the serverless.js is converted to a serverless.yml, a clue is given:
frameworkVersion: '3'
service: hello-world
plugins:
- serverless-esbuild
- serverless-offline
custom:
esbuild:
entryPoints:
- app.js
platform: node
target: node16
bundle: true
outdir: .esbuild/.build
plugins:
- helloPlugin
serverless-offline:
host: localhost
httpPort: 3005
provider:
name: aws
runtime: nodejs16.x
functions:
hello-world:
handler: app.api
events:
- http:
path: hello
method: get
The error now is:
✘ [ERROR] Plugin at index 0 must be an object
Which might mean the problem is related to how the configuration is handled by the serverless framework itself, not the serverless-esbuild plugin.
I have raised this in the serverless framework main repo to see if there are any insights that can be found there: https://github.com/serverless/serverless/issues/11388
Example repo: https://github.com/robblovell/plugin-missing-setup-function
Hey have you seen how we're asking you to set it up in the documentation? That might solve your issue https://github.com/floydspace/serverless-esbuild#esbuild-plugins
I tested it using serverless.ts.
As a prerequisite, importing the esbuild plugin directly into serverless.ts didn't work. So, referring to the documentation, I changed it as follows.
- serverless.ts
custom: {
esbuild: {
bundle: true,
minify: false,
sourcemap: true,
exclude: '*',
target: 'node16',
define: { 'require.resolve': undefined },
platform: 'node',
concurrency: 10,
plugins: 'esbuild-plugins.ts',
},
},
- esbuild-plugins.ts
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { nodeExternalsPlugin } = require('esbuild-node-externals');
module.exports = () => {
return [nodeExternalsPlugin()];
};
Interestingly, even though the files we load are TypeScript, we can still deploy with this configuration.
However, the esbuild-plugins.ts
file must be written in CommonJS format. Otherwise you will get an error like:
Error:
Error: "plugins" must be an array
at Object.buildOrServe (/home/<User>/Projects/<Sample>/node_modules/esbuild/lib/main.js:1125:17)
at /home/<User>/Projects/<Sample>/node_modules/esbuild/lib/main.js:2110:17
at new Promise (<anonymous>)
at Object.build (/home/<User>/Projects/<Sample>/node_modules/esbuild/lib/main.js:2109:14)
at build (/home/<User>/Projects/<Sample>/node_modules/esbuild/lib/main.js:1956:51)
at bundleMapper (/home/<User>/Projects/<Sample>/node_modules/serverless-esbuild/dist/bundle.js:69:50)
at /home/<User>/Projects/<Sample>/node_modules/p-map/index.js:57:28
However, this configuration method is not intuitive, so I think it would be better if we could use the writing style suggested by @robblovell.
Im also getting the same error when attempting to use "esbuild-plugin-copy";`
import { copy } from "esbuild-plugin-copy";
....
esbuild: {
bundle: true,
minify: false,
sourcemap: true,
exclude: ["aws-sdk"],
target: "node16",
define: { "require.resolve": undefined },
platform: "node",
concurrency: 10,
plugins: [
copy({
resolveFrom: "cwd",
assets: {
from: ["src/templates"],
to: ["templates"],
},
}),
],
},
✘ [ERROR] [plugin plugin:copy] Plugin is missing a setup function
/home/kay/checkpoint/email-service/node_modules/esbuild/lib/main.js:798:16: 798 │ throw new Error(`Plugin is missing a setup function`);
@kaito3desuyo Thanks so much for that tip 🙏