Reputation: 10930
My JavaScript Firebase Cloud Functions are running with npm modules imported with require
. Now I want to use npm modules that are installed with import
, not require
. The Firebase Emulators throw this error:
SyntaxError: Cannot use import statement outside a module
The Node.js documentation says:
import
statementsAn
import
statement can reference an ES module or a CommonJS module.import
statements are permitted only in ES modules, but dynamicimport()
expressions are supported in CommonJS for loading ES modules.When importing CommonJS modules, the
module.exports
object is provided as the default export. Named exports may be available, provided by static analysis as a convenience for better ecosystem compatibility.require
The CommonJS module
require
always treats the files it references as CommonJS.Using
require
to load an ES module is not supported because ES modules have asynchronous execution. Instead, useimport()
to load an ES module from a CommonJS module.
If I understand this correctly, Node modules can either be ES
or CommonJS
, that import
handles both types, and require
handles only CommonJS
.
This documentation also suggests that my Cloud Function also needs to be an ES module to use import
. Should the error message say:
SyntaxError: Cannot use import statement outside an ES module
That seems to be the problem: my Cloud Functions aren't in an ES module. How do I make an ES module for my Cloud Functions?
Reproduce error
Here's how to reproduce the error. Make a new directory and install Firebase, following the official documentation:
npm install -g firebase-tools
firebase init
Select Emulators
.
Select Create a new project
.
Select Functions Emulator
and Firestore Emulator
.
Emulators Setup
Accept the default ports. Download the emulators.
Functions Setup
Select TypeScript
. Don't use ESLint. Install dependencies.
Emulate execution of your functions
firebase emulators:start
Here's the default index.ts
.
import * as functions from "firebase-functions";
// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript
//
export const helloWorld = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase!");
});
I also tried the module provided in the TypeScript documentation on modules:
import * as functions from "firebase-functions";
export default function helloWorld() {
console.log("Hello, world!");
}
Fix path to index.ts
The first bug in functions/package.json
is:
functions/lib/index.js does not exist, can't deploy Cloud Functions
Fix this by opening functions/package.json
and changing
"main": "lib/index.js",
to
"main": "src/index.ts",
Module error
The next error is
Cannot use import statement outside a module
This is where I'm stuck. This seems to be saying that my Firebase Cloud Functions are not in an ES Module.
package.json
This question said to put "type": "module",
in functions/package.json
:
{
"name": "functions",
"type": "module",
"scripts": {
"build": "tsc",
"build:watch": "tsc --watch",
"serve": "npm run build && firebase emulators:start --only functions",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "16"
},
"main": "src/index.ts",
"dependencies": {
"firebase-admin": "^11.2.0",
"firebase-functions": "^4.0.1",
"got": "^12.5.2"
},
"devDependencies": {
"typescript": "^4.7.4"
},
"private": true
}
That doesn't fix the error.
tsconfig.json
I opened tsconfig.json
and changed "module": "CommonJS",
to "module": "ESNext",
and I changed "target": "es2017"
to "target": "ESNext"
. This question explains what ESNext
is.
The emulator continued to throw the error. Here's my tsconfig.json
file:
{
"compilerOptions": {
"module": "ESNext",
"noImplicitReturns": true,
"noUnusedLocals": true,
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "ESNext"
},
"compileOnSave": true,
"include": [
"src"
]
}
Recommended tsconfig.json
The TypeScript Handbook recommends this tsconfig.json
:
{
"compilerOptions": {
"target": "ES2015",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Recommended"
}
That throws the same error.
Misconfiguration
There's clearly more than one misconfiguration in the default package.json
and tsconfig.json
. If someone can tell me how to configure these correctly I'll do a pull request to firebase-tools
.
I'm using Node 18.1.0. Firebase recommends Node 16.
Firebase Cloud Functions documentation on handling dependencies says to use require
with JavaScript cloud functions and import
with TypeScript cloud functions.
Use the Node.js
require()
function to load any Node.js module you have installed. You can also use therequire()
function to import local files you deploy alongside your function.If you are writing functions in TypeScript, use the
import
statement in the same way to load any Node.js module you have installed.
That doesn't make sense if the Node.js documentation is correct. require()
can't load any Node module, it only handles CommonJS
modules. The paragraph about TypeScript seems to say that you can't use import
with JavaScript Cloud Functions.
ts-node
Would ts-node help?
Upvotes: 1
Views: 1162