Related
Newbie to Storybook here.
I'm trying to integrate Storybook into my Gatsby front end. However, when trying to preview the test components in Storybook Canvas I get the following error:
react is not defined
ReferenceError: react is not defined
at react-dom/client (http://localhost:6006/main.iframe.bundle.js:1970:18)
at webpack_require (http://localhost:6006/runtime~main.iframe.bundle.js:28:33)
at fn (http://localhost:6006/runtime~main.iframe.bundle.js:339:21)
at webpack_require.t (http://localhost:6006/runtime~main.iframe.bundle.js:106:38)
I'm able to see the component preview in Storybook Docs but not in Storybook Canvas.
Link to repository:
https://github.com/akarpov91/gatsby-tutorial
Try adding the following snippet in your main.js:
module.exports = {
// ...
babel: async (options) => ({
...options,
presets: [
...options.presets,
[
'#babel/preset-react', {
runtime: 'automatic',
},
'preset-react-jsx-transform'
],
],
}),
};
Apparently, #storybook/react adds #babel/preset-react without runtime: 'automatic' property
I have had the same problem, try copying this into your .storybook/main.js config. Hope this works for you too.
module.exports = {
// You will want to change this to wherever your Stories will live
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.#(js|jsx|ts|tsx)"],
addons: ["#storybook/addon-links", "#storybook/addon-essentials"],
framework: "#storybook/react",
core: {
builder: "webpack5",
},
webpackFinal: async config => {
// Transpile Gatsby module because Gatsby includes un-transpiled ES6 code.
config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/]
// Use installed babel-loader which is v8.0-beta (which is meant to work with #babel/core#7)
config.module.rules[0].use[0].loader = require.resolve("babel-loader")
// Use #babel/preset-react for JSX and env (instead of staged presets)
config.module.rules[0].use[0].options.presets = [
require.resolve("#babel/preset-react"),
require.resolve("#babel/preset-env"),
]
config.module.rules[0].use[0].options.plugins = [
// Use #babel/plugin-proposal-class-properties for class arrow functions
require.resolve("#babel/plugin-proposal-class-properties"),
// Use babel-plugin-remove-graphql-queries to remove graphql queries from components when rendering in Storybook
// While still rendering content from useStaticQuery in development mode
[
require.resolve("babel-plugin-remove-graphql-queries"),
{
stage: config.mode === `development` ? "develop-html" : "build-html",
staticQueryDir: "page-data/sq/d",
},
],
]
return config
},
}
After nest build or nest build --webpack dist folder does not contain all required modules and I got Error: Cannot find module '#nestjs/core' when trying to run node main.js.
I could not find any clear instructions on https://docs.nestjs.com/ on how to correctly build app for production, so maybe I missed something?
Out of the box, nest cli does not support including the node_modules dependencies into the dist bundle.
However, there are some community examples of custom webpack configs that include the dependencies in the bundle, e.g. bundled-nest. As described in this issue, it is necessary to include the webpack.IgnorePlugin to whitelist unused dynamic libraries.
bundle-nest has been archived/discontinued:
We've concluded that it is not recommended to bundle NestJS, or actually, NodeJS web servers in general. This is archived for historical reference during the period of time when the community was attempting to tree-shake, and bundle NestJS apps. Refer to #kamilmysliwiec comment for details:
In many real-world scenarios (depending on what libraries are being used), you should not bundle Node.js applications (not only NestJS applications) with all dependencies (external packages located in the node_modules folder). Although this may make your docker images smaller (due to tree-shaking), somewhat reduce the memory consumption, slightly increase the bootstrap time (which is particularly useful in the serverless environments), it won't work in combination with many popular libraries commonly used in the ecosystem. For instance, if you try to build NestJS (or just express) application with MongoDB, you will see the following error in your console:
Error: Cannot find module './drivers/node-mongodb-native/connection' at webpackEmptyContext
Why? Because mongoose depends on mongodb which depends on kerberos (C++) and node-gyp.
Well, about mongo, you can make some exceptions (leave some modules in node_modules), can you? It's not like it's all or nothing. But still, I'm not sure you want to follow this path. I've just succeeded with bundling a nestjs application. It was a proof of concept, I'm not sure if it'll go into production. And it was hard, I might have broken something in the process, but at first glance it works. The most complex part was adminjs. It has rollup and babel as dependencies. And in the app code they unconditionally call watch for some reason (UDP noop in production). Anyways, if you'd like to follow this path you should be ready to debug/inspect your packages' code. And you might need to add workarounds as new packages are added to the project. But it all depends on your dependencies, it may be easier than in my case. For a freshly created nestjs + mysql app it was relatively simple.
The config I ended up with (it overrides the nestjs defaults):
webpack.config.js (webpack-5.58.2, #nestjs/cli-8.1.4):
const path = require('path');
const MakeOptionalPlugin = require('./make-optional-plugin');
module.exports = (defaultOptions, webpack) => {
return {
externals: {}, // make it not exclude `node_modules`
// https://github.com/nestjs/nest-cli/blob/v7.0.1/lib/compiler/defaults/webpack-defaults.ts#L24
resolve: {
...defaultOptions.resolve,
extensions: [...defaultOptions.resolve.extensions, '.json'], // some packages require json files
// https://unpkg.com/browse/babel-plugin-polyfill-corejs3#0.4.0/core-js-compat/data.js
// https://unpkg.com/browse/core-js-compat#3.19.1/data.json
alias: {
// an issue with rollup plugins
// https://github.com/webpack/enhanced-resolve/issues/319
'#rollup/plugin-json': '/app/node_modules/#rollup/plugin-json/dist/index.js',
'#rollup/plugin-replace': '/app/node_modules/#rollup/plugin-replace/dist/rollup-plugin-replace.cjs.js',
'#rollup/plugin-commonjs': '/app/node_modules/#rollup/plugin-commonjs/dist/index.js',
},
},
module: {
...defaultOptions.module,
rules: [
...defaultOptions.module.rules,
// a context dependency
// https://github.com/RobinBuschmann/sequelize-typescript/blob/v2.1.1/src/sequelize/sequelize/sequelize-service.ts#L51
{test: path.resolve('node_modules/sequelize-typescript/dist/sequelize/sequelize/sequelize-service.js'),
use: [
{loader: path.resolve('rewrite-require-loader.js'),
options: {
search: 'fullPath',
context: {
directory: path.resolve('src'),
useSubdirectories: true,
regExp: '/\\.entity\\.ts$/',
transform: ".replace('/app/src', '.').replace(/$/, '.ts')",
},
}},
]},
// adminjs resolves some files using stack (relative to the requiring module)
// and actually it needs them in the filesystem at runtime
// so you need to leave node_modules/#adminjs/upload
// I failed to find a workaround
// it bundles them to `$prj_root/.adminjs` using `rollup`, probably on production too
// https://github.com/SoftwareBrothers/adminjs-upload/blob/v2.0.1/src/features/upload-file/upload-file.feature.ts#L92-L100
{test: path.resolve('node_modules/#adminjs/upload/build/features/upload-file/upload-file.feature.js'),
use: [
{loader: path.resolve('rewrite-code-loader.js'),
options: {
replacements: [
{search: /adminjs_1\.default\.bundle\('\.\.\/\.\.\/\.\.\/src\/features\/upload-file\/components\/edit'\)/,
replace: "adminjs_1.default.bundle('/app/node_modules/#adminjs/upload/src/features/upload-file/components/edit')"},
{search: /adminjs_1\.default\.bundle\('\.\.\/\.\.\/\.\.\/src\/features\/upload-file\/components\/list'\)/,
replace: "adminjs_1.default.bundle('/app/node_modules/#adminjs/upload/src/features/upload-file/components/list')"},
{search: /adminjs_1\.default\.bundle\('\.\.\/\.\.\/\.\.\/src\/features\/upload-file\/components\/show'\)/,
replace: "adminjs_1.default.bundle('/app/node_modules/#adminjs/upload/src/features/upload-file/components/show')"},
],
}},
]},
// not sure what babel does here
// I made it return standardizedName
// https://github.com/babel/babel/blob/v7.16.4/packages/babel-core/src/config/files/plugins.ts#L100
{test: path.resolve('node_modules/#babel/core/lib/config/files/plugins.js'),
use: [
{loader: path.resolve('rewrite-code-loader.js'),
options: {
replacements: [
{search: /const standardizedName = [^;]+;/,
replace: match => `${match} return standardizedName;`},
],
}},
]},
// a context dependency
// https://github.com/babel/babel/blob/v7.16.4/packages/babel-core/src/config/files/module-types.ts#L51
{test: path.resolve('node_modules/#babel/core/lib/config/files/module-types.js'),
use: [
{loader: path.resolve('rewrite-require-loader.js'),
options: {
search: 'filepath',
context: {
directory: path.resolve('node_modules/#babel'),
useSubdirectories: true,
regExp: '/(preset-env\\/lib\\/index\\.js|preset-react\\/lib\\/index\\.js|preset-typescript\\/lib\\/index\\.js)$/',
transform: ".replace('./node_modules/#babel', '.')",
},
}},
]},
],
},
plugins: [
...defaultOptions.plugins,
// some optional dependencies, like this:
// https://github.com/nestjs/nest/blob/master/packages/core/nest-application.ts#L45-L52
// `webpack` detects optional dependencies when they are in try/catch
// https://github.com/webpack/webpack/blob/main/lib/dependencies/CommonJsImportsParserPlugin.js#L152
new MakeOptionalPlugin([
'#nestjs/websockets/socket-module',
'#nestjs/microservices/microservices-module',
'class-transformer/storage',
'fastify-swagger',
'pg-native',
]),
],
// to have have module names in the bundle, not some numbers
// although numbers are sometimes useful
// not really needed
optimization: {
moduleIds: 'named',
}
};
};
make-optional-plugin.js:
class MakeOptionalPlugin {
constructor(deps) {
this.deps = deps;
}
apply(compiler) {
compiler.hooks.compilation.tap('HelloCompilationPlugin', compilation => {
compilation.hooks.succeedModule.tap(
'MakeOptionalPlugin', (module) => {
module.dependencies.forEach(d => {
this.deps.forEach(d2 => {
if (d.request == d2)
d.optional = true;
});
});
}
);
});
}
}
module.exports = MakeOptionalPlugin;
rewrite-require-loader.js:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
function processFile(source, search, replace) {
const re = `require\\(${escapeRegExp(search)}\\)`;
return source.replace(
new RegExp(re, 'g'),
`require(${replace})`);
}
function processFileContext(source, search, context) {
const re = `require\\(${escapeRegExp(search)}\\)`;
const _d = JSON.stringify(context.directory);
const _us = JSON.stringify(context.useSubdirectories);
const _re = context.regExp;
const _t = context.transform || '';
const r = source.replace(
new RegExp(re, 'g'),
match => `require.context(${_d}, ${_us}, ${_re})(${search}${_t})`);
return r;
}
module.exports = function(source) {
const options = this.getOptions();
return options.context
? processFileContext(source, options.search, options.context)
: processFile(source, options.search, options.replace);
};
rewrite-code-loader.js:
function processFile(source, search, replace) {
return source.replace(search, replace);
}
module.exports = function(source) {
const options = this.getOptions();
return options.replacements.reduce(
(prv, cur) => {
return prv.replace(cur.search, cur.replace);
},
source);
};
The supposed way to build the app is:
$ nest build --webpack
I didn't bother with source maps, since the target is nodejs.
It's not a config you can just copy-paste, you should figure out what's needed for your project yourself.
One more trick here, but well, you probably won't need it.
UPD adminjs seems to come with prebuilt bundles, so this config may be significantly simpler.
I am trying to use a legacy JavaScript in a npm package to distribute to other projects.
If I import the Javascript file directly into one of the projects with help of webpack's exports-loader I am able to use all of the functions without any issues.
However if I import the npm package into one of the projects I run into issues. I think my webpack setup in the npm package is incorrectly setup.
npm package:
import { BrowserPrint } from "./BrowserPrint.fat";
exports.printEan = function(ean) {
BrowserPrint.getLocalDevices(
function(printers) {
console.log("something happened");
},
undefined,
"printer"
);
};
My webpack config from the npm-package:
const webpack = require("webpack");
module.exports = {
entry: ["./src/index.js"],
output: {
path: __dirname + "/lib",
filename: "main.js",
library: "zpl-browser-print",
libraryTarget: "umd"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: require.resolve("./src/BrowserPrint.fat.js"),
use: "exports-loader?BrowserPrint=BrowserPrint"
}
]
}
};
When I import this module into my project and call printEan I get "Uncaught ReferenceError: finishedFunction is not defined". Which suggests that it finds the printEan function, and BrowserPrint, but somehow the code in the legacy script isn't being handled properly by webpack.
Looking at the code of the legacy script finishedFunction is never defined but then I have no idea why it works when I directly import the script in my other projects.
snippet from the legacy code:
c &&
((finishedFunction = function(e) {
if (((response = e), "" == response)) return void s(null);
response = JSON.parse(response);
var n = new t.Device(response);
s(n);
}),
i(void 0, c, finishedFunction, o),
I'm having trouble visualizing how I can leverage the DllPlugin/DllReferencePlugin with Webpack while also using Grunt for the building. For those without knowledge, the DllPlugin creates a separate bundle that can be shared with other bundles. It also creates a manifest file (important) to help with the linking. Then, the DllReferencePlugin is used by another bundle when building to grab the previous made DllPlugin Bundle. To do this, it requires the manifest file created previously.
In Grunt, this would require the manifest file created before grunt even runs, no? Heres a simplified code example:
webpack.dll.js
// My Dll Bundles, which creates
// - ./bundles/my_dll.js
// - ./bundles/my_dll-manifest.json
module.exports = {
entry: {
my_dll : './dll.js'
},
// where to send final bundle
output: {
path: './bundles',
filename: "[name].js"
},
// CREATES THE MANIFEST
plugins: [
new webpack.DllPlugin({
path: "./bundles/[name]-manifest.json",
name: "[name]_lib"
})
]
};
webpack.app.js
// My Referencing Bundle, which includes
// - ./bundles/app.js
module.exports = {
entry: {
my_app : './app.js'
},
// where to send final bundle
output: {
path: './bundles',
filename: "[name].js"
},
// SETS UP THE REFERENCE TO THE DLL
plugins: [
new webpack.DllReferencePlugin({
context: '.',
// IMPORTANT LINE, AND WHERE EVERYTHING SEEMS TO FAIL
manifest: require('./bundles/my_dll-manifest.json')
})
]
};
If you look in the second section, webpack.app.js, I've commented where everything would seem to fail in grunt. For the DllReferencePlugin to work, it needs the manifest file from the DllPlugin, but in a Grunt workflow, grunt will load both of these configurations on initialization of grunt itself, causing the manifest: require('./bundles/my_dll-manifest.json') line to fail, because the previous grunt step that builds webpack.dll.js has not completed, meaning manifest does not yet exist.
var path = require("path");
var util = require('util')
var webpack = require("webpack");
var MyDllReferencePlugin = function(options){
webpack.DllReferencePlugin.call(this, options);
}
MyDllReferencePlugin.prototype.apply = function(compiler) {
if (typeof this.options.manifest == 'string') {
this.options.manifest = require(this.options.manifest);
}
webpack.DllReferencePlugin.prototype.apply.call(this, compiler);
};
// My Referencing Bundle, which includes
// - ./bundles/app.js
module.exports = {
entry: {
my_app : './app.js'
},
// where to send final bundle
output: {
path: './bundles',
filename: "[name].js"
},
// SETS UP THE REFERENCE TO THE DLL
plugins: [
new MyDllReferencePlugin({
context: '.',
// IMPORTANT LINE, AND WHERE EVERYTHING SEEMS TO FAIL
manifest: path.resolve('./bundles/my_dll-manifest.json')
})
]
};
I have a web app that I compile with webpack. One of the modules that my code uses is named table.js. Until recently, it's just been another module and has been compiled into my bundle.js file with everything else.
Now I need to run table.js in a Web Worker, so I need to pull it and its dependencies into a separate file that can be loaded both standalone and by my other modules.
At first I thought to include table.js in my webpack.config.js's entry.
var config = {
...
entry: {
app: [ './src/main.js', './src/classes/table.js' ],
vendors: [],
},
...
}
That didn't work. Then I thought to separate it out like my vendors bundle.
var config = {
/* for vendors (and other modules) we have a CDN for */
addExternal: function (name, globalVar) {
this.externals[name] = globalVar;
this.entry.vendors.push(name);
},
/* for vendors we don't have a CDN for */
addVendor: function (name, path) {
this.resolve.alias[name] = path;
this.entry.vendors.push(name);
},
addPlugin: function (plugin) {
this.plugins.push(plugin);
},
entry: {
app: [ './src/main.js' ],
vendors: [],
table: [ __dirname + '/src/classes/table.js' ]
},
plugins: [],
externals: { },
output: {
path: __dirname + '/public/dist/',
filename: 'bundle.js',
publicPath: '/dist/',
sourceMapFile: '[file].map'
},
resolve: {
alias: { 'table': './src/classes/table.js' },
extensions: [ '', '.js', '.jsx' ]
},
...
}
/* add vendors and externals */
...
config.addPlugin(new CommonsChunkPlugin('vendors', 'vendors.js'));
config.addPlugin(new CommonsChunkPlugin('table', 'table.js'));
This seems to pull Table and its dependencies into a chunk of bundle.js, 1.bundle.js. Unfortunately, then calling import Table from 'table' causes this error:
ERROR in CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk (table)
I also have a circular dependency between TableStore and Table. TableStore needs to stay in bundle.js because it shouldn't be loaded into the Web Worker. Previously, when I've needed to throw things into a separate chunk, I've done:
if (someThingNeedsRequiring) {
require.ensure([], () => {
require('something');
}
}
With the circular dependency, this doesn't seem to work.
/* table.js */
let _inWebWorker = self instanceof Window,
TableStore = null;
if (!_inWebWorker) {
require.ensure([], function() { TableStore = require('../stores/table-store'); } );
}
/* table-store.js */
import Table from 'table';
Could someone set me straight on the correct way to have my webpack.config.js and how to use my imports in my module files?
(It's been quite a while since I figured this out, and I haven't touched the project in nearly six months, so I may have missed some of the details. Comment if it's not working, and I'll try to figure out what I'm missing.)
webpack.config
It turns out there are two handy-dandy JavaScript packages for doing what I want: worker-loader and workerjs.
npm install --save workerjs worker-loader
I added this in my webpack.config.js:
var config = {
// ...
worker: {
output: {
filename: '[name].worker.js',
chunkFilename: '[name].worker.js'
}
},
// ...
}
require()
In order to specify that I want my class to be run in a WebWorker file, my require looks like:
// ecmaScript 6
import TableWorker from 'worker?name=tableRoller!path/to/table';
// ecmaScript 5
var TableWorker = require('worker?name=tableRoller!path/to/table');
TableWorker is just a variable name I used for table.js's export default class Table {...}. The name=tableRoller specifies the generated outputted [name].worker.js filename. For example, I have another WebWorker named distCalc.worker.js, so my import looks like:
import DistWorker from 'worker?name=distCalc!path/to/distWorker';
Note that in this case, distWorker only ever runs in a WebWorker, while Table is used in both my main.js entry point and my tableRoller.worker.js WebWorker file.
workerjs and worker-loader generate a new entry point file and pull in all of the dependencies of those classes. Tobias Koppers (worker-loader) and Eugene Ware (workerjs) are geniuses.
Detecting WebWorker
My _inWebWorker detection is:
let _inWebWorker = typeof Window === 'undefined';
Change output filename in your webpack.config.js file
output: {
path: __dirname + '/public/dist/',
filename: '[name].js',
publicPath: '/dist/',
sourceMapFile: '[file].map'
},
then Webpack can separate your entries with its name in dist directory.