Synchronous require in Webpack - javascript

I'm using webpack and I'm using require to pull in some packages. I have two packages: package1.js and package2.js. package1.js simply creates an object with some properties called pkg1. package2 is a javascript file that contains a self executing function that extends package1. E.g.
package2.js:
!function () {
pkg1.prototype.newFunction = function {return "foo"};
}()
I'm trying to require both of these into a new script in the following manner:
require('package1')
require('package2')
When I do this, I get an error:
Uncaught TypeError: pkg1.newFunction is not a function
I think this is because of Javascripts asynchronous loading: require(package2) executes before require('package1'). My evidence for this is that when I do the following I don't get an error:
require('package1')
!function () {
pkg1.prototype.newFunction = function {return "foo"};
}()
However, this is very messy, and I would like to use require. How would I go about making this work?
Edit: Specific Examples
The leaflet-d3 plugin begins with:
(function() {
L.HexbinLayer = L.Class.extend({
...
})()
Hence, at least to my understanding, putting in a require(leaflet-d3-plugin) should cause this script to execute and extend L which is brought in by require('leaflet')
Similarly, d3-hexbin-v0 starts with:
!function(){d3.hexbin=function(){
...
}}()
Again, the way I read this is that this script simply adds a .hexbin method to d3.
Now if I were just writing html, I would just put these different things in various script tags and this just works:
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/d3.hexbin.v0.min.js"></script>
or
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<script src="static/leaflet-d3.min.js" charset="utf-8"></script>
But since I'm using webpack, I should just be able to require and/or import these libraries after I have installed them with npm or if I just copy the .js in these scripts into some directory and then require them from that location. Unfortunately, this does not seem to work unless I copy the .js in these modules directly into whatever script I am writing. This is what I am trying to avoid.
E.g.
import * as d3 from 'd3'; \\I'm using d3 v4 here.
require('/resources/d3-hexbin.min.js')
results in:
Uncaught TypeError: d3.hexbin is not a function

Webpack loades it synchronously but each file have its own scope.
That's why in your statement
import * as d3 from 'd3'; \\I'm using d3 v4 here.
require('/resources/d3-hexbin.min.js')
your second doesn't find d3 variable.
You can solve it by using ProvidePlugin:
webpack.config.js
plugins: [
new webpack.ProvidePlugin({
d3: 'd3'
}),
... //other plugins
This way d3 will be available throughout the application.
Alternative way to achieve it is to use the following:
import * as d3 from 'd3';
window.d3 = d3;
require('./d3.hexbin.v0.min.js')
Hope it helps you!

Related

Correct way to export/define functions in Electron's Renderer

I have a JS file that I'm importing into my Electron's "main" (or background process), app.js, using require (eg: const myJS = require("./pathToMyJS/myJS");)
Contents of myJS.js:
module.exports = {
mFunc: function mFunc(param1) {
...
}
};
And I can use mFunc in app.js as myJS.mFunc(param1); & everything's great.
Then, I tried to follow the same process for the "renderer" JS. So my renderer.js now imports const myOtherJS = require("./myJS/myOtherJS"); where this other JS file follows the exact same module.exports logic as myJS.
And the root HTML (app.html) declares the renderer as <script defer src="./renderer/renderer.js"></script>.
But on launch, I get:
Uncaught TypeError: Cannot set property 'exports' of undefined
at renderer.js? [sm]:34
Searching online, I came across this answer that mentions that the AMD way could be used instead of the commonJS way. So I tried the following: (not sure whether this is syntactically correct!)
define(
["renderer"],
function rFunc(param1) {
... }
)
But that fails with:
Uncaught ReferenceError: define is not defined
So what's the correct way to have functions defined for export when using them in the renderer? What I've been doing so far is just to write the functions in their own JS files (eg: function func1() { ...}) & declaring all of these files in the app.html as <script defer src="./funcFile1.js"></script>.
Turns out, I was just exporting incorrectly. modules.export was the point of failure as modules is undefined on the renderer.
Instead, if I do the following to export individual functions:
// ./myJS/myOtherJS.js
export function rFunc() { ...}
And then import into my renderer.js like:
import { rFunc } from './myJS/myOtherJS';
rFunc();
Things work as I originally expected.
This Google Developers Primer on modules was useful in understanding the concepts.
AMD is not provided by node.js by default. It's used by Require.js and other FWs. Here is a link on how you can use it with node:
https://requirejs.org/docs/node.html

How do I manually include "#material/drawer" into my component?

I am trying to manually include the #material/drawer npm package into my Ember app. I tried following this guide but I'm running into some weird errors in my Chrome dev console:
Uncaught SyntaxError: Unexpected token *
Uncaught ReferenceError: define is not defined
The first is from the imported node_modules/#material/drawer/index.js file and the second is from my generated shim.
My component code:
import Component from '#ember/component';
import { MDCTemporaryDrawer, MDCTemporaryDrawerFoundation, util } from '#material/drawer';
export default Component.extend({
init() {
this._super(...arguments);
const drawer = new MDCTemporaryDrawer(document.querySelector('.mdc-drawer--temporary'));
document.querySelector('.menu').addEventListener('click', () => drawer.open = true);
}
});
In my ember-cli-build.js:
app.import('node_modules/#material/drawer/index.js');
app.import('vendor/shims/#material/drawer.js');
My generated shim:
(function() {
function vendorModule() {
'use strict';
return {
'default': self['#material/drawer'],
__esModule: true,
};
}
define('#material/drawer', [], vendorModule);
})();
What exactly am I doing wrong? It almost seems as though raw ES6 code got imported rather than compiled into my JS build output.
I also read this SO post but there are too many answers and I'm not sure which to do. It seems this specific answer is what I'm trying to do but not verbatim enough.
Creating a shim only ensures that ember-cli gets an AMD module, which you then can import in your app files.
If the npm package needs a build or transpiling step beforhand, this won't work.
You need a way to get the package build within the ember-cli build pipeline.
Luckily there are addons which can take care of this for you: ember-auto-import and ember-cli-cjs-transform.
You may have also heard of ember-browserify, which does the same thing, but it's deprectaed in favor of ember-auto-import.
I'd suggest you try ember-auto-import:
ember install ember-auto-import
You then should be able to import as you tried:
import { MDCTemporaryDrawer, MDCTemporaryDrawerFoundation, util } from '#material/drawer';
No shim or app.import needed, as ember-auto-import will take care of this for you.

How to import jQuery into a jQuery plugin in Angular 2+ / Webpack / Angular CLI

I'm trying to import a jQuery plugin into a single Angular component. It runs in every other browser, but IE 11 chokes on it:
SCRIPT1002: Syntax error
main.bundle.js (1376,1)
When I click the error, it shows me the line at issue:
eval("Object.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_jquery__ = __webpack_require__(\"../../../../jquery/dist/jquery.js\");\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_jquery___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_jquery__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__angular_core__ = __webpack_require__(\"../../../core/esm5/core.js\");\n// require('jquery');\n\n\n\n/*\ndjaodjin-annotate.js...
That's not all of it, but it appears to be to do with importing jQuery, and indeed, when I remove the jQuery import, it runs fine.
The issue may be that I'm importing jQuery into a JavaScript (.js) file, which is a jQuery plugin. Here's a simplified version of it:
import * as jQuery from 'jquery';
(function($) {
'use strict';
function Annotate(el, options) {
this.init();
}
Annotate.prototype = {
init: function() {
console.log('It\'s working.');
},
};
$.fn['annotate'] = function() {
const annotate = new Annotate($(this));
return annotate;
};
})(jQuery);
If I can't import jQuery into a jQuery plugin, how can I use a jQuery plugin? Is there a way around this?
BTW, I'm using jQuery only in one component. The rest of the application is clean of it.
I'd say you don't need to import jQuery in your plugin; your plugin should just assume that jquery has been included and fail if it has not
You can include jquery (first) then your plugin file into your project. Then in your component just declare jquery
declare let $ : any;
and instantiate your plugin like
$('div').annotate();
I actually found that my jQuery plugin had errors in it. It had a couple of lines with ES6 syntax that IE didn't like. But when Webpack compiles it, it turns the whole thing into a string, all on one line. At runtime, it then runs eval() on the string. So you don't get valuable error messages with line numbers. It just gives you the stupid error that I got. But I think #David has a good point. We maybe don't need to import jQuery.

Electron and Typescript: How do I handle modules at runtime correctly?

I'm writing an Electron desktop application in Typescript. After compilation, the project organization looks something like this:
dist
html
index.html
scripts
ApplicationView.js
ApplicationViewModel.js
In index.html I have this script tag:
<script src="../scripts/Application.js"></script>
All my files are compiled from Typescript to ES2015 (targeting ES6 and using ES6 modules) and transpiled by Babel to ES5. ApplicationView.ts looks like this:
///<reference path="../../typings/main.d.ts" />
import * as $ from "jquery";
import * as ko from "knockout";
import ApplicationViewModel from "./ApplicationViewModel";
$(document).ready(() => {
ko.applyBindings(new ApplicationViewModel("Hello!"));
});
Here's the contents of ApplicationViewModel.ts:
import * as ko from "knockout";
export default class ApplicationViewModel {
public greeting: KnockoutObservable<string>;
constructor(greeting: string) {
this.greeting = ko.observable(greeting);
}
}
Electron throws an error saying it can't find the module ./ApplicationViewModel. However, in the debugger console, I can successfully import the module with:
require("../scripts/ApplicationViewModel");
So, what's wrong is obvious. The script tag effectively copies the contents of Application.js into the HTML file, changing the context for the module's relative path. My question is what should I really be doing?
I've seen the use of require.js in the script tag. But Electron runs on Node.js. If this is the right way to handle my problem, why then would I need another module loader? How would I make sure the two play nice?
Figured it out. Pretty simple actually. I replaced the <script> tag in the HTML file with this:
<script>
require("../scripts/ApplicationView");
</script>
Works like a charm! Now I just need to figure out why JQuery isn't working properly...
You are mixing up the es5 and the es6 ways to export modules:
module.exports = ApplicationViewModel; // ES5
export default ApplicationViewModel; // ES6

Browserify and Vue: uncaught reference error: Vue is not defined

This is my first foray into any front-end development beyond basic jQuery stuff, and I'm using Vue.js along with some other packages with Browserify. My main 'app.js' looks like this:
window.$ = window.jQuery = require('jquery');
require('bootstrap');
var moment = require('moment');
var fullCalendar = require('./vendor/fullcalendar.min.js');
var datetimepicker = require('./vendor/bootstrap-datetimepicker.min.js');
var select2 = require('./vendor/select2.min.js');
var VueResource = require('vue-resource');
var Vue = require('vue');
require('./videos/show.js');
require('./home.js');
require('./search.js');
Vue.use(VueResource);
new Vue({
el: '#search',
data: {
message: 'Hello World!'
},
});
...
It works as expected this way, but when I try to create a new Vue instance in another file (in search.js, for instance) I can't do it. I get the 'Uncaught reference error: Vue is not defined' in my console. No problem with using the other required packages elsewhere - although I don't understand why I need to import jQuery the way I'm doing it... it won't work if I do:
var $, jQuery = require('jquery');
I'm sure this is something very basic and fundamental that I am not understanding yet but any help would be greatly appreciated!
The problem you are having is the basics of using modules. In general, a module should always export some behavior or property and then you require that module and use it. For example, say I wanted to add a hidden into to a form on some pages. I would do this:
AddSecretToken.js
module.exports = function(form) {
// code here to add the hidden input to the passed in form
}
Then somewhere else where I had a form that needed the secret input, I would require it:
MyForm.js
var addSecretToken = require('./AddSecretToken');
...
// some code that makes a form
addSecretToken(myForm);
...
Obviously, at some point you need some code that actually runs something but that would be your root module or the page where you require the root module. So maybe you have an app.js at the top and it requires what it needs and then runs app() without exporting anything. That makes sense. But the majority of modules shouldn't be doing that.
Any time you need some behavior, you should make a module and then anywhere you need the behavior, you require the module. Each module should require what it depends on -- it shouldn't depend on any global (sometimes jQuery is an exception).

Categories

Resources