How do I properly include JS libraries using Webpack? - javascript

I am quite new to Webpack and I am using it to bundle the assets used for a static website I am creating. This website uses multiple JS files from npm packages like
fullpage.js and parallax.js. I need to know how I can include these JS files using Webpack.
What I have tried
The first option I tried was import JS files via app.js
app.js
import './style.css';
import '../node_modules/fullpage.js/dist/jquery.fullpage.css';
window.$ = require('jquery');
window.$ = require('fullpage.js');
window.$ = require('parallax-js');
require('./index.js');
index.js
var scene = document.getElementById('scene');
var parallaxInstance = new Parallax(scene,{
frictionX: 0.1,
frictionY: 0.1
});
$(document).ready(function() {
$('#fullpage').fullpage({
recordHistory: false,
scrollBar: true,
});
});
However, since parallax-js is not imported in index.js, this doesn't work. Chrome debugger says that Parallax is not defined -- index.js
I can overcome this by moving the require section to the index.js but that doesn't seem clean.
My second option was to import these via webpack.config.js which seems to me is the correct approach.
webpack.config.js
module.exports = {
entry: {
main: './src/app.js',
libs: [
'./node_modules/parallax-js/dist/parallax.js',
'./node_modules/fullpage.js/dist/jquery.fullpage.js'
]
}
}
I included both the above created JS files (libs.js and main.js).
However I am still getting Parallax is not defined -- index.js
Which of the above two methods are correct? Is there a better way to achieve this?
I've read through a lot of documentation and articles but I could not find a proper answer for this very basic question which is why I am posting this here, wondering if I am missing something big.
Thank You.

Yep, you're missing something conceptually. You're still trying to use the global context (& window object). Webpack doesn't want you to work that way. Webpack wraps everything up and puts everything in modules.
You need to define your dependencies in every file you use them in. Then, you let webpack figure out how to bundle everything together. It's actually not messy to define dependencies per file; it's the right way of doing things, and the way every other programming language does it.
I would also recommend switching from require syntax to import syntax, as it helps you to think of it as a declaration, not a function call.
It might be helpful to poke around an ES6 tutorial/project to learn the new syntax and approach. It's a fairly major shift, and particularly difficult for existing JS developers who aren't drawing from experiences in other languages.
You may find this tutorial (or others) helpful: https://www.youtube.com/watch?v=lziuNMk_8eQ

Related

How to use a capacitor plugin meant to be imported in a vanilla js app

I'm currently pulling hairs trying to figure out how to go about this.
So, I'm working in a vanilla JS environment with no webpack setup served with capacitor and want to use this capacitor-plugin: https://github.com/CodetrixStudio/CapacitorGoogleAuth
However, to use this plugin I have to import the package into my client code.
Here's what I've tried:
Unpkg type="module": however browser support in mobile isn't that great. And this app will be served to a ton of users
Using browserify + esmify to bundle the plugins code into something I could import with a <script> tag into my index.html. Didn't work
My last thought is to setup webpack to bundle everything for me, similar to the browserify approach and import that. However before I go through with all of that I wanted to reach out here to see if you guys had any other ideas.
Is there a way to access this plugin from window maybe?
so I figured out the way to go about this by following this article: https://medium.com/#SmileFX/a-complete-guide-building-a-capacitorjs-application-using-pure-javascript-and-webpack-37d00f11720d
Basically you have a www/js directory (vanilla js), and a src directory (ES6/import code goes). You then configure webpack to output in your www/js/ directory.
Note: Any variable you want accessible to your vanilla js code must be explicitly stored in the window object.
Example
./src/toBeWebpacked.js
import Module from "your-module"
window.doSomething = () => Module.doSomething()
./www/js/vanilla.js
const useModuleCode = () => {
// use code from webpacked ES6 JavaScript here
return window.doSomething();
}

Exporting outside webpack

