Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Support loading Firebase Functions packaged as an ES module. (#3485)
25 changes: 23 additions & 2 deletions src/deploy/functions/runtimes/node/triggerParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,28 @@ var EXIT = function () {
process.exit(0);
};

(function () {
/**
* Dynamically load import function to prevent TypeScript from
* transpiling into a require.
*
* See https://github.com/microsoft/TypeScript/issues/43329.
*/
// eslint-disable-next-line @typescript-eslint/no-implied-eval
const dynamicImport = new Function("modulePath", "return import(modulePath)");

async function loadModule(packageDir) {
try {
return require(packageDir);
} catch (e) {
if (e.code === "ERR_REQUIRE_ESM") {
const mod = await dynamicImport(require.resolve(packageDir));
return mod;
}
throw e;
}
}

(async function () {
if (!process.send) {
console.warn("Could not parse function triggers (process.send === undefined).");
process.exit(1);
Expand All @@ -23,7 +44,7 @@ var EXIT = function () {
var mod;
var triggers = [];
try {
mod = require(packageDir);
mod = await loadModule(packageDir);
} catch (e) {
if (e.code === "MODULE_NOT_FOUND") {
process.send(
Expand Down
19 changes: 17 additions & 2 deletions src/emulator/functionsEmulatorRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ import * as _ from "lodash";
let triggers: EmulatedTriggerMap | undefined;
let developerPkgJSON: PackageJSON | undefined;

/**
* Dynamically load import function to prevent TypeScript from
* transpiling into a require.
*
* See https://github.com/microsoft/TypeScript/issues/43329.
*/
// eslint-disable-next-line @typescript-eslint/no-implied-eval
const dynamicImport = new Function("modulePath", "return import(modulePath)");

function isFeatureEnabled(
frb: FunctionsRuntimeBundle,
feature: keyof FunctionsRuntimeFeatures
Expand Down Expand Up @@ -567,6 +576,7 @@ async function initializeFirebaseAdminStubs(frb: FunctionsRuntimeBundle): Promis
// Stub the admin module in the require cache
require.cache[adminResolution.resolution] = {
exports: proxiedAdminModule,
path: path.dirname(adminResolution.resolution),
Copy link
Contributor Author

@taeold taeold Jun 27, 2021

Choose a reason for hiding this comment

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

This needed to be done - otherwise, the dynamic import called failed with errors like this:

TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined
    at new NodeError (node:internal/errors:363:5)
    at validateString (node:internal/validators:119:11)
    at Object.resolve (node:path:1098:7)
    at Function.Module._nodeModulePaths (node:internal/modules/cjs/loader:625:17)
    at cjsPreparseModuleExports (node:internal/modules/esm/translators:258:30)
    at Loader.commonjsStrategy (node:internal/modules/esm/translators:188:35)

@samtstern Think this is safe modification, but I'm 100% unfamiliar with this code path. Let me know if this isn't going to work (I tested things out, and emulator continues to work normally).

Copy link
Contributor

Choose a reason for hiding this comment

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

This seems safe to me.

};

logDebug("firebase-admin has been stubbed.", {
Expand Down Expand Up @@ -776,6 +786,7 @@ async function initializeFunctionsConfigHelper(frb: FunctionsRuntimeBundle): Pro
// Stub the functions module in the require cache
require.cache[functionsResolution.resolution] = {
exports: proxiedFunctionsModule,
path: path.dirname(functionsResolution.resolution),
};

logDebug("firebase-functions has been stubbed.", {
Expand Down Expand Up @@ -1065,8 +1076,12 @@ async function initializeRuntime(
try {
triggerModule = require(frb.cwd);
} catch (err) {
await moduleResolutionDetective(frb, err);
return;
if (err.code !== "ERR_REQUIRE_ESM") {
await moduleResolutionDetective(frb, err);
return;
}
// tslint:disable:no-unsafe-assignment
triggerModule = await dynamicImport(require.resolve(frb.cwd));
}
}
if (extensionTriggers) {
Expand Down