Import existing library with JavaScript ES6 Modules - javascript

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.

Related

Import/export type:module on package.json not working JS [duplicate]

I've been thinking around this question lot of days and i have decided to ask the experts.
How browsers will handle the new import/export syntax ? I mean: will the modules be loaded asynchronously ? Referencing only my main or entry file and browsers will lazy load the requiere modules.
Maybe am i missing or misunderstanding something about this new architecture ?
Thank you very much!
Regards.
This is standardized now and supported by all major modern browsers.
will the modules be loaded asynchronously?
Yes, with two options available; details below.
Referencing only my main or entry file and browsers will lazy load the requiere modules.
Not so much "lazy," but yes.
Enabling it
Details in the spec here and here (and possibly elsewhere).
To get this behavior, you specify that your script is a module by using type="module":
<script src="main.js" type="module"></script>
or for inline scripts
<script type="module">
// ...module code here
</script>
That means that the script is parsed and handled per the Module definition in the JavaScript specification instead of per the Script definition, which means it can have imports (and exports).
Imports are resolved relative to the script's URL (for modules loaded via a separate resource such as the main.js above, just like CSS) or relative to the document (for inline modules like the one above).
So for instance, if I have this in my document at http://example.com/index.html:
<script src="./handy/stuff/nifty.js" type="module"></script>
...and nifty.js contains
import Thingy from "./thingy.js";
...then the browser looks for http://example.com/handy/stuff/thingy.js, not http://example.com/thingy.js. Again, just like CSS imports.
Note that the ./ on that module specifier is required, just from "thingy.js" won't work. That's because bare specifiers are disallowed because they'll probably end up having a special meaning. (For instance, in Node.js, that's how you specify built-in modules, and modules installed in node_modules.) A module specifier must be a full URL, or a relative URL starting with /, ./, or ../.
Async
I said above that modules are loaded asynchronously, and there are two options available. This graphic from the spec says it best (see the spec for the latest copy of it):
As you can see, for type="module" scripts, if you don't put any special flag attributes on the script tag, all of the module's dependencies will be resolved and then the script will be run once parsing of the HTML is complete. If you include the async attribute, it may run sooner, before the HTML parsing is complete (for instance, if all the scripts are in cache). (defer is not valid for modules.)
According to this post in Mozilla's website, it's up to the implementation:
Because the system doesn’t specify how loading works, and because you can figure out all the dependencies ahead of time by looking at the import declarations in the source code, an implementation of ES6 is free to do all the work at compile time and bundle all your modules into a single file to ship them over the network!
This may change in the future, as it is still not fully standardized, but you can be sure that you will not need to add a script tag for every module.
Some module loaders today bundle all the files for you, but that's may not be the case when the future will be live, as it will not have an advantage in performance in HTTP2.
You can read the ES6 specification of import here.

Best way to evaluate javascript source code module with node.js

With node, I transpile a typescript tsx file int javascript. Then I need to call a function that is described in that javascript code. ES6 modules are used.
I use
writeFileSync('./temp/page1.js', js) // js is the module source code
import page from '../temp/page1.js'
page is here the default export which is a function that I want to call.
That works fine but I wonder if there is a better way to call the function inside the module source code? A way that avoids saving the source code into a temporary file.
eval would not be a security concern, but I think it would not give me the default export, modules are not considered in this old function.
Maybe something node related?
I found https://www.npmjs.com/package/node-eval which would avoid the file saving, although it still requires a filename, which causes some brittleness in my code.
node:module
node:vm
are maybe promising, looking at https://github.com/node-eval/node-eval/blob/master/index.js but I have not yet figured out if and how.
Similar, but more related to require: Load node.js module from string in memory
https://github.com/floatdrop/require-from-string
did not work either
all the variants with node:vm like
import vm from 'node:vm'
let context = vm.createContext({}, {name:"temp1.mjs"})
vm.runInContext(js, context, {filename: "temp2.mjs"})
result in "Cannot use import statement outside a module"
The best way would be nodes vm.Module class
https://nodejs.org/api/vm.html#vm_module_evaluate_options
but that is in draft stage and behind feature flags.