This is just something I thought today and I didn't see a lot of information so I'm going to share this weird cases and how I personally solved them (if there's a better way please comment, but meanwhile this might help others ^^)
In a regular module, you would do something like this to export your function/library/object/data:
// regular NodeJS way:
module.exports = data;
// ES6 way
// (will get transpiled to the regular way using the module variable by webpack)
export data;
default export data;
When compiling the library usually babel or tsc are used, but if for any reason you want not only to compile (transpile) your library but also pack it using webpack, you will encounter this case.
As you know, in a webpack bundle the module variable is local to the bundle (every module/file gets wrapped with a function where module is a parameter = local variable), so nothing really gets exported outside the bundle, is just nicely managed by webpack.
That means that you can't also access the contents using the regular require/import methods.
In some case you might find necessary to export outside webpack. (i.e. you are trying to build a library using webpack and you want it to be accessible by other people). This basically means you need to access the original module variable, but webpack doesn't expose it like it happened with __non_webpack_require__.
See also: Importing runtime modules from outside webpack bundle
The solution is to create our own __non_webpack_module__ (as webpack does with __non_webpack_require__.
How I did it is using webpack.BannerPlugin to inject some code outside the bundle. This code is prepended to the build after the minification is done, so it's preserved safely.
In your webpack.config.js:
plugins: [
new BannerPlugin({
raw: true,
banner: `const __non_webpack_module__ = module;`,
}),
]
And again, if you are using TypeScript, in global.d.ts:
declare const __non_webpack_module__: NodeModule;
And now, you can do something like this in your code:
__non_webpack_module__.exports = /* your class/function/data/whatever */
This will allow to import it as usual from other files
Tip: You might want to look at BannerPlugin to check other options, like include or exclude so this variable is only generated on the desired files, etc.

How to use js modules from non-module files

I'm a beginner at using js modules.
I'm working on a fairly simple web application. It uses typescript and angular 2, which heavily relies on modules.
Most of my app ts files 'import' one or many js modules (usually mostly angular 2 modules).
As I understand, because my app ts files have a top level 'import', they are automatically considered a js module by typescript.
However, I want any of my app ts files to be accessible by any other of my app ts files, without having to 'import' each other. But because they are now modules themselves, ts requires me to do that...
Is it possible?
It seems crazy to me that for each of my app ts file, I should have to declare every other of my app ts files that are used in there (I like to have tiny files with a single class/interface). In addition, this relies on relative paths which breaks as soon as I restructure my folder structure.
Am I thinking about this the wrong way?
You must have a js file which is an entry point to your application right?.. So in that file just import all the modules which you want to access without importing and attach them to the window object. Since the window object is available globally, you can access your module from anywhere without importing the corresponding module. For example,
Consider this scenario:
You have a module in a file called module1.ts
The entry point of your application is a file called index.ts
And you have a module2 where you require something from module1
// module1.ts
function add(first: number, second: number): number {
return first + second
}
export {add}
in your index.ts
// index.ts
import {add} from '<path to module1>/module1';
window.add = add
Now in your module2
// module2.ts
window.add(1, 2)
Since the window object is available globally you can attach as many properties to it as you like.
As far as the type resolution is concerned you can declare a window module with the add function you require in a .d.ts file as follows:
declare module window {
add: (first: number, second: number) => number
}
Declaring dependencies (e.g modules) for each file is a double-edged sword.
The advantage is that there is no 'magic' - you know exactly where each function, variable, class etc. is coming from. This makes it much easier to know what libraries / frameworks are being used and where to look to troubleshoot issues. Compare it to opposite approach that Ruby on Rails uses with Ruby Gems, where nothing is declared and everything is auto-loaded. From personal experience I know it becomes an absolute pain to try to workout where some_random_method is coming from and also what methods / classes I have access to.
You're right that the disadvantage is that it can become quite verbose with multiple imports and moving relative files. Modern editors and IDEs like WebStorm and Visual Studio Code have tools to automatically update the relative paths when you move a file and also automatically add the imports when you reference code in another module.
One practical solution for multiple imports is to make your own 'group' import file. Say you have a whole bunch of utility functions that you use in all your files - you can import them all into a single file and then just reference that file everywhere else:
//File: helpers/string-helpers.ts
import {toUppercase} from "./uppercase-helper";
import {truncate} from "./truncate-helper";
export const toUppercase = toUppercase;
export const truncate = truncate;
Then in any other file:
import * as StringHelpers from "../path-to/helpers/string-helpers";
...
let shoutingMessage = StringHelpers.toUppercase(message);
The disadvantage of this is that it may break tree shaking, where tools such as webpack remove unused code.
Is it possible
Not in any easy way. The ts file is a module and uses e.g. module.exports (if commonjs) that will need to be shimmed out. And that is just the runtime story. The TypeScript story will be harder and one way would be to make a .d.ts file for the module stating the contents as global.
Like I said. Not worth doing. Modules are the way forward instead of making something hacky.
It's not crazy at all. You are definitively thinking in the wrong way.
Actually what you don't like it's a common feature in all modern programming languages and it makes the code and structure of the app a lot clearer and simple to understand.
Without imports and going to old school way looks very crazy to me :)
You can have only chaos with so many global variables.

How to use ember.js without module support

The guides for ember.js are assuming one has the full ES6 support e.g. http://guides.emberjs.com/v2.2.0/routing/specifying-a-routes-model/ shows using the export default construct and doesn't specify any alternative way to achieve the goals. However the module feature is not implemented in all browsers that ember is supporting.
How can I use these features with a browser that doesn't support modules? How would the code in these examples translate to ES5?
Documentation assumes you are using a transpiling tool, because the recommended tool, ember-cli does. Unless you have good reasons not to use it, you definitely should look into it.
It is, however, perfectly fine to work without it. For instance, without a module system, Ember will map controller:posts.index to App.PostsIndexController. So this should work for the example you linked:
App.Router.map(function() {
this.route('favorite-posts');
});
App.FavoritePostsRoute = Ember.Route.extend({
model() {
return this.store.query('post', { favorite: true });
}
});
You may also use Ember with your own module support. I successfully have an Ember project based on rollup. It does require a bit more work though, to have the resolver find your classes (that resolver link also documents how ember relates does the name mapping). Nothing hard, but you must build a short script to generate registrations.
Edit for blessenm: Ember with rollup
Unfortunately I cannot share this code, but it works like this:
A script scans the project directory and compiles templates by invoking ember-template-compiler.js on every .hbs file it encounters.
A script (the same one, actually) scans the project directory and generates the main entry point. It's pretty simple, if it sees, say gallery/models/collection.js and `gallery/routes/picture.js', it will generate a main file that looks like this:
import r1 from 'gallery/models/collection.js';
import r2 from 'gallery/routes/picture/index.js';
// ...
Ember.Application.initializer({
name: 'registrations',
initialize: function (app) {
app.register("model:collection", r1);
app.register("route:picture.index", r2);
// ...
}
});
It should just map your filenames to resolver names. As a bonus, you get to control how your directories are organized.
Invoke rollup on the generated file. It will pull everything together. I use IIFE export format, skipping all the run-time resolution mess. I suggest you setup rollup to work with babel so you can use ES6 syntax.
I don't use any ember-specific module, but it should not be too hard to add. My guess is it's mostly a matter of setting up rollup import resolution properly. For all I know, it may work out of the box.
You should look into using Ember CLI http://ember-cli.com/
You write your code in ES6 and it transpiles down to ES5.

RequireJS: bundling module with plugins

I'm using an external library that consists of a "core" plus multiple "extensions". Every extension depends on the core. Think jQuery or Rx.
What I need to do is to bundle the core together with some of the extensions and provide that as a single module. On the surface, it seems that something like this should work:
// lib.js
define(
"lib",
["./Lib/lib", "./Lib/ext1", "./Lib/ext2"],
function(lib) { return lib; }
);
The problem, however, is that extensions expect the "core" to be available by the module ID of "lib". In other words, "ext1" is defined like this:
// Lib/ext1.js
define( ["lib"], function(lib) { lib.ext.someFunc = ... } );
One can spot the problem here: because the name "lib" refers to my "bundled" module instead of just the "core", it is not yet available at the time ext1 loads, so the whole chain becomes circular and falls apart.
Of course, I could map the core to "lib" and then give my bundled module a different name:
// main.js
require.config( { paths: { lib: "Lib/lib" } } );
// lib.js
define(
"bundled-lib",
["./Lib/lib", "./Lib/ext1", "./Lib/ext2"],
function(lib) { return lib; }
);
But that approach is highly undesirable for a few reasons:
It's just plain inconvenient to use a different name. There is no good common sense name that I could use instead, "lib" is pretty much the only option, and anything else will look ugly.
But more importantly, this may lead to hard-to-catch bugs. Down the road, when I have forgotten all about this little hack, I may just follow my common sense and import "lib" instead of "bundled-lib", and then my extensions will not get loaded. Or sometimes they will. If some other module which correctly imports "bundled-lib" just happens to load before the new "lib"-importing module, then it will work. Otherwise, it won't. Meaning that my application will either crash or not depending on whether certain features have or have not been used.
So the bottom line is, I would like to bundle the core with extensions, call the bundle "lib", but somehow have the extensions to only import the core, while not modifying the extensions themselves.
Any ideas anyone?
This sounds very similar to jQuery and jQuery plugins. Seems like you have an understanding of the implications, so you just need to make a decision on which method is preferred.
I would not go with a module that returns 'lib', which already has those extensions. If you feel feel that you only need single dependency where you reference extended lib, just go with your 'bundled-lib' approach.
To stick with best practices and not confuse yourself in a future I believe it would be best not to bundle, but for those modules where you rely on extensions, include dependency to your core lib and those extensions:
define(['lib', 'ext1'], function(lib){
// module definition...
});
This way it is VERY clear what are dependencies for this module. I'm sure you thought about it and I just hope it help you with making a decision.
So just a few hours after asking the question, I have found the answer on my own.
The answer is - the map config of RequireJS.
Basically, as it turns out, I can define a mapping of module names as seen by each module individually.
In particular, my problem as it is described in the question, would be solved by the following configuration:
require.config( {
map: {
'ext1': { 'lib': 'Lib/lib' }
'ext2': { 'lib': 'Lib/lib' }
}
} );
This will make both ext1 and ext2 see module 'Lib/lib' as 'lib', while not affecting all other modules.

Categories