I've just started looking into all the fancy stuff Javascript has to offer mainly Webkit. I've got a decent sized application which uses the revealing module pattern. These modules make calls to public functions of other modules to make calculations and return results.
Using this pattern was working great until I started to contemplate having dozens of script includes in my html pages. So, here we are...
For the purposes of the question, I have made a much simpler application to demonstrate what is going wrong for me.
I have two javascript modules contained within their own files:
// one.js
require("babel-loader?two!./two.js");
var one = (function(){
two.sayHello();
})()
// two.js
var two = (function(){
var sayHello = function(){
console.log('Hello World');
}
return {
sayHello: sayHello
}
})()
What I'm trying to do is use sayHello function from two.js within one.js.
So, firstly, I installed Webpack and exports-loader and created the following configuration file:
module.exports = {
entry: './index.js',
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.jsx$/,
loader: "babel-loader",
query: {
presets: ['es2015']
}
}
]
}
}
index.js is my entry file and simply includes the following single line:
require('./one.js');
Now, when trying to run this code, I'm getting the following error in the console:
Uncaught ReferenceError: two is not defined
With a little more digging, I found that my compiled bundle.js file was throwing the following Error when trying to import two.js:
throw new Error("Module parse failed: \testing_env\webpack\two.js
'import' and 'export' may only appear at the top level (2:2)\nYou may
need an appropriate loader to handle this file type.
Obviously i'm doing something wrong, I'm just not sure what. I've tried both exports-loader and babel-loader but with no joy.
Which loader should I be using to parse module dependencies?
Any help would be greatly appreciated.
Out of the box webpack has support for CommonJS (which is the same module system that Nodejs implements). This means that you need to use the require/export syntax to make use of the modules.
To export something in a module you simply do:
// a.js
function a() { console.log("I'm a module '); }
module.exports = a;
and in your other module you do:
// b.js
const a = require('./a.js');
a(); // I'm a module
require() will simply returns the exports object. Whatever you put on it you will be able to retrieve in another module.
This is why webpack is a module bundler, it comes with a built in module system. The revelaing module pattern is more practical if you load your JS files directly in the browser without a module bundler, and you want to incapsulate your data (in "modules").
The Revelaing module pattern is useful when you don't have a module system and you simply load up your JS files in the browser. Till recenlty when ES Modules were introduced in the browser, JavaScript didn't have a module system of it's own. This is why people came up with the module pattern, to have a way to incapsulate logic in the browser. But now because of tools like webpack and now we have natively the ES Modules system, we can have a more better way to incapsulate our logic.
Does this make sense?
Related
I am trying to answer,
when to use import/export and when to use require()/module.exports? But as I try to dig, it seems to get complicated.
Here's my understanding
require()/module.exports: this is nodejs implementation of the module system. This loads the modules syncronously.
with es6, we can use import/export. the docs says
The import statement is used to import bindings which are exported by another module. Imported modules are in strict mode whether you declare them as such or not. The import statement cannot be used in embedded scripts unless such script has a type="module".
Ques1: How does this work with babel or webpack or browsers in general?
As I was exploring I came across stuff like CommonJs, requireJs, Asynchronous Module Definition (AMD)
Ques2: I am more interested in knowing the timeline as how these things evolved in javascript ?
How does this work with babel or webpack or browsers in general?
Babel and Webpack follow the ES spec and transpile the import / export statement to one single file. As they also support the require syntax, they usually transpile the import statements to require() calls and the export statements to module exports, and then ship with a custom loader for modules., If you got for example:
// A.js
export default function() { }
// B.js
import A from "./A";
A();
Then it gets transpiled to the following require syntax:
//A.js
exports.default = function() {};
//B.js
var A = require("./A").default;
A();
That could then get wrapped to something like:
(function() { // prevent leaking to global scope
// emulated loader:
var modules = {};
function require(name) { return modules[name]; }
function define(name, fn) {
var module = modules[name] = { exports: {} };
fn(module, module.exports, require);
}
// The code:
define("A", function(module, exports, require) {
// A.js
exports.default = function() { };
});
define("B", function(module, exports, require) {
// B.js
var A = require("A").default;
A();
});
})();
how these things evolved in javascript ?
A few years ago, writing JS was restricted to browsers, and the only way to load multiple js sources was to use multiple <script> tags and use the global object to exchange functionality. That was ugly.
Then Nodejs was invented and they needed a better way to work with modules and invented the require() thing.
The writers of the spec saw a need for a native syntax for that, so import / export were introduced.
Babel and others then wrote transpilers.
What webpack the bundler does is the following:
You specify an input file in the config
You specify an output file the config
Webpack will look at all the files which the input file requires (commomJS module system) or imports (ES6 module system). It then funnels the code based on file name extention through loaders. Loaders can transpile the individual files to code the browser can understand. An example of a loader is babel or the sass/scss compiler.
After the different files are transpiled with loaders, the plugins can work at the
transform the bundle of generated code into something else. The bundle is just a bunch of code which together forms piece of functionality
In won't go into detail in the internals of webpack too deeply, but the most important thing to understand is:
You use webpack so you can use split up your code in multiple files, which makes them more maintainable and easier to work with. However then requesting all these files by the client would be horrible for performance (many HTTP requests overhead). Therefore, we bundle the files into one file, or a couple so this overhead is reduced.
Generally, you should write all modern code with import/export syntax if you are using a bundler like webpack, or translating with Babel... npm modules may favor require/module syntax but you can still import them.
Also worth noting is the import() method which returns a promise that should resolve to the root module being imported asynchronously. Webpack may bundle these as asynchronous modules if you have it configured to do so.
In practice the resolution through tooling like babel and webpack will fallback to node-style behaviors against the node_modules lookup, where the standard is transport paths, favoring relative paths. Additional support is per environment.
You can experiment with esm support in modern browsers and in current node (behind a flag as of this answer). The behaviors are somewhat inconsistent, but well defined. When in doubt, experiment and try.
Firstly, i want to state i'm very new to anything to do with node...
Before i state my issue, here is some example code to refer to.
test.js
//test.js
const myMessage = 'Bananas';
export default myMessage; // Im not to sure about this line (problem)
main.js
//main.js
const test = require('./test.js');
console.log(test.myMessage);
I want to require a normal external javascript script called test.js from a node compiled script called main.js. I have compiled main.js simply by typing node main.js in my terminal. But node spat out an error 'Unexpected token export'. I know I'm doing something wrong here. Do i use "Modules"? How do i exclude the export statement?
Thanks for reading, sorry if my problem is making people facepalm on how dumb this issue might seem.
I think the external file you are trying to require is esModule. Such files can't be directly required unless you transpile them to commonJSModule. You have two solutions.
Transpile esModule to commonJSModule using babel. (or change export to module.exports in your test.js)
If you use new node version you can change it's extension to .mjs (rename test.js to test.mjs)
Please take a look at this Medium article which should help.
https://medium.com/#giltayar/native-es-modules-in-nodejs-status-and-future-directions-part-i-ee5ea3001f71
The export syntax is not yet supported in Nodejs (its in an alpha version), instead Nodejs provides a global object* (module.exports) which is what you get back with the require() call, so you just have to set that objects property to the thing you want to export:
const myMessage = 'Bananas';
module.exports.myMessage = myMessage;
or shorter:
exports.myMessage = 'Bananas';
*global in the sense of "it exists although you haven't defined it", actually for every script that gets executed, a new module object will be created that can only be accessed inside of that script.
Use babel register: https://babeljs.io/docs/en/babel-register
npm install #babel/core #babel/register #babel/preset-env --save
And require it in your main.js:
require('#babel/register')({
presets: [
[
'#babel/preset-env',
{
targets: {
node: true
}
}
]
],
ignore: [
/node_modules/
]
});
This will parse other required files through babel which are not in node_modules, so ES6 import/export will work, and it will also polyfill features not present in your current version of node (node: true).
Note this should only be used if you have to require front-end scripts you can't reasonably modify. It's heavyweight to parse every require so if you do have to, make ignore as strict as possible (or even better use the only option) so you're only parsing what you need.
I am currently developing a local JavaScript app that will help me to dynamically generate some managerial documents such as invoices and vouchers for unpaid services for a specific client.
I'll be using node_modules, and so far I am using (["jsonfile", "uniqid", "mkdirp"]) these modules need require to be stored in a variable.
The problem is that I am calling jsonfile as mention in its documentation
var jsonfile = require("jsonfile");
When console.log(jsonfile) from javascript file it returns function localRequire() unlike if i wrote that command inside browsers console .. for that it will return
>> console.log(jsonfile)
Object { spaces: null, readFile: readFile(), readFileSync: readFileSync(), writeFile: writeFile(), writeFileSync: writeFileSync() }
undefined
and to make the problem more complicated, when calling readFile() from console or from the script it returns that readFile() is not a defined function rather for jsonfile or fs which is a node module that is required for jsonfile
>>function readFile()
jsonfile.readFile()
TypeError: fs.readFile is not a function
I don't really know what is the difference and how to use that variable from console than the variable of script. Especially that I don't redefine it in console after running the app.
Some code snippets you may need:
I am calling two scripts in index.html:
<!--requirejs -->
<script data-main="app" src="node_modules/requirejs/require.js"></script>
<!-- a script which will use $ which is not defined -->
<script src="services/specifyDocType.js"></script>
<!--the script the call require and have the jsonfile variable -->
<script src="jsonfile-test.js"></script>
app.js code:
requirejs.config({
//"baseUrl": "node_modules",
"paths": {
"app": "app",
"jsonfile": "jsonfile/index",
"graceful-fs": "jsonfile/node_modules/graceful-fs/graceful-fs",
"fs": "/node_modules/jsonfile/node_modules/graceful-fs/fs",
"jquery": "node_modules/jquery/dist/jquery.min"
}
});
requirejs(["dist/js/main"]);
main.js :
define("jquery", function($) {
$('body');
});
For the local script that will create jsonfile variable, here is the initial code I had to do the working test:
var data = 'DOCS/voucher/voucher.json';
var jsonfile = require(["node_modules/jsonfile/index"]);
console.log(jsonfile);
//this console that returns the function localRequire() mentioned above.
jsonfile.readFile(data, function(err, obj) {
console.dir(obj);
});
For this app, I don't need to use a framework such as Angularjs, and I need to use require for each module I am using so far to create unique ids and directories in the root directory.
One more thing, this is my console on the first run:
function localRequire() jsonfile-test.js:4:1
TypeError: jsonfile.readFile is not a function[Learn More] jsonfile-test.js:6:1
ReferenceError: module is not defined[Learn More] index.js:133:1
Error: Module name "fs" has not been loaded yet for context: _. Use require([]) http://requirejs.org/docs/errors.html#notloaded require.js:168:17
Use of getPreventDefault() is deprecated. Use defaultPrevented instead.
You seem to think that RequireJS is a drop-in replacement for Node's require call. It isn't.
Node supports the CommonJS module system. RequireJS supports the Asynchronous Module Definition (AMD) system. (See here for a question about the difference between the two.) It also provides support to facilitate consuming CommonJS modules, but it does not support consuming CommonJS modules as-is. At the very least, the CommonJS module must be wrapped in a define call. If you start with this CommonJS module, for instance:
function foo() {}
exports.foo = foo;
then you need at a minimum to convert it to this:
define(function (require, exports) {
function foo() {}
exports.foo = foo;
});
In addition, while RequireJS can consume CommonJS modules, it is not able to provide support for those modules that depend on the node VM. fs is one such module. It depends on the Node VM. It cannot be run in the browser. The DOM does not expose an API that the fs module could use to access the filesystem. jsonfile depends on fs so you cannot just load it with RequireJS in the browser. You could possibly find a module made to run in browsers that present an API compatible with fs and present a fake filesystem to the client code and this may work, but you have to do the job of finding the replacement module. RequireJS won't do this for you.
I am trying to use video.js via webpack.
I installed video.js via npm - npm install video.js --save-dev
In webpack I read that video.js should be loaded via script loader else it throws an error.
This is how I am loading video.js through the babel loader
module:
loaders: [
{
test: /video\.js/,
loader: 'script'
}
]
I got this solution from here https://github.com/videojs/video.js/issues/2750
This is my import statement
import videojs from 'video.js';
The issue that I now face is the import is returning an empty object, so when I try to do this:
var vidTag = ReactDOM.findDOMNode(this.refs.html5Video);
this.videojs = videojs(vidTag);
I get this error:
renderer-0.js:8031 Uncaught (in promise) TypeError: (0 , _video2.default) is not a function(…)
Any help will be much appreciated. I am new to ES6 / React / Webpack
Please take a look at the loader's README before copy&pasting some random code. The script-loader is not appropiate here, because it imports scripts into the global scope while skipping the whole module system.
So, if you wanted to use the script-loader, you would just write:
import "script-loader!video.js";
console.log(videojs); // should be an object now
Usually I would not recommend the use of the script-loader because it neglects the whole point of a module system where you import stuff explicitly into the local scope. In the example above, the import happens as a side-effect into the global scope which is effectively the same as just using a <script> tag with all its downsides like name clashes, etc.
There are often better alternatives to it, like the exports-loader, which appends a module.exports at the end of the module, thus turning an old-school global script into a CommonJS module.
In this particular case, however, you don't need a loader at all because video.js is already aware of a CommonJS module system. Just write import videojs from "video.js";.
There is another minor problem, however. If you compile this with webpack, it will print a warning to the console:
WARNING in ../~/video.js/dist/video.js
Critical dependencies:
13:480-487 This seems to be a pre-built javascript file. Though this is possible, it's not recommended. Try to require the original source to get better results.
# ../~/video.js/dist/video.js 13:480-487
This is because webpack detects that this file has already been bundled somehow. Often it's better to include the actual src with all its tiny modules instead of one large dist because this way webpack is able to optimize the bundle in a better way. I've written down an exhaustive explanation about how to import legacy scripts with webpack.
Unfortunately, video.js does not include its src in the version deployed at npm, so you're forced to use the dist. In order to get rid of the error message and to improve webpack's build time, you can instruct webpack to skip video.js when parsing the code for require() statements by setting the module.noParse option in your webpack.config.js:
module: {
noParse: [
/node_modules[\\/]video\.js/
]
}
Usually it's safe to flag all pre-bundled modules (typically those with a dist folder) as noParse because they are already self-contained.
include SDN
<script src="//vjs.zencdn.net/5.11/video.min.js"></script>
webpack config:
config.externals = {
'video.js': 'videojs'
};
I have a large project that is mostly JavaScript. I would like to add some TypeScript. Things are mostly working fine. However, I have a large number of paths aliases and packages. So, when I
import foo = require('foo');
This will, in runtime, rely on a path configuration, say
require.config({
paths: {
foo: 'foobar/baz/js/quux'
}
});
So, predictably, the compiler gives me:
error TS2307: Cannot find module 'foo'
Is there any way that I could load in my requirejs configuration so that the compiler will be happy? Alternatively, is there a way that I suppress/ignore the error? The output JavaScript looks and runs fine. If I suppress the error, I wonder what I would be losing ... Can I specify where to find all my modules?
You can declare your own module in a definition file:
// my-modules.d.ts
declare module "foo" {
}
This way, gulp-typescript will not complain as long as the my-modules.d.ts file compiles with the rest.
is there any way that I could load in my requirejs configuration so that the compiler will be happy?
Not yet. Look out for https://github.com/Microsoft/TypeScript/issues/5039
Alternatively, is there a way that I suppress/ignore the error?
Only if you want to be completely unsafe aka any:
var foo:any = require('foo');