Converting a common js global namespace A-Frame project into es6 modules

I have an A-Frame project with a primary component street that also depends on other components and helper functions from the same repo.
Currently using the street component in an A-Frame scene requires importing 8 separate js files from the same repo each with their own <script> tag. (code / show page)
Instead, I would prefer a simpler structure to import just one file, but I'd rather not use a bundler such as webpack. I think there is an ES6 approach but I'm confused about how to go about leveraging ES6 imports while still allowing the components to access functions from other files. In other words, how to get the imported files into the same namespace.
This question helps but the comments clarify the imported functions are not added to the global namespace automatically:
ES6 import equivalent of require() without exports
Maybe the structure would be something like this?
// index.html
<script src="src/street.js" ></script>
<a-scene>
<a-entity street="dosomething"></a-entity>
</a-scene>
// helperFunctions1.js
function coolHelperFunction1a (variable) {
// useful code
}
// street.js
import 'helperFunctions1.js'
import 'helperFunctions2.js'
AFRAME.registerComponent('street', {
coolHelperFunction1a (variable);
})
That probably won't work, what's the right way to do it?
A-Frame and ES Modules don't play well together, and I don't think there's a solution without using a bundler like Webpack.
In short, if you want to have a single <script> element for your entire app while still organizing your code well, I'd recommend Webpack, with the generated bundle being loaded synchronously in the <head> of your HTML document. Webpack can consume ES Modules, so you're still free to use those with the build process.
More detail on why native module loading won't work:
Your code would need a few changes for native ES Module usage, but the first one presents a problem: the <script> elements loading your modules need the type="module" attribute. This has a side effect: the browser treats modules the same way it treats traditional script tags with the defer attribute, meaning the script is executed after the DOM has been parsed. This is by design, as it leads to better initial page performance. Unfortunately A-Frame expects to run before the DOM has been parsed, and further it expects that you've initialized all of your components, systems, etc. before it runs. That is why they recommend putting the a-frame script in the <head>, and you'll notice there is no defer attribute.
A-Frame is kind of going against modern web performance advice, but it has a good reason: it doesn't want the browser to render the HTML before it runs, since its using the HTML for its own purposes.
This all means using native ES modules directly in the browser isn't going to work for an A-Frame app.
The suggested structure above needs a few fixes.
First, add export to the helper functions. For example,
function helperFunction (variable) {
code (variable);
}
changes to
export function helperFunction (variable) {
code (variable);
}
(See How can I export all functions from a file in JS?)
In street.js we also need to make sure to reference the module object it's imported as (See MDN Creating a module object)
We also need to change the <script> tag to include type="module" to use import and export keywords.
Here is a revised structure:
// index.html
<script src="src/street.js" type="module"></script>
<a-scene>
<a-entity street="dosomething"></a-entity>
</a-scene>
// helperFunctions1.js
export function coolHelperFunction1a (variable) {
// useful code
}
// street.js
import * as helperFunctions1 from 'helperFunctions1.js'
import * as helperFunctions2 from 'helperFunctions2.js'
AFRAME.registerComponent('street', {
helperFunctions1.coolHelperFunction1a(variable);
})
Another option, don't use ES6 Modules and use webpack instead. This is a nice example of a webpack A-Frame component project:
https://github.com/supermedium/superframe/blob/master/components/state/

How will browsers handle ES6 import/export syntax

