Why is `window` undefined when running my Webpack plug-in after upgrading to Webpack 4? - node.js

I've written a plug-in for Webpack that takes my generated React component, renders it in Node, and then inserts it into the generated HTML document.
This used to work fine in Webpack 3. To upgrade it to Webpack 4, I replaced
compiler.plugin("after-emit", compilation => {
with
compiler.hooks.afterEmit.tap('prerender-plugin', (compilation) => {
which, as far as I can see, should be sufficient.
In the plug-in, I ask Webpack for the path to my generated bundle and then require that, roughly as follows:
const assetHash = Object.keys(compilation.assets).filter(asset => /app(.*).js/.test(asset))[0];
const appFilePath = compilation.assets[assetHash].existsAt;
// The `.default` is needed because it's an ES2015 module
const App = require(appFilePath).default;
However, I then all of a sudden hit errors like the following as a result of the require:
ReferenceError: window is not defined
at Object.<anonymous> (/home/vincent/Workspace/Flockademic/stacks/frontend/dist/app.da5512cf55a3c4446086.js:1:286)
...
The offending code seems to be some standard Webpack top matter:
!function(e,i){if("object"==typeof exports&&"object"==typeof module)module.exports=i();else if("function"==typeof define&&define.amd)define([],i);else{var a=i();for(var o in a)("object"==typeof exports?exports:e)[o]=a[o]}}(window,function(){return function(e){var i={};function a(o){if
(Note that this sample is up to the supposedly-offending column. As you can see, window is not actually referred to on column 286.)
I'm not that familiar with Webpack, and documentation on plug-ins relatively scarce, so any pointers on how to get closer to solving this are very much welcome/

Add this at your root .js file will probably work.
if (typeof window === 'undefined') {
global.window = {};
}

Related

How to deal with node modules in the browser?

My title is a bit vague, here is what I'm trying to do:
I have a typescript npm package
I want it to be useable on both node and browser.
I'm building it using a simple tsc command (no bundling), in order to get proper typings
My module has 1 entry point, an index.ts file, which exposes (re-exports) everything.
Some functions in this module are meant to be used on node-only, so there are node imports in some files, like:
import { fileURLToPath } from 'url'
import { readFile } from 'fs/promises'
import { resolve } from 'path'
// ...
I would like to find a way to:
Not trip-up bundlers with this
Not force users of this package to add "hacks" to their bundler config, like mentioned here: Node cannot find module "fs" when using webpack
Throw sensible Errors in case they are trying to use node-only features
Use proper typings inside my module, utilizing #types/node in my code
My main problem is, that no matter what, I have to import or require the node-only modules, which breaks requirement 1 (trips up bundlers, or forces the user to add some polyfill).
The only way I found that's working, is what isomorphic packages use, which is to have 2 different entry points, and mark it in my package.json like so:
{
// The entry point for node modules
"main": "lib/index.node.js",
// The entry point for bundlers
"browser": "lib/index.browser.js",
// Common typings
"typings": "lib/index.browser.d.ts"
}
This is however very impractical, and forces me to do a lots of repetition, as I don't have 2 different versions of the package, just some code that should throw in the browser when used.
Is there a way to make something like this work?
// create safe-fs.ts locally and use it instead of the real "fs" module
import * as fs from 'fs'
function createModuleProxy(moduleName: string): any {
return new Proxy(
{},
{
get(target, property) {
return () => {
throw new Error(`Function "${String(property)}" from module "${moduleName}" should only be used on node.js`)
}
},
},
)
}
const isNode = typeof window === undefined && typeof process === 'object'
const safeFs: typeof fs = isNode ? fs : createModuleProxy('fs')
export default safeFs
As it stands, this trips up bundlers, as I'm still importing fs.

nodemon starting `node server.js` TypeError: marked is not a function

I'm creating a blog, using this 'Web Dev Simplified' tutorial:
https://www.youtube.com/watch?v=1NrHkjlWVhM
I've copied the code from git hub https://github.com/WebDevSimplified/Markdown-Blog, installed the node modules and linked it to my mongodb database online.
Node Modules include;
express, mongoose, ejs, --save-dev nodemon, slugify, method-override, dompurify, jsdom.
The database was working and I could save articles, until I added the last part about sanitizing HTML and converting markdown to HTML, this is when the 'TypeError: marked is not a function' comes up, and the save button ceases to work.
Seems a once understood function is now not understood because of a more recent node module dependency, either the dompurify library or jsdom. I'm really out of my depth here! please help!
From Marked Documentation:
https://marked.js.org/#demo
Node JS
import { marked } from 'marked';
// or const { marked } = require('marked');
const html = marked.parse('# Marked in Node.js\n\nRendered by **marked**.');
Your Code:
if (this.markdown) {
this.sanitizedHtml = dompurify.sanitize(marked(this.markdown))
}
try this:
if (this.markdown) {
this.sanitizedHtml = dompurify.sanitize(marked.parse(this.markdown))
}
its work for me
In my case:
const { marked } = require('marked');
instead of
const marked = require('marked')
...
this.sanitizedHTML = dompurify.sanitize(marked.parse(this.markdown))
Per node example documentation at https://marked.js.org/#demo

Issue bundeling a library using webpack to be consumed in browser

Looking for on how to bundle a library using webpack, the library link is : https://github.com/InteractiveAdvertisingBureau/Consent-String-SDK-JS/
I tried the following structure :
> /dist
> - index.html
> /src
> - index.js
> package.json
> webpack.config.js
content of :
index.html
<!doctype html>
<html>
<head>
<title>Hello Webpack</title>
</head>
<body>
<script src="bundle.js"></script>
<script type="text/javascript">
var consentData = new CSLib();
console.log('euconsent : '+consentData);
</script>
</body>
</html>
index.js
require('consent-string');
webpack.config.js :
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
library: 'CSLib',
libraryTarget: 'umd',
path: path.resolve(__dirname, 'dist')
}
}
after runnning npm run build the bundle.js file is generated
but when trying to access the index file, an error is occured in the browser, say that CSlib is undefined.
please your help, I would really appreciate it.
First of all, you need to bind the result of require('consent-string'); to something. I do not know the library but looking at their npm page you should be able to do the following.
const { ConsentString : CSLib} = require('consent-string');
However even if you do that it would not work due to some details of how webpack actually works. Basically each module, or rather file, is executed inside it's own scope and they do not leak to the global context. How does it do this? Allow me to demonstrate.
Webpack internals basics
Let's start with the following example file that imports jquery, prints "test", and exports something.
const $ = require('jquery');
console.log("test");
export function test() {
console.log("test");
}
Run webpack on this file and open bundle.js. You will find that it starts by defining a function as follows: function(modules). This is webpacks bootstrap function. If you really count all the brackets you will find that it is defined and then immediately called with an array of functions with the following signature function(module, exports, __webpack_require__). Each function in this array represents a module, or rather a file, that webpack has included in the bundle. You will find a module 0 which is generated by webpack. It looks like this:
/* 0 */
/***/function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ }
All it does is call __webpack_require__(1). So what does that do? __webpack_require__ is passed in as an argument to the module function that we are in but if you look inside the bootstrap function you will find that it is defined there. It works as follows
If the module with the given id (the id being an index into the array of modules that we discussed earlier) has been "installed" simply return that modules exported properties. A module has been installed if it has an entry in the installedModules array.
Otherwise, define a new module object (stores the modules id, if it has been loaded yet and it's exports) then call the module function with some arguments that I will discuss later.
Mark the module as loaded, add the module object to installedModules, and return its exports property (we will see how exports is populated in a minute).
Now let's look at how webpack has transformed the code we gave it. It can be found in module 1 (which is called from module 0 remember) and looks as follows (there might be some bookkeeping stuff in there too but I will ignore it, it's there for compatibility reasons I think):
var $ = __webpack_require__(2);
console.log("test");
function test() {
console.log("test");
}
exports.test = test;
The first line is var $ = __webpack_require__(2); This we have already discussed. It just imports jquery which is module 2 (which is why module 2 takes up most of the file as it includes all of jquery).
Then we have console.log("test");. Nothing has changed from the code we passed in.
But the function we exported has been split into two statements. First the function is defined and then it is added to exports as a property. The exports object is passed in by __webpack_require__ and it represents the properties that the module exports. It is stored in installedModules in a module object. Remember that any subsequent call to __webpack_require__ will just return the exports property of this module object.
tldr: Webpack will transform all of those fancy module based operations into calls to __webpack_require__ for imports and assignments to exports for export statements. This gives the illusion that every module exists in it's own little world.
What this means to you
Since each module is run inside it's own scope you will need to group the code that uses require('consent-string'); with the require statement. In conclusion your index.js should look like this:
const { ConsentString : CSLib} = require('consent-string');
var consentData = new CSLib();
console.log('euconsent : '+consentData);

Purescript pulp build output generates requirejs error in browser

When I use pulp build -O -t html/main.js and then pulp build -O -I test -m Test.Main -t html/testmain.js (i.e. building main and test) I get two different js output. In the former case, the format is
// Generated by psc-bundle 0.8.2.0
var PS = { };
(function(exports) {
// Generated by psc version 0.8.2.0
"use strict";
var Prelude = require("../Prelude");
var Control_Monad_Eff = require("../Control.Monad.Eff");
exports["main"] = main;
})(PS["Main"] = PS["Main"] || {});
PS["Main"].main();
Please note the require. In the latter case, the require is not in place at all
// Generated by psc-bundle 0.8.2.0
var PS = { };
(function(exports) {
/* global exports */
"use strict";
exports.concatArray = function (xs) {
return function (ys) {
return xs.concat(ys);
};
};
exports.showNumberImpl = function (n) {
/* jshint bitwise: false */
return n === (n | 0) ? n + ".0" : n.toString();
};
})(PS["Prelude"] = PS["Prelude"] || {});
(function(exports) {
// Generated by psc version 0.8.2.0
"use strict";
var $foreign = PS["Prelude"];
var Semigroupoid = function (compose) {
this.compose = compose;
};
Both examples are shorten, but the point is that require is used in the first time, while not used in the second time.
The issue is that I am not able to run the version using require in the browser due to this error
ReferenceError: require is not defined
When I included require.js into page, I got this error
Error: Module name "../Prelude" has not been loaded yet for context: _. Use require([])
http://requirejs.org/docs/errors.html#notloaded
Thus my question is, what can be done to run the first case in browser.
My guess would be that this comes from running builds with different --require-path options; once with the old default, which was an empty string, and once with ../. This would lead to psc-bundle not realising it needed to include Prelude and Control.Monad.Eff properly in the first case. psc-bundle should replace those require calls with references to the other modules, so that the code works in browsers.
There are a few different ways this can happen, and the compiler has been updated now in a way that should make the probability of this happening again much lower, so I wouldn't spend too much time worrying about exactly how this has occurred.
If none of the above makes any sense to you, don't worry; I think you just need to do the following to fix this:
Update to the latest version of psc (0.8.3 changed the --require-path default to ../, so any version after 0.8.3 should do, but you will want the latest version in most cases)
Delete your output directory
Compile everything again.
You probably need to use the --browserify option to build the first case for the browser.

Not able to load AMD modules through Jest

I'm trying to use Jest for unit testing my React code but I'm also using requirejs and so all my React code is in AMD modules. It obviously works well in browser but when I run Jest tests, it can't load my AMD modules.
Initially it was giving me error for define saying define is not defined, so I used amdefine by Requirejs to fix it. Now it can understand define but still can't load my module.
I had the same problem today and here is what I've done :
Module.js
(()=> {
const dependencies = ['./dep.js'];
const libEnv = function (dep) {
// lib content
function theAnswer (){
return dep.answer;
}
return {theAnswer}; // exports
};
//AMD & CommonJS compatibility stuff
// CommonJS
if (typeof module !== 'undefined' && typeof require !== 'undefined'){
module.exports = libEnv.apply(this, dependencies.map(require));
module.exports.mockable = libEnv; // module loader with mockable dependencies
}
// AMD
if (typeof define !== 'undefined') define(dependencies, libEnv);
})();
You will found all needed files on my github repository to test :
in browser for requirejs
with node for jest
https://github.com/1twitif/testRequireJSAmdModulesWithJest
I ran into same problem, so I went ahead and mocked my dependency at the top of test file:
jest.mock('../../components/my-button', () => {
// mock implementation
})
import MyButton from '../../components/my-button';
This way, when MyButton is loaded, its dependency is already mocked, so it won't try to load the RequireJS module.
There's no official Facebook support for requirejs in Jest yet. But's planned. Look this thread:
https://github.com/facebook/jest/issues/17
Also in this thread Sterpe posted a plugin he wrote to do it (but I didn't try it):
https://github.com/sterpe/jest-requirejs

Resources