Andrew Redican
Andrew Redican

Reputation: 21

Transpiled ES5 not recognized by Firebase Cloud Functions: firebase is undefined

A bit of an introduction, currently (at the time I wrote this) Firebase functions does not support features like async and await from a more recent version of node. I am trying to overcome this by transpiling my code to the node version they currently support.

I am using webpack 3 to bundle my react.js web application. I have set up two separate configurations -one for development and the other production. I use a third confirguration to execute as a second task on production pipiline to take cloudfunctions.js and spit it out to the deployment dir functions/index.js using babel to transpile the code,

Instead of describing the transpile requirements in a .babelrc, I am adding those details using webpack to the options configuration object in inside the module in rules.

I've been successful transpiling the code, yet Firebase keeps complaining about 'firebase' not being defined. I've narrowed down the trigger of this error to the very first line of code which is an import.

Any suggestion that can point me to the right direction is truly appreciated.

import functions from ''firebase-functions';

As a matter of fact, I've tried Commonjs (using require) and made other attempts at ES6 import/exports.

Package.json:

{
  "scripts": {
  "start":"node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack/dev.config.js",
"build": "cross-env NODE_ENV=production node ./node_modules/webpack/bin/webpack.js --config webpack/prod.config.js"
  },
  "dependencies": {
    "@babel/polyfill": "^7.0.0-beta.40",
    "firebase-admin": "^5.5.1",
    "firebase-functions": "^0.7.3",
  },
  "devDependencies": {
    "@babel/cli": "^7.0.0-beta.40",
    "@babel/core": "^7.0.0-beta.40",
    "@babel/plugin-proposal-object-rest-spread": "^7.0.0-beta.40",
    "@babel/preset-env": "^7.0.0-beta.40",
    "babel-loader": "^8.0.0-beta.0",
    "babel-preset-react": "^6.24.1",
    "cross-env": "^5.1.3",
    "generate-json-webpack-plugin": "^0.2.2",
    "uglifyjs-webpack-plugin": "^1.1.8",
    "webpack": "^3.10.0",
    "webpack-merge": "^4.1.1"
  }
}

functions.config.js (webpack)

const
    path               = require('path'),
    pkg                = require('../package'),
    GenerateJsonPlugin = require('generate-json-webpack-plugin'),
    UglifyJSPlugin     = require('uglifyjs-webpack-plugin'),
    webpack            = require('webpack');

const externals = [
    'firebase-admin',
    'firebase-functions'
]

const genPackage = () => ({
    name         : 'functions',
    private      : true,
    main         : 'index.js',
    license      : 'MIT',
    dependencies : externals.reduce( (acc, name) => Object.assign({}, acc, { [name]: pkg.dependencies[name] || pkg.devDependencies[name] }), {} )
})

module.exports = {
    entry  : [
        '@babel/polyfill',
        path.join(__dirname, '../cloudfunctions.js')
    ],
    output : {
        path     : path.join(__dirname, '../functions/'),
        filename : 'index.js'
},
    module : {
        rules: [
            {
                test    : /\.js$/,
                loader  : 'babel-loader',
                options : 
                    {
                        presets : [
                            [ 
                                '@babel/env',
                                { 
                                    option : { 
                                        targets : { 
                                            node : '6.11.5'
                                        }
                                    }
                                }
                            ]                 
                        ],
                        plugins: [
                            '@babel/plugin-proposal-object-rest-spread'
                        ]   
                    }
                ,
                exclude : /node_modules/
            }
        ]
    },
    externals : externals.reduce( (acc, name) => Object.assign({}, acc, { [name]: true }), {} ),
    plugins   : [
        new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) }),
        new UglifyJSPlugin(),
        new GenerateJsonPlugin('package.json', genPackage())
    ]
}

Environtment

Expected Behaviour

  1. Transpile succesfully.
  2. Deploy to firebase successfully

Actual Behaviour

Transpiles succesfully. Continue receiving this log with the same error after hitting the firebase deploy --only functions command:

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
+  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...

Error: Error occurred while parsing your function triggers.

ReferenceError: firebase is not defined
at Object.module.exports (C:\Users\Andrew Redican\Compass\functions\index.js:9040:18)
at __webpack_require__ (C:\Users\Andrew Redican\Compass\functions\index.js:20:30)
at Object.module.exports (C:\Users\Andrew Redican\Compass\functions\index.js:8967:17)
at __webpack_require__ (C:\Users\Andrew Redican\Compass\functions\index.js:20:30)
at Object.<anonymous> (C:\Users\Andrew Redican\Compass\functions\index.js:3687:18)
at __webpack_require__ (C:\Users\Andrew Redican\Compass\functions\index.js:20:30)
at C:\Users\Andrew Redican\Compass\functions\index.js:63:18
at Object.<anonymous> (C:\Users\Andrew Redican\Compass\functions\index.js:66:10)
at Module._compile (module.js:635:30)
at Object.Module._extensions..js (module.js:646:10)

cloundFunctions.js [input]

let functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

functions/index.js [output]

  1. Depending on whether I include Uglify plugin or not the output will be minified or normal.
  2. Webpack also takes in @babel/polyfill so help functions are added to this file.

I have not included outfile since unmifinify version is 9049 lines of code long, while minified is not legibile.

I've Tried

  1. Used Babel 6 and then Babel 7 still unfruitful.
  2. Environment Configuartion https://firebase.google.com/docs/functions/config-en
  3. I've tried providing the access object directly hardcoded.

I am obviosly missing something, but I've went over this article/repo several times now.

Other Notes

I am trying to get away from typescript and dealing promises' callback hell as far as I can. I am also trying not to rely directly on npm to run command directly but rather take advantage of webpack.

Upvotes: 0

Views: 852

Answers (1)

Andrew Redican
Andrew Redican

Reputation: 21

I was able have webpack use babel to transpile code to functions/index.js. I figured out the problem.

When using webpack, make sure you specify output.libraryTarget to 'umd'.

Below is the webpack confg file:

const
    path               = require('path'),
    pkg                = require('../package'),
    GenerateJsonPlugin = require('generate-json-webpack-plugin');

const
    externals = [
        'firebase-admin',
        'firebase-functions'
    ], 
    genPackage = () => ({
        name    : 'functions',
        private : true,
        dependencies: externals.reduce(
            (acc, name) =>
                Object.assign({}, acc, {
                [name]:
                    pkg.dependencies[name] ||
                    pkg.devDependencies[name]
                }),
            {}
        )
    });

module.exports = {
    entry : [
      'babel-polyfill',
      './cloudfunctions.js'
    ],
    output: {
        path          : path.resolve(__dirname,'../functions'),
        filename      : 'index.js',
        libraryTarget : 'umd'
    },
    module : {
        rules : [
            {
                exclude : /node_modules/,
                loader  : 'babel-loader',
                query   : { 
                    presets : [ 'babel-preset-env' ] 
                }
            }
        ]
    },
    resolve: {
        extensions: ['.js']
    },
    node: {
        fs  : 'empty',
        net : 'empty',
        tls : 'empty'
    },
    externals: externals.reduce(
        (acc, name) => Object.assign({}, acc, { [name]: true }),{}
    ),
    plugins: [
        new GenerateJsonPlugin('package.json', genPackage())
    ]
};

Cheers!

Upvotes: 1

Related Questions