Creating an npm package from CRA build - node.js

We are trying to create a microfront ends app, we would like to take each micro app (created with CRA) run
npm run build
over this app, take the /build folder created, and make a npm package out of it and publish it to our npm repo.
We don't want to ejact the app projects to be able to edit the webpack.config.
We prefer to take the /build and pass it over webpack again (even in a different project) in order to get an output that can be used as npm package and can be published and imported correctly to a new project.
Im trying doing it by taking the /build folder and running it over webpack with this configuration:
const path = require("path");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const glob = require("glob");
module.exports = {
entry: {
"bundle.js": glob
.sync("build/static/?(js|css)/main.*.?(js|css)")
.map(f => path.resolve(__dirname, f))
},
output: {
filename: "build/static/js/bundle.min.js",
libraryTarget: "commonjs2"
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
plugins: [new UglifyJsPlugin()],
resolve: {
alias: {
src: path.join(__dirname, "./src")
}
},
externals: {
react: "commonjs react"
}
};
The result is a single js. after publishing it to npm, im trying to import it and getting many errors like:
I think there is something missing in my webpack.config or maybe there is a different way to take the all /build folder and combine it to something that can be published as npm package and imported correctly?
Any help will be very much appreciated!
Thank you!

Related

Bundling files in "src" folder into "public" folder using webpack

How to Bundle all the files inside “src” folder and replace the existing bundled files inside “Public” folder in any Node.js web app?
My project structure is similar to this: https://github.com/googlearchive/friendlypix-web
For javascript files, you should have at least one entry-point for your application in order to bundle it.
Example of webpack.config.json with one entrypoint src/index.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'public'),
},
};
More information about bundling one or many entry-points : https://webpack.js.org/concepts/entry-points/
Furthermore, if you have imported static assets (images, css, saas, fonts...) in your javascript files, you need to add some configuration and eventually install webpack loaders to bundle them.
For example, for CSS assets :
Install webpack loaders via npm install --save-dev style-loader css-loader
Update webpack configuration
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'public'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};
More information about asset management : https://webpack.js.org/guides/asset-management/
Finally, you may need to bundle static files or directories that are not imported in your javascript files and you want to copy them as they are.
In this case, you could use the copy-webpack-plugin :
Install plugin via npm install copy-webpack-plugin --save-dev
Update webpack configuration
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
...
plugins: [
new CopyPlugin({
patterns: [
{ from: "source", to: "dest" },
{ from: "other", to: "public" },
],
}),
],
...
}
More info about copy-webpack-plugin : https://webpack.js.org/plugins/copy-webpack-plugin/
I really recommend you to take a look at the official webpack documentation which covers topics including creating production bundles, code splitting, caching and other tips and hints.

How to include migration schema files in webpack build

Issue
When building the application with Webpack into a single bundle, the library migrate-mongo is unable to find the migration schema module eg migrations/[some-number]-[some-name]-migration.js. This is probably due to the dynamic import of the migration module by the library itself and because webpack replaces how require loads modules.
How can I configure Webpack that it registers the migration js files in it's build, so when the library calls the require it will be able to retrieve the modules.
Library tries requires the module in the following absolute path strategy:
// Roughly
path.join(pwd(), migrationsDir, fileName)
Error received
Could not migrate up [file-name]-migration.js. Cannot find module [absolute-path]/migrations/[file-name]-migration.js
Even though the file exists, tried multiple relative locations just to be sure.
State
I'm using Webpack for building Nest.js application and migrate-mongo for migrations in an NX project. Migration files are located in migrations/ directory and migration up is triggered by application on startup. Migration works normally when app is started in development mode.
migrate.ts
// This function is executed on app bootstrap
export const migrate = async (): Promise<void> => {
Logger.log('[Migration]: Started');
config.set(createConfig());
const {db, client} = await database.connect();
const migrated = await up(db, client);
migrated.forEach((fileName) => {
Logger.log(`[Migration]: migrated ${fileName}`);
});
Logger.log('[Migration]: End');
};
webpack.config.js
module.exports = {
mode: 'development',
target: 'node',
entry: './src/main.ts',
module: {
rules: [
{
test: /\.ts?$/,
use: 'ts-loader',
exclude: [/node_modules/, /other-app/],
}
],
},
resolve: {
extensions: ['.ts', '.js'],
plugins: [
new TsconfigPathsPlugin({
configFile: path.resolve(__dirname, '../../tsconfig.json'),
}),
],
},
output: {
path: path.resolve(__dirname, '../../dist/apps/my-backend'),
filename: 'src/main.js',
},
externals: [
nodeExternals(),
],
plugins: [/* ... */],
}
Tried solutions
Tried using CopyPlugin to copy the migrations, but as found out all modules are placed within the built bundle.
Tried updating externals to include the directory with migrations
Tried requiring or importing the file and directory in app to register the modules

