How do you import jQuery plugins with Webpack? - javascript

I'm using Webpack v4, and I have jQuery plugins which I currently load into our app with the webpack-merge-and-include-globally webpack plugin (and then manually load these into the html file with a <script> tag) but I would like to be able to move this into our main app code, so that webpack can be aware of them. There's been issues where some dependancies/classes are loaded twice, once in the merge-plugin mentioned above, and again in the Webpack dynamic imports.
So far its been hit and miss trying to get jQuery plugins to load and properly attached to the jQuery object.
Is there a recommended way to import jQuery plugins, as its not like a normal JavaScript ES6 class which you can just prefix with export class ClassName or export default class ClassName, because the plugin is wrapped in an IIFE (Immediately Invoked Function Expression).

A few potential solutions:
Option 1 - jQuery plugin that does not need its own global
import 'myapp/jquery-plugins/MyjQueryPlugin';
This one seemed potentially too good to be true / too easy, as it did not require new loaders.
Add the import statement without a name, just the script path. This should self execute the script, similar to how it would on a load from HTML.
Note that this would not work with a script that needs to be Global. For that, use the expose-loader in option 2 to specify what globals to expose.
As I found that each plugin/library/class I had to work with required a different way of handling it, I will also mention some other methods I found useful, and may be useful to others:
Option 2 - globals, such as jQuery
import 'expose-loader?exposes[]=$&exposes[]=jQuery!jquery';
This would import and execute it as it went, so that the next import line could use the global variable straight away.
Option 3 - alternative for globals
import MyClass from './views/MyClass';
window.MyClass = MyClass;
You can import, and then set on the window yourself, but if you have several import statements, and the latter depend on the first one already being available, then this wont work, as its not yet defined. See option 2. This is my least favorite, but worth mentioning as a fallback.
Option 4
Use the [webpack-merge-and-include-globally][1] plugin, but this is the one I was trying to avoid/reduce the use of, as its outside of "webpack's awareness."
Others
There's also other loaders like raw-loader, which you can use as normal with a !! in the import, or with a .then promise to control execution after load.
import('raw-loader!someScript.js').then(rawModule => eval.call(null, rawModule.default))
This is the example given by the docs in script-loader as an alternative, as script-loader is deprecated.

Related

Importing files from a either src/ or lib/