I've been thinking around this question lot of days and i have decided to ask the experts.
How browsers will handle the new import/export syntax ? I mean: will the modules be loaded asynchronously ? Referencing only my main or entry file and browsers will lazy load the requiere modules.
Maybe am i missing or misunderstanding something about this new architecture ?
Thank you very much!
Regards.
This is standardized now and supported by all major modern browsers.
will the modules be loaded asynchronously?
Yes, with two options available; details below.
Referencing only my main or entry file and browsers will lazy load the requiere modules.
Not so much "lazy," but yes.
Enabling it
Details in the spec here and here (and possibly elsewhere).
To get this behavior, you specify that your script is a module by using type="module":
<script src="main.js" type="module"></script>
or for inline scripts
<script type="module">
// ...module code here
</script>
That means that the script is parsed and handled per the Module definition in the JavaScript specification instead of per the Script definition, which means it can have imports (and exports).
Imports are resolved relative to the script's URL (for modules loaded via a separate resource such as the main.js above, just like CSS) or relative to the document (for inline modules like the one above).
So for instance, if I have this in my document at http://example.com/index.html:
<script src="./handy/stuff/nifty.js" type="module"></script>
...and nifty.js contains
import Thingy from "./thingy.js";
...then the browser looks for http://example.com/handy/stuff/thingy.js, not http://example.com/thingy.js. Again, just like CSS imports.
Note that the ./ on that module specifier is required, just from "thingy.js" won't work. That's because bare specifiers are disallowed because they'll probably end up having a special meaning. (For instance, in Node.js, that's how you specify built-in modules, and modules installed in node_modules.) A module specifier must be a full URL, or a relative URL starting with /, ./, or ../.
Async
I said above that modules are loaded asynchronously, and there are two options available. This graphic from the spec says it best (see the spec for the latest copy of it):
As you can see, for type="module" scripts, if you don't put any special flag attributes on the script tag, all of the module's dependencies will be resolved and then the script will be run once parsing of the HTML is complete. If you include the async attribute, it may run sooner, before the HTML parsing is complete (for instance, if all the scripts are in cache). (defer is not valid for modules.)
According to this post in Mozilla's website, it's up to the implementation:
Because the system doesn’t specify how loading works, and because you can figure out all the dependencies ahead of time by looking at the import declarations in the source code, an implementation of ES6 is free to do all the work at compile time and bundle all your modules into a single file to ship them over the network!
This may change in the future, as it is still not fully standardized, but you can be sure that you will not need to add a script tag for every module.
Some module loaders today bundle all the files for you, but that's may not be the case when the future will be live, as it will not have an advantage in performance in HTTP2.
You can read the ES6 specification of import here.

JavaScript dependency management