How do I integrate NewRelic into a Node Typescript Express server bundled with Webpack?

Frankly, I've tried it all. I'm not a total whiz with Webpack, however I seem to be getting along pretty well over the years with configuring new projects.
What I cannot seem to do now is set up the NewRelic service into an existing Node/Typescript/Express/Webpack application.
As it stands, my app gets nicely bundled to a single file in my /dist folder and runs quick and nimble. Seems like this 'node agent' put out by New Relic doesn't play well with Typescript imports.
Webpack Config
const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const NodemonPlugin = require ('nodemon-webpack-plugin');
module.exports = (env = {}) => {
const config = {
entry: ['./src/app.ts'],
mode: env.development ? 'development' : 'production',
target: 'node',
devtool: env.development ? 'inline-source-map' : false,
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', 'src', 'package.json'],
},
module: {
rules: [
{
test: /\.ts$/,
use: ['ts-loader', 'eslint-loader'],
// exclude: /node_modules/,
},
],
},
plugins: [],
externals: [ 'newrelic', nodeExternals() ]
};
if (env.nodemon) {
config.watch = true;
config.plugins.push(new NodemonPlugin())
}
return config;
};
there exists a standard /project_root/.newrelic file
CircleCi picks up this project up and runs "build:ci" script from package.json ==> "webpack"
output is /dist/main.js
references
https://docs.newrelic.com/docs/agents/nodejs-agent/installation-configuration/install-nodejs-agent
https://docs.newrelic.com/docs/agents/nodejs-agent/installation-configuration/nodejs-agent-configuration
https://discuss.newrelic.com/t/node-agent-fails-with-webpack/24874
Your first line of the starting point of the app should be
import newrelic from 'newrelic';
Of course, run npm install newrelic --save first
Then, create a newrelic.js file on the root of the repo (outside of src).
Then you put in the details like:
'use strict'
exports.config = {
app_name: ['appName'],
license_key: '1234567890',
allow_all_headers: true,
attributes: {
exclude: [
'request.headers.cookie',
'request.headers.authorization',
'request.headers.proxyAuthorization',
'request.headers.setCookie*',
'request.headers.x*',
'response.headers.cookie',
'response.headers.authorization',
'response.headers.proxyAuthorization',
'response.headers.setCookie*',
'response.headers.x*'
]
}
}

How do I bundle bcrypt in a yarn workspace monorepo using webpack and serverless-framework? [duplicate]

