Why should I always use "browser" field in package.json to load jQuery plugin installed as npm module?
Some more details:
I'm using browserify-shim to load jQuery via CDN, and want to use jQuery plugin installed as npm module.
My app.js is as the following:
var $ = require('jquery'),
plugin = require('jquery-plugin');
$(function(){
// test jQuery
$('.test-jquery').text('jQuery works');
// test jQuery plugin
$('.test-plugin').plugin({
// some config
});
});
My package.json includes the following config for browserify:
"browserify": {
"transform": [
"browserify-shim"
]
},
"browserify-shim": {
"jquery": "global:$"
},
"browser": {
"jquery-plugin": "./node_modules/jquery-plugin/jquery-plugin.min.js"
}
It only works if I have the "browser" field for every plugin I want to use, otherwise I get "plugin is not a function" error at plugin function, but jQuery works.
How can I skip the "browser" field?
Related
I'm trying to create a javascript bundle with JSPM that includes fabric.js version 2. Alas, performing jspm bundle fails.
$ jspm bundle src/main --minify --inject
Building the bundle tree for src/main...
err Error on fetch for #empty/lib/jsdom/living/generated/utils.js at file:///Users/dkoerner/projects/JSPMFabricTestCase/#empty/lib/jsdom/living/generated/utils.js
Loading npm:fabric#2.0.0-rc.4/dist/fabric.js
Loading npm:fabric#2.0.0-rc.4.js
Loading src/bootstrap.js
Loading src/main.js
Error: ENOENT: no such file or directory, open '/Users/dkoerner/projects/JSPMFabricTestCase/#empty/lib/jsdom/living/generated/utils.js'
I have created a test project (https://github.com/dkoerner85/JSPMFabricTestCase) following the jspm and fabric examples and verified that bundle creation works with the current fabric stable release v1.7.22. Raising the version to the release candidate v2.0.0-rc4 however breaks bundling.
I am fairly new to javascript app development and hence do not understand the reason why this fails and how to repair it. I am grateful for any pointers or explanation.
npm: v5.5.1
node: v8.9.3
Solution
Add an override section to your project's package.json:
"overrides": {
"npm:fabric#2.0.0-rc.4": {
"map": {
"canvas": "#empty",
"fs": "#empty",
"jsdom/lib/jsdom/living/generated/utils": "#empty",
"jsdom/lib/jsdom/utils": "#empty",
"jsdom": "#empty",
"http": "#empty",
"https": "#empty",
"xmldom": "#empty",
"url": "#empty"
}
}
}
Insight
After reading about jspm and systemJS, I have found a solution to this problem. The critical code is in the fabric.js and package.json files of the fabric package.
dist/fabric.js
...
fabric.document = require('jsdom').jsdom(decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'),{ features: {FetchExternalResources: ['img']}});
fabric.jsdomImplForWrapper = require('jsdom/lib/jsdom/living/generated/utils').implForWrapper;
fabric.nodeCanvas = require('jsdom/lib/jsdom/utils').Canvas;
fabric.window = fabric.document.defaultView;
DOMParser = require('xmldom').DOMParser;
...
package.json
...
"browser" : {
"canvas": false,
"fs": false,
"jsdom": false,
"jsdom/lib/jsdom/living/generated/utils": false,
"jsdom/lib/jsdom/utils": false,
"http": false,
"https": false,
"xmldom": false,
"url": false
},
...
Now, when installing the package with jspm install npm:fabric#2.0.0-rc.4, jspm will modify the require statements, resulting in the following code:
jspm_packages/npm/fabric#2.0.0-rc.4/dist/fabric.js
...
fabric.document = require('#empty').jsdom(decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'), {features: {FetchExternalResources: ['img']}});
fabric.jsdomImplForWrapper = require('#empty/lib/jsdom/living/generated/utils').implForWrapper;
fabric.nodeCanvas = require('#empty/lib/jsdom/utils').Canvas;
fabric.window = fabric.document.defaultView;
DOMParser = require('#empty').DOMParser;
...
When trying to create a bundle, jspm will stumble over the '#empty/lib/jsdom/living/generated/utils' require statement.
Error: ENOENT: no such file or directory, open '/Users/dkoerner/projects/JSPMFabricTestCase/#empty/lib/jsdom/living/generated/utils.js'
jspm treated the jsdom entries first as required by the package.json, thereby failing to recognize the more detailled entry 'jsdom/lib/jsdom/living/generated/utils'. It is necessary to provide the map in the correct order - long paths before short paths.
This can be achieved with a local override as depicted in the solution above. Please note that jsdom is ordered below #empty/lib/jsdom/living/generated/utils in the override, as opposed to the original package.json.
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.
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')
I've added jQuery as a script tag in my html file and have added it to package.json for working with browserify-shim as follows:
"browserify": {
"transform": [
"browserify-shim"
]
},
"browserify-shim": {
"jquery": "global:jQuery"
},
I'm able to expose it in my main script file with a simple require('jquery') call.
The problem is that I'm using some jQuery plugins which internally do a require('jquery') and since browserify transforms don't apply to dependency of dependencies, it's causing browserify to complain with bundling since it cannot find jQuery.
Now I know that I can solve it by applying global-transforms by I cannot find a way to do it easily.
Browserify docs say that you cannot apply global-transforms in package file so the following don't work, (which I thought would):
"browserify": {
"global-transform": [
"browserify-shim"
]
},
"browserify": {
"transform": [
"browserify-shim"
],
"global": true
},
I also tried adding the option to my Gruntfile.js as follows, but even that doesn't work:
browserify: {
options: {
global: true
},
dist: {
files: {
'js/bundle.js': 'js/script.js'
}
},
},
The last option is to manually add a browserify-shim to every dependency's package.json, but I don't want to do it, since it means every time I add a new plugin, I would have to repeat the same process.
Any ideas to mitigate the above problem?
You should be able to apply global-transforms by providing transform with a hash option:
"browserify": {
"transform": [
["browserify-shim", {global: true}]
]
}
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")