Browserify shim doesn't seem to attach Tether to window object - javascript

I have the following in my package.json file:
"browserify": {
"transform": [
"browserify-shim"
]
},
"browser": {
"jquery": "./node_modules/jquery/dist/jquery.js",
"tether": "./node_modules/tether/dist/tether.js"
},
"browserify-shim": {
"jquery": "$",
"tether": "Tether"
}
And then this in one of my JS modules:
const $ = require('jquery');
const Tether = require('tether');
I then get the following error in the browser:
tether.min.js:1 Uncaught TypeError: Cannot set property 'Tether' of undefined
However, if I don't try to shim Tether and just use window.Tether in the module that requires it, it works fine.
const $ = require('jquery');
window.Tether = require('tether');
Does anyone know why the browserify-shim wouldn't work for Tether in this way?

You're correct - you need to manually specify the window object from your bundle.
I'm not 100% sure, but my understanding is that this part of the documentation, when it says
x exports window.$
actually means that $ is available to all modules within the bundle as $ - this does not mean the window object of your webapp.
See for instance this issue.
The problem is in that section of the documentation where it seems people believe the object should be part of the window - might be a good idea to change the wording of that.

Related

Cannot read property 'bind' of undefined in Angular 8 custom web component

I am building a custom web component using angular 8. I have noticed that Angular 8 doesn't have --single-build true, so i used the following code
(async function build() {
const files = [
"./dist/bundle/runtime-es2015.js",
"./dist/bundle/polyfills-es2015.js",
"./dist/bundle/styles-es2015.js",
"./dist/bundle/scripts.js",
"./dist/bundle/vendor-es2015.js",
"./dist/bundle/main-es2015.js"
];
await fs.ensureDir("elements");
await concat(files, "elements/bundle.js");
})();
I have excluded the ...-es5.js files. The following code works when i remove all scripts tags in the index.html file and use bundle.js but when i import the custom element in another project i get Uncaught TypeError: Cannot read property 'bind' of undefined from the following line :
*/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/******/ jsonpArray.push = webpackJsonpCallback;
window["webpackJsonp"] returns a function instead of an array causing jsonpArray.push.bind(jsonpArray); to error. Came across ngx-build-plus, --single-bundle works but excludes the global styling only imports component scss, tried including --bundle-styles false and --keep-styles in the ng build, it didn't work.
How can i solve the following error Uncaught TypeError: Cannot read property 'bind' of undefined when i concat all files ? Take note i am sing angular webpack not custom webpack. 2nd how can i make ngx-build-plus render global scss ?
The following link Cannot read property 'bind' of undefined helped me a lot. To add jsonpFunction: "o3iv79tz90732goag" in angular 8 had to npm i #angular-builders/custom-webpack and create a webpack config file see below: see documentation for #angular-builders/custom-webpack
const webpack = require('webpack');
module.exports = {
output: {
jsonpFunction: 'wpJsonpBundle'
}
};
Then add the custom webpack config file to angular.json
"architect": {
"build": {
"builder": "#angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.config.js"
},
....
After ng build, i then concatenate the files using the function in the question and can use the custom element in other angular projects.

Webpack error on lodash load using externals config

i am trying to modify a current project using webpack. On my scripts i am using lodash library but i have also scripts that are not written on ES6 so i load lodash globally using a normal script tag.
Based on the webpack documentation i have added the below config on the webpack (https://webpack.js.org/configuration/externals/).
lodash : {
commonjs: 'lodash',
amd: 'lodash',
root: '_' // indicates global variable
},
I compile the code using babel without any problem but when the actual code is executed i receive the below error
"TypeError: Cannot read property 'forEach' of undefined"
If i remove the import _ from "lodash"; statement from the file then the issue is resolved and script is working as expected as _ is on global.
But this was expected to work also without adding the externals correct ? According with the webpack example for jquery
module.exports = {
//...
externals: {
jquery: 'jQuery'
}
};
The below code will be unchanged
import $ from 'jquery';
$('.my-element').animate(/* ... */);
In my case why is not working ? Any suggestions ? ideas?
I think i found a solution, after i read the documentation again and again i noticed that message which is also on yellow border
An object with { root, amd, commonjs, ... } is only allowed for libraryTarget: 'umd'. It's not allowed for other library targets.
So i have changed my config from
lodash : {
commonjs: 'lodash',
amd: 'lodash',
root: '_' // indicates global variable
}
to
lodash: '_'
and issue solved, now is working as expected. It will be great of course if i understand why this was an issue

Is there a way to allow browserify modules access to the global scope (window object)?

The Scenario
I'm using Browserify (or NodeJS/CommonJS like imports) to require some JavaScript libraries. However, I'm having trouble getting them to play nice since apparently Browserify by default denies global scope access to all modules. For example, doing this doesn't work;
file1.js
require('moment');
file2.js
moment(new Date()); // throws moment is undefined
But this works by changing the contents of file1.js to the following;
window.moment = require('moment');
The Problem
This has worked well enough so far, however now I'm having trouble loading the moment timezone library (an extension of MomentJS). Moment Timezone's docs imply both scripts should run on the window global scope by adding them as script tags like so;
<script src="moment.js"></script>
<script src="moment-timezone-with-data.js"></script>
So they can be used like this;
moment().tz("America/Los_Angeles").format();
This however, seems very hard to achieve, since if I try the following;
window.moment = require('moment');
require('./../../node_modules/moment-timezone/builds/moment-timezone-with-data-2010-2020'); // the location of my moment-timezone library
I get a runtime error saying that moment.tz is undefined (meaning that the second library wasn't run with the global scope). And if I try to add the window.moment to the second line of code, I'll be rewriting the first instance of the full library.
So, in short;
Is there a way to allow certain Browserify imports to have global scope access, or to run with the window object as their scope?
I'm aware of the security implications this has, but selectively used, this would be immensely useful while requiring things like JavaScript libraries that need global access to set up themselves correctly. Any help would be greatly appreciated.
Put tiny libraries into vendor.js, include that on your page with <script>, make them known to browserify with browserify-shim and use them with require('libname') in your modules.
Larger libraries may be included from, say, vendor CDNs, and also be made known to browserify with browserify-shim.
index.html
<html>
<head>
<script src="vendor.js"></script>
<script src="bundle.js"></script>
</head>
<body>
Open devtools and inspect the output
</body>
</html>
package.json
{
"scripts": {
"build": "cat vendor1.js vendor2.js > dist/vendor.js && cp index.html dist/index.html && browserify index.js -o dist/bundle.js"
},
"browserify-shim" : {
"vendor1" : "global:vendor1",
"vendor2" : "global:vendor2"
},
"browserify" : {
"transform" : [ "browserify-shim" ]
},
"devDependencies": {
"browserify": "^14.1.0",
"browserify-shim": "^3.8.14"
},
"dependencies": {
"moment": "^2.18.1",
"moment-timezone": "^0.5.11"
}
}
A couple of things to note about the above package.json:
build: it builds 'application' in dist folder, creating the following structure:
dist
index.html
bundle.js
vendor.js
"browserify" entry: it just adds browserify-shim
"browserify-shim" entry: it tells browserify that vendor1 and vendor2 are available as properties on window (global) object. Browserify won't attempt to bundle them, and require() will just return those properties. You need to make sure they are actually available (so index.html above includes them with <script src="vendor.js">
index.js
var momentfromnpm = require('moment-timezone');
var vendor1 = require('vendor1');
var vendor2 = require('vendor2');
console.log("Hello");
console.log("Using module from npm", momentfromnpm().tz("Europe/London").format());
console.log("using a function from vendor1.js (bundled into vendor.js)", vendor1());
console.log("using a 'module' from vendor2.js (bundled to vendor.js)", vendor2.doWork());
vendor1.js
window.vendor1 = function() { // could also be just `function vendor1()...`
return "I'm a simple function, defined on a window";
};
vendor2.js
var vendor2 = { // could also be window.vendor2
doWork: function() {
console.log("vendor2 doing work");
}
};
Install the moment-timezone package as instructed on the website.
npm install moment-timezone --save
In file1.js, define window.moment as so:
window.moment = require('moment-timezone/builds/moment-timezone-with-data-2010-2020');
This should allow you to use moment().tz() in file2.js

Browserify with jQuery plugins

I'm trying to get some jQuery plugins to work with browserify. I have my package.json setup like this:
"browser": {
"jquery": "./client/js/vendors/jquery-2.2.2.min.js",
"jquery-validation": "./client/js/vendors/jquery.validate.js"
},
"browserify-shim": {
"jquery": "global:$"
},
However, when I require('jquery-validation'), I get cannot read property fn of undefined as it relates to this plugin. I'm trying to also have it so that $ will be global as it's used all over, without having to require it.
I've seen so many different articles and configs for this, but nothing seems to work.
Any suggestions or clarity would be greatly appreciated.
EDIT:
I also sometimes get Uncaught Error: Cannot find module 'jquery'
You don't need to shim jquery, it's conpatible with node yoo can simply install it with npm install jquery --save and make var $= require("jquery") in your code.
Yo do need to shim jquery.validate.
package.json:
{
"browser":{
"jquery-validation":"./node_modules/jquery-validation/dist/jquery.validate.js"
},
"browserify-shim":{
"jquery-validation":{
"depends":[
"jquery:jQuery"
]
}
},
"browserify":{
"transform":[
"browserify-shim"
]
},
"dependencies":{
"jquery":"^2.0.0",
"jquery-validation":"^1.15.1"
}
}
Note: jquery.validate depend on jQuery version 2.0^ ( you can see in the package.json file of the jquery-validation's npm package ) so you have to set dependency on jquery ^2.0 in your project otherwise jquery-validation will load he's own version of jQuery and integration will not work.
It seems like you have typo, you should call require('jquery-validation') instead of require('jquery-validate')

order dependencies: jQuery is not defined with browserify

I am trying to use a plugin that is in /js/lib/stellar.jquery.js:
var $ = require('jquery');
require('./lib/stellar.jquery')
$(function(){
$.stellar();
});
When I run this though I get jQuery is not defined. I think the stellar jQuery plugin is loading before the jq library. At the bottom of the stellar plugin there's this code:
...
// Expose the plugin class so it can be modified
window.Stellar = Plugin;
}(jQuery, this, document));
Changing "jQuery" to "$" does not work either, gives "$ is not defined"
There is not any need to specify order for dependencies.
Because neither jQuery nor your plugin support CommonJS modules, you need to shim them to make them compatible with the browserify modules concept.
npm install browserify-shim --save-dev
add alias for jQuery and your plugin to your package.json (optional, but recommended)
"browser":{
"customPlugin": "path/to/custom/plugin",
"jquery": "./node_modules/jquery/dist/jquery.js"
}
add browserify shim transformation to enable shimming by adding to your package.json
"browserify": {
"transform": [
"browserify-shim"
]
}
configure shims
"browserify-shim": {
"jquery" : "jQuery",
"customPlugin" : { "depends": [ "jquery:jQuery" ] },
}
Consider, in dependencies configuration before colon you should specify file name, NOT SHIMMED MODULE NAME!!!
after colon you should specify identifier, which is expected by your module in global namespace.
Then, require your plugin to initialize it's code before usage
'use strict';
require('customPlugin');
var $ = require('jQuery');
$('.some-class-selector').myCustomPlugin();
Seems like another solution is to add :
global.jQuery = require("jquery")

Categories

Resources