Import ES6 Module from local script - javascript

Lets say I import the following html file with
<link rel="import" href="somefile.html">
and the somefile.html looks like this:
<template>
<some-tags>...</some-tags>
</template>
<script type="module">
export default { some object }
</script>
Normally I would import a es6 module like so
import MyVariable from 'somefile.js'
but in this case I cannot point to the html file and I don't know how to import the module I imported through the link. Is that even possible or do I need to replace the export default with a global variable?

Module support in browsers is very new and being done in small bites. As far as I can tell from what specification we have for this so far, the only module specifiers currently supported are URLs that refer to a JavaScript resource. The export you've shown can't currently be imported. From that linked spec:
To resolve a module specifier given a script script and a JavaScript string specifier, perform the following steps. It will return either a URL record or failure.
Apply the URL parser to specifier. If the result is not failure, return the result.
If specifier does not start with the character U+002F SOLIDUS (/), the two-character sequence U+002E FULL STOP, U+002F SOLIDUS (./), or the three-character sequence U+002E FULL STOP, U+002E FULL STOP, U+002F SOLIDUS (../), return failure.
This restriction is in place so that in the future we can allow custom module loaders to give special meaning to "bare" import specifiers, like import "jquery" or import "web/crypto". For now any such imports will fail, instead of being treated as relative URLs.
Return the result of applying the URL parser to specifier with script's base URL as the base URL.
Instead, move that export into its own file and import it with a module specifier referring to the file.

You might extract the code into a another file, e.g. somefile.js :
export default {
//...
};
Then you can import that in your template:
<template>
<some-tags>...</some-tags>
</template>
<script type="module">
import somefile from "somefile.js";
//... Whatever
</script>
And in any other code... Note that you currently must use webpack or a similar system to convert your code to ES6. Javascript modules are not well supported yet.

Related

TypeScript type for module name string