I have a package (let's call it preset) that imports a component (component). Both are maintained by me. For backwards compatibility, preset additionally has to import a specific function from component, which is only available from a specific file (not the one in the main field). To do this, I use the require('component/lib/file.js') syntax.
However, file.js has side-effects. This is fine normally, but for Webpack builds I also have lib-mjs (with ES module syntax for tree shaking). This causes file.js to be loaded twice, once indirectly in lib-mjs via component, and once directly in lib.
A few options:
I could move file.js out of lib/lib-mjs altogether. However, this complicates the build process and messes up the repo structure.
I could export the specific function anyway. However, since component follows the same pattern with a lot of different components, I'd have to change that in a lot of places.
I could remove the side-effect to some init function. However, this would be a breaking change and involve quite some work.
I could make sure the side-effect only happens if it hasn't happened already. However, that and the previous change still has the file loaded twice. On top of that, it would require some semi-global state, I don't think I can pull off duck-typing for this one.
Is there some option like require(require.resolve('component') + '/file.js') that actually works for both Node and Webpack?

javascript one class per file using eslint

I would like to use javascript classes with one class per file. It is part of a larger project using eslint. I started with:
/*global CSReport*/
/*global CSManager*/
class CSMain {
constructor() {
this.report = new CSReport();
this.manager = new CSManager(this.report);
}
launchReport(...
}
However, eslint generates an error saying CSMain is defined but never used. This led to the idea of using export and import which seemed better than making everything global (side note: CS in front of main is the old style method to avoid global conflicts)
The question is how to put this together. The release version will be a single (uglified) file, so the class file names will no longer exist when they are all concatenated together in (say) csCompiled.js.
Questions:
Import uses a file name. Should I use the CSCompiled.js name rather than the file names before concatenation?
Do I want a single module or a module for each class?
Do I need to export every class and import every class it uses?
I am not fully sure how angular accesses this code but am thinking to import csMain.
I tried to find an answer to this but am only finding older posts that don't use ecmascript 6 and classes. If an answer to this exists, I am not sure how to get to it.
Background:
The main project uses angular 1. This code is separate for legacy reasons. It is currently written in java using gwt, but we want to move to javascript to remove the reliance on gwt. It is about 30-40 files (classes) total to convert.
The code gets and handles data from the server for report requests. There is a lot of pre-processing done before it is handed back to the rest of the UI.
I have used javascript for an established project using angular, but lack expertise on how to create new projects.
I am trying to use basic javascript for this, so it won't need updating if (for example) we go from angular 1 to the current versions. I do not yet know if this is a good way to do it.
ESLint is complaining because you are not exporting the class you created, therefore, it can't be accessed by other modules. You can fix that with a simple line at the end
export default CSMain;
Import uses a file name. Should I use the CSCompiled.js name rather
than the file names before concatenation?
Use the file name before you compile/transpile/uglify/etc. After that it will all become 1 file and the bundler will take care of that for you.
Do I want a single module or a module for each class?
Completely optional, I like to have 1 class per file and then 1 file for the module (index.js) that lists all classes in that module.
Do I need to export every class and import every class it uses?
Yes, you need to import everything your module will use and export everything that should be public or "importable" for other modules.
I am not fully sure how angular accesses this code but am thinking to import csMain.
It all depends on how you export your file. Make sure to import the same name your module/file is exporting.

ES6: What does "import $ from 'jquery'" really means?

I assumed at first that it simply means, load the jQuery module and initialize it in a variable called $.
But then, by using Atom with the atom-typescript, I got an error saying that it "Cannot find module 'jquery'". Even though all the code works in the browser, it looks like atom-typescript can't resolve anything looking like import x from y.
Now, looking at ES6 doc, I found out that you import a class/function from a module. The meaning is totally different, and it makes sense with for example this:
import { Component } from 'angular2/core';
But then what does it mean in the case of jQuery?
I am probably mixing different issues in the same one but any explanation would clear this confusion, so thanks a lot in advance :)
The statement import $ from jquery pretty much amounts to dependency injection. Just like one would write import React from 'react' to give oneself access to the React library within a file, so to can one write import $ from jquery. (In case it's throwing you off, the dollar sign is used because jQuery and its methods are accessed using the dollar (a.k.a. jQuery) operator.
As for the errors being thrown, that could be several things:
If you separately installed jQuery as a dependency in your package.json file as well as included a <script> tag from a jQuery CDN, this error will be thrown. If you're usage of jQuery is through NPM, then the import $ from jquery syntax is correct/necessary. If you intend to use jQuery through a CDN (as I would recommend), the import statement is unnecessary. (Since you've included that script tag in your index.html, you have access to jQuery and its library throughout the scope of your application). Do not, however, do both.
Also note that in the case of the import { Component } from 'angular2/core'; statement, something slightly different is going on. Namely, one is importing the named export Component, as per the (AMD specification. You can think of it, in this case, as importing only a part of the larger Angular2 core library when the entire library would be unnecessary.
Just to be sure, check that you have actually given yourself access to jQuery through either a CDN or by installing it as an NPM dependency.

Why are package imports needed in Meteor

About a year ago I have used Meteor, and now I want to use it again, but many things have changed.
When I follow the Blaze tutorial on Meteor.com, they add imports on top of their files:
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { ReactiveDict } from 'meteor/reactive-dict';
I got the app working. But when I comment the imports out, the app keeps working like it should work. Why are these imports needed?
I am still using the regular Javascript, not ES6.
Thanks!
The import statement is used to import functions, objects or primitives that have been exported from an external module, another script, etc.
The name parameter is the name of the object that will receive the exported members. The member parameters specify individual members, while the name parameter imports all of them. name may also be a function if the module exports a single default parameter rather than a series of members. Below are examples to clarify the syntax.
Import an entire module's contents. This inserts myModule into the current scope, containing all the exported bindings from "my-module.js".
For more detail about the different ways we can use import along with their usage, please check this.
They still use the old globals for backwards compatibility. However it is recommended to use the imports so if in some future release they remove the globals your code will still work. You can read more in the appropriate section of the guide.
Ok you know import is to import an exported object from another file already.
The point that you may have missed is that MDG heard the need to stop loading everything by default, or at least to provide a mean to control what is loaded in memory and what is not.
Look for the /imports special directory.
Files in that folder are no longer loaded automatically, but only through import statement.
As for the tutorial, I guess they did not explained this functionality, and because it imports only standard functionalities which are still loaded eagerly for backward compatibility, it does not change anything removing those statements.

Import existing library with JavaScript ES6 Modules

How can an existing library be loaded and run using JavaScript's ES6 Modules?
For example, suppose I need to load an existing polyfill:
import {poly} from "thirdParty/poly";
How can I run the imported poly script and load its properties into the current namespace without changing the source?
Here are two practical problems to help clarify the problem I'm trying to solve:
I have a script called rafPolyfill.js which is a polyfill for window.requestAnimationFrame. I need to import it into the global scope and run it immediately as soon as it loads. This is easy to do with a <script> tag:
It runs and loads itself into the global scope. How is can this be done using ES6 modules?
I have another script called Font.js which is a pre-loader for fonts. It let's you create new font object like this:
var font = new Font();
I used Font.js by loading it with a script tag, like this:
<script src="Font.js"><script>
Without accessing, changing, or understanding the source code of this script, how is it possible to use ES6 modules to load and use the in the same way that I would with a <script> tag? I just need these scripts to run when they're loaded and take care of themselves.
A possible solution might be using the module Loader API:
http://wiki.ecmascript.org/doku.php?id=harmony:module_loaders
This document describes global binding of the System loader, but I'm afraid I don't have the vocabulary to fully understand what it's trying to explain. Any help in decoding this document would be greatly appreciated!
This answer is: "No, based on the ES6 spec it's not possible to load and run a global script the same way you can with a script tag."
But there is a solution: SystemJS
https://github.com/systemjs/systemjs
It's a universal module loader for ES6 modules, AMD modules, and global scripts (anything you load with the <script> tag)
Does this or something close to this work for you?
var stuffFromPoly = import "thirdParty/poly"
Then you would call methods off of the object stored in stuffFromPoly.
If that's not quite it, could you expand your question a bit, I'm trying to guess at exactly what you mean and I may be a bit off.
Quick note post-'your update':
Are you opposed to using https://www.npmjs.org/package/es6-module-loader ? Or does that not quite solve the problem?
From the readme:
The new ES6 module specification defines a module system in JavaScript using import and export syntax. For dynamically loading modules, a dynamic module loader factory is also included in the specification (new Loader).
A separate browser specification defines a dynamic ES6 module loader loader for the browser, window.System, as well as a tag for using modules.
This polyfill implements the Loader and Module globals, exactly as specified in the 2013-12-02 ES6 Module Specification Draft and the System browser loader exactly as suggested in the sample implementation.

Categories

Resources