Is it possible to make firebase functions work with webpack and babel?
We need to reuse existing ES6 classes on server side which we can't edit so we need to transpile them to make it work in node.
Spent two days on the related tutorials but I'm hitting the problem where firebase can't seem to see functions declared in index.js after they were wrapped by webpack in a function.
here is a part of the resulting index.js:
(function(module, exports, __webpack_require__) {
var functions = __webpack_require__(/*! firebase-functions */ "firebase-functions");
var admin = __webpack_require__(/*! firebase-admin */ "firebase-admin");
admin.initializeApp();
exports.api = functions.https.onRequest(function (req, res) {
res.send({
msg: "ok"
});
});
})
exports.api = functions.https.onRequest is not deploying in this case.
Is it ever possible to make firebase work with webpack and babel?
I had a similar challenge. I am not completely satisfied with how things are automated on the dev side (I will give more time on that during the next few days), but it is working indeed.
I will try to tell how I did it by changing code and instructions to remove unrelated complexity.
My main goal is not to manually maintain a "functions" folder with its own package.json as a separate project from my main one. Everything that resides in the root source code and some webpack magic should suffice.
1 - My entry point source code in a app.js file inside a /src/main folder. It reads like this:
import * as functions from 'firebase-functions';
import { someProcessedOutputThatDependsOnExternalLibraries } from './my-code';
export const hello = functions.https.onRequest((req, res) => {
functions.logger.info('Hello logs!', { structuredData: true });
const output = someProcessedOutputThatDependsOnExternalLibraries(req)
res.send(output).end();
});
2 - I want to export it to a /dist folder, so I have to set up that folder in the firebase.json file in the project root:
{
"functions": {
"source": "dist"
},
"emulators": {
"functions": {
"port": 5001
},
}
}
3 - The package.json file in the root holds all the information needed for the project, especially the dependencies and useful scripts. Here some of the file content to illustrate.
{
"scripts": {
"env:firebase": "firebase emulators:start",
"build:firebase": "webpack --config webpack.firebase.js",
},
"dependencies": {
"axios": "^0.21.0",
"core-js": "^3.8.1",
"firebase-admin": "^9.4.2",
"firebase-functions": "^3.13.1",
"inquirer": "^7.3.3",
"regenerator-runtime": "^0.13.7",
"rxjs": "^6.6.3",
"uuid": "^8.3.2",
"xstate": "^4.15.3"
},
"devDependencies": {
"generate-json-webpack-plugin": "^2.0.0",
"webpack": "^5.15.0",
"webpack-cli": "^4.3.1",
"webpack-node-externals": "^2.5.2"
}
}
4 - Webpack is used to build a dist folder compatible with firebase specs. The webpack file below is almost entirely copied from this blessed github gist.
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const GenerateJsonPlugin = require('generate-json-webpack-plugin');
const pkg = require('./package.json');
const genFirebaseFunctionsPackage = () => ({
name: 'functions',
private: true,
main: 'index.js',
license: 'MIT',
engines: {
node: '14'
},
dependencies: pkg.dependencies
});
module.exports = {
target: 'node',
mode: 'production',
entry: './src/main/functions.js',
externals: [nodeExternals()],
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'commonjs'
},
node: {
__dirname: false,
__filename: false
},
optimization: {
minimize: false
},
plugins: [new GenerateJsonPlugin('package.json', genFirebaseFunctionsPackage())]
};
5 - So, if I run npm run build:firebase, the dist folder is created with index.js and package.json files. The dist dir is now compatible with firebase CLI and emulator expectations.
But to run it in the local emulator, I still have to go to that folder and type "npm install" (that's the pain point I'm still trying to solve).
It's a good idea to bundle your functions app using webpack for alot of reasons:
access to webpack loaders/plugins
much better developer experience with HMR (that works better than the functions runtime's implementation)
better performance for deployment and runtime (one or a few optimized chunks vs hundreds/thousands of unoptimised/unused files)
Many of the requirements are covered in this answer, but rather than making it all yourself, you might have an easier time using webpack-cloud-functions (of which I'm the author). It abstracts away much of the configuration you need to do that - e.g. libraryTarget, some of the externals, etc, and also provides reliable HMR for development out of the box.
Related
next-transpile-modules works great for Next projects, but how do I transpile modules for a raw SWC build??? I stumped ðŸ˜
Repo: https://github.com/deltaepsilon/script-kitty
I started from a Turborepo base and exported two packages, ui and command-k into a Turborepo Next app named web. Everything worked great once I added ui and command-k to the next.config.js file like so:
const withTM = require('next-transpile-modules')(['command-k', 'ui']);
module.exports = withTM({
reactStrictMode: true,
});
Now I've got a new app named external that's going to be a standalone build of the command-k package. This will get published to npm.
I'm using swc-loader to transpile it with the following config:
const path = require('path');
// See https://github.com/iykekings/react-swc-loader-template
const config = {
mode: 'development',
entry: './index.tsx',
module: {
rules: [
{
test: /\.(ts|tsx)$/,
loader: 'swc-loader',
include: [
path.resolve(__dirname),
path.resolve(__dirname, '../../packages/command-k'),
path.resolve(__dirname, '../../packages/ui'),
],
exclude: /node_modules/,
},
],
},
};
module.exports = config;
I keep getting the following error when building with yarn dev:
ERROR in ../../packages/command-k/index.tsx 2:0-50
Module not found: Error: Can't resolve './command-k' in '/kitty/packages/command-k'
resolve './command-k' in '/kitty/packages/command-k'
using description file: /kitty/packages/command-k/package.json (relative path: .)
// /packages/command-k/index.tsx
import * as React from 'react';
export { default as CommandK } from './command-k';
It looks like swc-loader is somehow unable to import from inside of a Turborepo package. It works fine if I inline the contents of ./command-k into /packages/command-k/index.tsx, but swc-loader refuses to follow the import.
I want to use codegen.macros to load different libs based on web or native output. But my web components need to load css / scss files. I really googled the heck out of me, but I can't find a running example how to adjust webpack.config.js to have a css/scss loader running with Expo Web (react-native-web).
Expo is mandatory.
It always ends up with "cant resolve ....css".
I know that react native need CSS in JS but my approach will load a different component based for web or native (ios / android).
My setup is already working pretty decent but I can't manage to load and inject a third party css file. This is my webpack.config.js file.
const createExpoWebpackConfigAsync = require('#expo/webpack-config')
module.exports = async function (env, argv) {
const config = await createExpoWebpackConfigAsync({
...env,
// Passing true will enable the default Workbox + Expo SW configuration.
},
argv
);
// Customize the config before returning it.
config.module.rules.push({
test: /\.((c|sa|sc)ss)$/i,
use: [{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'postcss-loader',
},
{
loader: 'sass-loader',
},
],
}, );
return config
}
I managed to fix it on my own with a lot trial and error.
I adjusted my webpack.config.js like so.
const createExpoWebpackConfigAsync = require('#expo/webpack-config')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = async function (env, argv) {
const config = await createExpoWebpackConfigAsync({
...env,
// Passing true will enable the default Workbox + Expo SW configuration.
},
argv
);
// Customize the config before returning it.
config.module.rules.push({
test: /\.((sc)ss)$/i,
use: [
// Creates `style` nodes from JS strings
MiniCssExtractPlugin.loader,
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader",
],
}, );
config.plugins = config.plugins.concat(
[
new MiniCssExtractPlugin()
]
);
return config
}
Since Expo uses Webpack 4 under the hood, you need to install these packages:
package.json
"devDependencies": {
"#babel/core": "^7.15.0",
"#expo/webpack-config": "^0.14.0",
"#types/react": "~17.0.18",
"#types/react-native": "~0.63.2",
"#types/react-slick": "^0.23.5",
"css-loader": "^6.2.0",
"jest-expo": "~42.1.0",
"mini-css-extract-plugin": "^1.6.2",
"sass": "^1.37.5",
"sass-loader": "^10.1.1",
"sass-resources-loader": "^2.2.4",
"style-loader": "^3.2.1",
"typescript": "~4.3.5"
},
It is important to use sass-loader 10.1.1 and mini-css-extract-plugin 1.6.2.
And I can finally import .css and .scss files in Expo Web!
I'm getting the following error when deploying to Vercel:
Module not found: Can't resolve 'fs' in '/vercel/2d531da8/node_modules/mysql/lib/protocol/sequences'
I don't use the dependency fs, my package.json file looks like:
{
"name": "single",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"mysql": "^2.18.1",
"next": "^9.4.4",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"serverless-mysql": "^1.5.4",
"sql-template-strings": "^2.2.2"
}
}
I'm pretty new to NextJS, so I'm not really sure what's going on. Any ideas?
Edit:
For context, I'm getting data from mySQL in the app, not sure if that has anything to do with the error
export async function getStaticProps() {
const db = require('./lib/db')
const escape = require('sql-template-strings')
const articles = await db.query(escape`
SELECT *
FROM articles
ORDER BY pid
`)
const var1 = JSON.parse(JSON.stringify({ articles }))
return {
props: {
var1,
},
}
}
Solved it by creating a next.config.js file and adding the following to it:
module.exports = {
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
// Note: we provide webpack above so you should not `require` it
// Perform customizations to webpack config
// Important: return the modified config
// Example using webpack option
//config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//))
config.node = {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
return config
},
webpackDevMiddleware: config => {
// Perform customizations to webpack dev middleware config
// Important: return the modified config
return config
},
}
I had a similar problem whilst following the next.js tutorial. To resolve this I had to also create a next.config.js file as the above answer. However I have only added the following next configurations
module.exports = {
pageExtensions: ['page.tsx', 'page.ts', 'page.jsx', 'page.js']
}
Once I had added the above next.config.js file with the contents above I was able to successfully build the solution.
source: https://newspatrak.com/javascript/cant-build-react-next-project-found-page-without-a-react-component-as-default-export-context-api-file/
Next.js tutorial link: https://nextjs.org/learn/basics/deploying-nextjs-app/deploy
I was building a WhatsApp clone app (https://github.com/adrielwerlich/curso-hcode-whatsapp-clone)
running the build command
run the firebase deploy --only hosting command
But this screen is what I´m getting
And the Firebase dashboard is displaying
Update
I was able to make some advances.
I added to
webpack.config.js
new HtmlWebpackPlugin({
template: './index.html',
})
and now there is proper html content...
but the app.bundle.js is giving a error message when I try to use firebase serve
After discovering that the bundle was only creating the js files, and was necessary to also configure the HtmlWebpackPlugin to create the HTML file the problem was to make the js files visible to the html files.
So the final solution to make the bundle run inside local firebase serve command and remote firebase hosting service...
The whole issue was about the webpack bundle config
webpack.config.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry:{
app:'./src/app.js',
'pdf.worker':'pdfjs-dist/build/pdf.worker.entry.js'
},
output:{
filename:'[name].bundle.js',
path: path.join(__dirname, '/dist'),
publicPath:'/' // the issue was here. Instead of /dist only / makes the js bundle files visible inside the html index file
},
module: {
loaders: [
{
test: /\.css/,
loaders: ['style-loader', 'css-loader']
// include: __dirname + '/'
},
{
test: /.(gif|png|jpe?g|webp|svg)$/i,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
webp: {
quality: 80
}
}
}
]
}
],
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
})
]
}
and also had to configure de css-style loader and the image webpack loader in order to run properly inside firebase localhost server and firebase remote hosting service
Suffering with the small details of bundle builder configuration... many hours of anxiety...
I have a couple of issues with a NextJS project that I believe might be solvable with a well built webpack configuration (However I am fairly new to webpack).
I am using NextJS 8.0.3 with #zeit/next-css 1.0.2-canary in my local NextJS project, and I have a couple of npm installed dependencies that use css internally (styled reusable components).
The issue that I'm having is that I can't build or export my application because I get a CssSyntax error, and if I use next-plugin-transpile-modules it will build the installed node_modules dependencies but it won't load the css, because it does not build it.
This is the next.config.js file content, if you find the webpack part you will notice that it just uses the basic css-loader.
module.exports = withBundleAnalyzer(withCss({
poweredByHeader: false,
cssModules: false,
// assetPrefix: APP_PREFIX, // This actually sets the path for getting static files somewhere else?*
publicRuntimeConfig: { // Will be available on both server and client
// pathPrefix: APP_PREFIX,
cache: {
static: {
cdn: 3600,
browser: 3600
},
dynamic: {
cdn: 600,
browser: 0
}
}
},
webpack: (config, { defaultLoaders }) => {
// Fixes npm packages that depend on `fs` module
config.module.rules.push({
test: /\.css$/,
use: [
'css-loader',
'style-loader',
]
});
config.node = { fs: 'empty' }
// console.log(config)
return config
},
// bundle analyzer
analyzeServer: ["server", "both"].includes(process.env.BUNDLE_ANALYZE),
analyzeBrowser: ["browser", "both"].includes(process.env.BUNDLE_ANALYZE),
bundleAnalyzerConfig: {
server: {
analyzerMode: 'static',
reportFilename: '../bundles/server.html'
},
browser: {
analyzerMode: 'static',
reportFilename: '../bundles/client.html'
}
}
}))
My most immediate question is, what webpack loader or what pipeline configuration can I use so that webpack actually builds the css that is imported from the jsx dependency, and then build the component properly so that it's internal css and of course it's functionality are fully built.