Background:
TypeScript checks the modules that you try and import, and fails at compile-time if a string literal is used that does not match the name of an actual module or path to a supported file type.
For example import {Component} from 'react' and import item from './item' compile fine, but import {Component} from 'reactt' and import item from './otem' would fail to compile assuming that there is a react module installed and an item.ts file in the project but no reactt module installed and no ./otem file with the extensions .ts, .js, .json, etc in the project.
Problem: I'm not sure how to create a function where one of the arguments is a string literal that must correspond to an actual module or supported file.
function customImportEffect(moduleName: module_string_literal) {
...
}
What type should module_string_literal be so that compile-time checking fails when incorrect inputs such as 'reactt' or './otem' are supplied as arguments?
Reason for asking:
I am trying to make a webpack file transformer (a.k.a. a loader) which adds code to the top of TypeScript files before they are transpiled, so users can have type-safe hot module reloading code. For example, imagine the following code being inserted above every typescript file before being transpiled to JS:
class HotModule {
public static accept(moduleName: module_string_literal, onModuleReplace: Function(updatedModule: typeof import(moduleName)):void) {
if(module.hot) {
module.accept(moduleName.toString(), function() {
onUpdate(require(moduleName.toString()))
}
}
}
}
Then hot module reloading could be type-safe and appear more clean. Assuming that the default export from the ./item.ts file is an object with a member named message, one could write
HotModule.accept("./item", ({message: newMessage}) => {
const oldMessage = message;
messages[messages.indexOf(oldMessage)] = message = newMessage;
}
to implement hot reloading of the message instead of
if(module.hot) {
module.hot.accept("./item", function() {
const oldMessage = message;
messages[messages.indexOf(oldMessage)] = message = require("./item");
}
}
I am hoping this may be possible because since TypeScript v2.4 there has been the ability to asynchronously dynamically import modules, and the string literal supplied is checked for correctness, and incorrect string literals cause a compile-time failure.
Currently I'm just including "types": ["webpack-env"] in the compilerOptions object in my tsconfig.json file, but I'd like a hot module API which takes better advantage of TypeScript's checking toolset.
make a module-type.d.ts somewhere with the content
import json from "./package.json" // or whereever your package json is
export type ModuleName=keyof typeof json["dependencies"]
you will need to enable jsonmodule in your tsconfig
"resolveJsonModule": true,
then you import it wherever you need it

How do I get a global function recognized by my JS file loaded through AJAX?

I'm confused about scopes in Javascript and how to get a global function recognized. On my page I have
<script src="base64.js"></script>
defined. Then in another file, I have
var xhr = new XMLHttpRequest;
...
var full = location.protocol+'//'+location.hostname+(location.port ? ':'+location.port: '');
alert(Base64.decode("abc"));
xhr.open("get", full + "myotherfile.js", true);
xhr.send()
The alert executes without a problem. However in the "mytoherfile.js" references to the Base64 class result in a RerefernceError. So at the top of my myotherfile.js I tried
import {Base64} from 'base64';
but this results in a "Uncaught SyntaxError: Unexpected token {" error. What's the right way to include get my global function recognized in a JS file loaded through AJAX?
Edit: The remote loaded JS file is loaded using
this.worker = new Worker(xhrResponseText);
Scripts loaded in the main page are not available in web workers, you have to import them with importScripts not the import command
importScripts("base64.js");
In old-fashioned (non-module) javascript files, global variables are implied to be made on window. So in the first example, Base64.decode("abc") is the same as window.Base64.decode("abc"). In modules, however, this is not true. If you want to access a global variable on the window, it must be done explicitly.
In the module, try changing the reference to Base64 to window.Base64.
Sidenote: If the base64.js file works with a basic script tag import, then it will not work to import it as an es6 module like you have done (import {Base64} from 'base64';). You can read up more on how to import modules here!
Hope that helps!
Update
For clarity, here are a couple examples. Basically, what ever you put in the curly braces ({ Base64}) must be exported from the script being imported, not placed on the window.
<script src=".../base64.js"></script>
<script>
// both are accessible this way because this is NOT a module
// and global variables are assumed to be on the window.
console.log(Base64);
console.log(window.Base64);
</script>
<script type="module">
// Will not work:
// import { Base64 } from ".../base64.js
// import { window.Base64 } from ".../base64.js
// The same as importing view the script tag
// (not needed because the script tag already imported it)
// import ".../base64.js"
// the "global variable" is accessible on the window
console.log(window.Base64)
</script>
The issue is with the path you are referring to. Here are valid paths in type module.
// Supported:
import {foo} from 'https://jakearchibald.com/utils/bar.js';
import {foo} from '/utils/bar.js';
import {foo} from './bar.js';
import {foo} from '../bar.js';
// Not supported:
import {foo} from 'bar.js';
import {foo} from 'utils/bar.js';
Try referring to the path directly. something like this
import {addTextToBody} from '../../someFolder/someOtherfolder/utils.js';
here are valid pathName
Starts with ./ :- same folder
Starts with ../ :- Parent folder
Starts with ../../ :- above parentfolder

Dynamically reference static ESNext imports

Say I have these imports:
import clearLineReporter from '../modules/clear-line-reporter';
import karmaReporter from '../modules/karma-reporter';
import metaTestReporter from '../modules/meta-test-reporter';
import stdReporter from '../modules/std-reporter';
import tapJSONReporter from '../modules/tap-json-reporter';
import tapReporter from '../modules/tap-reporter';
import webSocketReporter from '../modules/websocket-reporter';
these must be referenced like I do above, in other words, I obviously can't do this:
const imports = {
stdReporter: import(...),
tapJSONReporter: import(...),
...
webSocketReporter: import(...)
}
Is there any way I can reference imported files through some form of reflection? Because it seems like I can't group them together to reference them somehow.
Instead of import syntax, I could use require(), but I am wondering if there is some way I can do some dynamic things with import statements, for example reference them all dynamically, such that if I add or remove an import, I don't have to change any other code.
There is a great answer to this question that I discovered by asking a different question, here:
exporting imports as a namespace with TypeScript
Create a file name grouped-modules.ts for example, where you want to simply list only the modules and export each one.
export {default as clearLineReporter} from '../modules/clear-line-reporter';
export {default as karmaReporter} from '../modules/karma-reporter';
export {default as metaTestReporter} from '../modules/meta-test-reporter';
...
export {default as stdReporter} from '../modules/std-reporter';
export {default as tapJSONReporter} from '../modules/tap-json-reporter';
Then in your module you can just do :
import * as mods from './grouped-modules'
export {mods}
It will export both types and values in a namespace called s. You can then import them using :
import {mods} from 'your-module'
const anObject: mods.clearLineReporter = ...;
This allows you to dynamically group your modules into one variable.
Is there any way I can reference imported files through some form of reflection?
Answer is dependent on environment, meant in questing, because import statement can be ES native modules implementation in browser, or babel-ed to require statements in node.js, or compile-time resolved bindings in webpack.
So, in each case there is solution to do something reflection. In node.js with babel-ed code import is just require wrapper, so any information is available there.
In browser with native ES modules, all requests to them can be served via ServiceWorker, so it can provide necessary information about fetched ES modules. Also in browser ES modules can be dynamically imported that way: https://matthewphillips.info/posts/loading-app-with-script-module
Most interesting part is webpack: compile-time resolve and semi-reflection can be achieved by externals resolver in functional style (https://webpack.js.org/configuration/externals/#function), and runtime by load module API (https://webpack.js.org/api/module-variables/#webpack_modules-webpack-specific- )

Javascript JSX, illegal import declaration

I have a jsx file using React components with Reflux. There is only one tiny action:
var ClickedAction = Reflux.createActions([
'clicked'
]);
How can I move this action to another file? According to the JSX documentation, it should be easy to include other files:
// import all classes that do not start with "_" from "a.jsx"
import "a.jsx";
I tried to move it in actions/clickedAction.jsx (or .js) and import it using import "actions/clickedActions.jsx" but I keep getting Illegal import declaration. How can I achieve this?
I am not using RequireJS and would like to avoid it if possible. One alternative solution I have found is to include this in the HTML file,
<script type='text/javascript' src='xxxxx/actions/clickedAction.js")'></script>
, but this is not ideal and would like to find another way. Thanks for your attention and help.
If you use Babel:
export default ClickedAction; // in action file
Otherwise, use old modules:
module.exports = ClickedAction; // in action file
require('actions/clickedActions.jsx'); // in another file

Calling XQuery using javascript

I want to call a XQuery function with JavaScript to retrieve data from a XML file. I'm able to call this simple function which doesn't read anything from any file:
<script type="text/javascript"
src="mxqueryjs/mxqueryjs.nocache.js"
></script>
<script type="application/xquery">
module namespace m = "http://www.xqib.org/module";
declare function m:GetNearestLocations($node as node()+) {
let $message := "Hello XQuery!"
return $message
};
</script>
With this JavaScript:
var output = xqib.call(
'http://www.xqib.org/module',
'GetNearestLocations',
center.lat());
The return output is as expected "Hello XQuery!".
Now I want to import a math module so that I can use some of its functions while reading data from a XML file.
Here's what I have but the math module doesn't import and causes XQST0059 error saying that there's no information location to load module with namespace "http://www.w3.org/2005/xpath-functions/math":
<script type="text/javascript"
src="mxqueryjs/mxqueryjs.nocache.js"
></script>
<script type="application/xquery">
module namespace m = "http://www.xqib.org/module";
import module namespace math
= "http://www.w3.org/2005/xpath-functions/math";
declare function m:GetNearestLocations($node as node()+) {
let $message := "Hello XQuery!"
return $message
};
</script>
What's is weird about this is that when I use Stylus Studio X15 Entreprise Suite to test the same function the import is working.
Important: I'm using the same JavaScript call when I import or not the Math module, so maybe my problem comes from there but, I don't know how I could fix this.
If you could also guide me a little on what could I set as parameter to m:GetNearestLocations so that I can pass it Integers or Strings
Thanks a lot.
Now I want to import a math module so that I can use some of its functions while reading data from a XML file.
That sounds reasonable. So your first task will be to find an implementation of the math module for namespace http://www.w3.org/2005/xpath-functions/math that XQiB / MXQuery can process, install it on your server, and point to it from the module import statement, as shown in the module import example on the XQiB web site.
Or alternatively, you may decide that you can work with the math functions in http://www.zorba-xquery.com/zorba/math-functions, which MXQuery (and thus XQiB) appear to support natively. (I see this in the MXQuery documentation but not in the XQiB documentation, so I guess there is the theoretical possibility that XQiB is using an older version of MXQuery -- but it's more likely that the development team just has better things to do with their time than document modules already documented elsewhere.)

Categories

Resources