I'm using yarn workspaces where the root directory has a package directory with all my repos. Each repo has its own node_modules directory containing its dependencies. The root node_modules directory contains all the dev dependencies for the whole project as well as all other dev related things such as webpack.config files. Webpack uses hot module reload for the express server package.
The problem I have is, how to configure webpack externals to exclude all node_modules directories through the whole project, not just in the root?
webpack-node-externals doesn't seem to work given this scenario.
Error message:
WARNING in ./packages/servers/express/node_modules/colors/lib/colors.js
127:29-43 Critical dependency: the request of a dependency is an expression
WARNING in ./packages/servers/express/node_modules/express/lib/view.js
79:29-41 Critical dependency: the request of a dependency is an expression
Webpack config:
const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const StartServerPlugin = require('start-server-webpack-plugin');
module.exports = {
entry: [
'babel-polyfill',
'webpack/hot/poll?1000',
path.join(__dirname, '../packages/servers/express/server/index.js')
],
watch: true,
target: 'node',
externals: [
nodeExternals({
whitelist: ['webpack/hot/poll?1000']
})
],
resolve: {
alias: {
handlebars: 'handlebars/dist/handlebars.js'
}
},
module: {
rules: [
{
test: /\.js?$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
plugins: [
new StartServerPlugin('server.js'),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
'process.env': { BUILD_TARGET: JSON.stringify('server') }
})
],
output: {
path: path.join(__dirname, '../packages/servers/express/.build'),
filename: 'server.js'
}
};
If using yarn workspaces with webpack-node-externals a better solution than setting modulesFromFile: true is to use the following externals setting in your webpack config:
externals: [
nodeExternals(),
nodeExternals({
modulesDir: path.resolve(__dirname, 'path/to/root/node_modules'),
}),
],
Essentially using two instances of nodeExternals. 1 for the package node_modules and one for the root node_modules.
Thanks to #blackxored I was able to fix it on my project.
In your webpack config file do the following:
import nodeExternals from 'webpack-node-externals'
Then add
externals: [
nodeExternals({
modulesFromFile: true,
}),
],
Yarn workspaces hoist compatible modules to the root node_modules directory leaving any incompatible (different semver, etc.) modules with the dependent workspace's node_modules directory. If a package is requested without using a relative path it is either native, from node_module's, or possibly a symlinked package from one of your workspaces. You probably want all of those packages to be external.
how to configure webpack externals to exclude all node_modules directories through the whole project, not just in the root?
I would try using a function with webpack's external option. You are passed the context of the require, the name of the module requested, and a callback to indicate whether this particular import (require) should be considered external.
externals: [
(ctx, req, cb) => {
if (!/node_modules/.test(ctx) && req[0] !== '.') {
// Assumes you have defined an "entries" variable
let notAnEntry = (path) => {
return Object.keys(entries).every((entry) => {
return entries[entry] !== path
});
};
if (notAnEntry(require.resolve(req))) {
// This module is external in a commonjs context
return cb(null, `commonjs ${req}`);
}
}
cb();
}
]

How to use webpack with a monorepo (yarnpkg workspaces)

I'm using yarn workspaces where the root directory has a package directory with all my repos. Each repo has its own node_modules directory containing its dependencies. The root node_modules directory contains all the dev dependencies for the whole project as well as all other dev related things such as webpack.config files. Webpack uses hot module reload for the express server package.
The problem I have is, how to configure webpack externals to exclude all node_modules directories through the whole project, not just in the root?
webpack-node-externals doesn't seem to work given this scenario.
Error message:
WARNING in ./packages/servers/express/node_modules/colors/lib/colors.js
127:29-43 Critical dependency: the request of a dependency is an expression
WARNING in ./packages/servers/express/node_modules/express/lib/view.js
79:29-41 Critical dependency: the request of a dependency is an expression
Webpack config:
const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const StartServerPlugin = require('start-server-webpack-plugin');
module.exports = {
entry: [
'babel-polyfill',
'webpack/hot/poll?1000',
path.join(__dirname, '../packages/servers/express/server/index.js')
],
watch: true,
target: 'node',
externals: [
nodeExternals({
whitelist: ['webpack/hot/poll?1000']
})
],
resolve: {
alias: {
handlebars: 'handlebars/dist/handlebars.js'
}
},
module: {
rules: [
{
test: /\.js?$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
plugins: [
new StartServerPlugin('server.js'),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
'process.env': { BUILD_TARGET: JSON.stringify('server') }
})
],
output: {
path: path.join(__dirname, '../packages/servers/express/.build'),
filename: 'server.js'
}
};
If using yarn workspaces with webpack-node-externals a better solution than setting modulesFromFile: true is to use the following externals setting in your webpack config:
externals: [
nodeExternals(),
nodeExternals({
modulesDir: path.resolve(__dirname, 'path/to/root/node_modules'),
}),
],
Essentially using two instances of nodeExternals. 1 for the package node_modules and one for the root node_modules.
Thanks to #blackxored I was able to fix it on my project.
In your webpack config file do the following:
import nodeExternals from 'webpack-node-externals'
Then add
externals: [
nodeExternals({
modulesFromFile: true,
}),
],
Yarn workspaces hoist compatible modules to the root node_modules directory leaving any incompatible (different semver, etc.) modules with the dependent workspace's node_modules directory. If a package is requested without using a relative path it is either native, from node_module's, or possibly a symlinked package from one of your workspaces. You probably want all of those packages to be external.
how to configure webpack externals to exclude all node_modules directories through the whole project, not just in the root?
I would try using a function with webpack's external option. You are passed the context of the require, the name of the module requested, and a callback to indicate whether this particular import (require) should be considered external.
externals: [
(ctx, req, cb) => {
if (!/node_modules/.test(ctx) && req[0] !== '.') {
// Assumes you have defined an "entries" variable
let notAnEntry = (path) => {
return Object.keys(entries).every((entry) => {
return entries[entry] !== path
});
};
if (notAnEntry(require.resolve(req))) {
// This module is external in a commonjs context
return cb(null, `commonjs ${req}`);
}
}
cb();
}
]

Resources