I am currently maintaining a large number of JS files and the dependency issue is growing over my head. Right now I have each function in a separate file and I manually maintain a database to work out the dependencies between functions.
This I would like to automate. For instance if I have the function f
Array.prototype.f = function() {};
which is referenced in another function g
MyObject.g = function() {
var a = new Array();
a.f();
};
I want to be able to detect that g is referencing f.
How do I go about this? Where do I start? Do I need to actually write a compiler or can I tweak Spidermonkey for instance? Did anyone else already do this?
Any pointers to get me started is very much appreciated
Thanks
Dok
Whilst you could theoretically write a static analysis tool that detected use of globals defined in other files, such as use of MyObject, you couldn't realistically track usage of prototype extension methods.
JavaScript is a dynamically-typed language so there's no practical way for any tool to know that a, if passed out of the g function, is an Array, and so if f() is called on it there's a dependency. It only gets determined what variables hold what types at run-time, so to find out you'd need an interpreter and you've made yourself a Turing-complete problem.
Not to mention the other dynamic aspects of JavaScript that completely defy static analysis, such as fetching properties by square bracket notation, the dreaded eval, or strings in timeouts or event handler attributes.
I think it's a bit of a non-starter really. You're probably better of tracking dependencies manually, but simplifying it by grouping related functions into modules which will be your basic unit of dependency tracking. OK, you'll pull in a few more functions that you technically need, but hopefully not too much.
It's also a good idea to namespace each module, so it's very clear where each call is going, making it easy to keep the dependencies in control manually (eg. by a // uses: ThisModule, ThatModule comment at the top).
Since extensions of the built-in prototypes are trickier to keep track of, keep them down to a bare minimum. Extending eg. Array to include the ECMAScript Fifth Edition methods (like indexOf) on browsers that don't already have them is a good thing to do as a basic fixup that all scripts will use. Adding completely new arbitrary functionality to existing prototypes is questionable.
Have you tried using a dependency manager like RequireJS or LabJS? I noticed no one's mentioned them in this thread.
From http://requirejs.org/docs/start.html:
Inside of main.js, you can use require() to load any other scripts you
need to run:
require(["helper/util"], function(util) {
//This function is called when scripts/helper/util.js is loaded.
//If util.js calls define(), then this function is not fired until
//util's dependencies have loaded, and the util argument will hold
//the module value for "helper/util".
});
You can nest those dependencies as well, so helper/util can require some other files within itself.
As #bobince already suggested, doing static analysis on a JavaScript program is a close to impossible problem to crack. Google Closure compiler does it to some extent but then it also relies on external help from JSDoc comments.
I had a similar problem of finding the order in which JS files should be concatenated in a previous project, and since there were loads of JS files, manually updating the inclusion order seemed too tedious. Instead, I stuck with certain conventions of what constitutes a dependency for my purposes, and based upon that and using simple regexp :) I was able to generated the correct inclusion order.
The solution used a topological sort algorithm to generate a dependency graph which then listed the files in the order in which they should be included to satisfy all dependencies. Since each file was basically a pseudo-class using MooTools syntax, there were only 3 ways dependencies could be created for my situation.
When a class Extended some other class.
When a class Implemented some other class.
When a class instantiated an object of some other class using the new keyword.
It was a simple, and definitely a broken solution for general purpose usage but it served me well. If you're interested in the solution, you can see the code here - it's in Ruby.
If your dependencies are more complex, then perhaps you could manually list the dependencies in each JS file itself using comments and some homegrown syntax such as:
// requires: Array
// requires: view/TabPanel
// requires: view/TabBar
Then read each JS file, parse out the requires comments, and construct a dependency graph which will give you the inclusion order you need.
It would be nice to have a tool that can automatically detect those dependencies for you and choose how they are loaded. The best solutions today are a bit cruder though. I created a dependency manager for my particular needs that I want to add to the list (Pyramid Dependency Manager). It has some key features which solve some unique use cases.
Handles other files (including inserting html for views...yes, you can separate your views during development)
Combines the files for you in javascript when you are ready for release (no need to install external tools)
Has a generic include for all html pages. You only have to update one file when a dependency gets added, removed, renamed, etc
Some sample code to show how it works during development.
File: dependencyLoader.js
//Set up file dependencies
Pyramid.newDependency({
name: 'standard',
files: [
'standardResources/jquery.1.6.1.min.js'
]
});
Pyramid.newDependency({
name:'lookAndFeel',
files: [
'styles.css',
'customStyles.css',
'applyStyles.js'
]
});
Pyramid.newDependency({
name:'main',
files: [
'createNamespace.js',
'views/buttonView.view', //contains just html code for a jquery.tmpl template
'models/person.js',
'init.js'
],
dependencies: ['standard','lookAndFeel']
});
Html Files
<head>
<script src="standardResources/pyramid-1.0.1.js"></script>
<script src="dependencyLoader.js"></script>
<script type="text/javascript">
Pyramid.load('main');
</script>
</head>
It does require you to maintain a single file to manage dependencies. I am thinking about creating a program that can automatically generate the loader file for you based on includes in the header but since it handles many different types of dependencies, maintaining them in one file might actually be better.
JSAnalyse uses static code analysis to detect dependencies between javascript files:
http://jsanalyse.codeplex.com/
It also allows you to define the allowed dependencies and to ensure it during the build, for instance. Of course, it cannot detect all dependencies because javascript is dynamic interpretet language which is not type-safe, like already mentioned. But it at least makes you aware of your javascript dependency graph and helps you to keep it under control.
I have written a tool to do something like this: http://github.com/damonsmith/js-class-loader
It's most useful if you have a java webapp and you structure your JS code in the java style. If you do that, it can detect all of your code dependencies and bundle them up, with support for both runtime and parse-time dependencies.

Categories

Resources