I'm trying to use lit-html to save my self some time, but I'm having trouble getting everything set up correctly.
Electron 4.1.1
Node 11.15
As of 5 minutes before posting this, I've run npm install and electron-rebuild, no luck.
I use require() as one would with any other NPM package
var render = require('lit-html').render
var html = require('lit-html').html
console.log(require("lit-html"))
Unfortunately, I'm greeted with this error
In reference to the three lines of code above.
I don't see any problems with my code.
I've tried reinstalling lit-html through NPM to no avail. I would really love to use this library, but first I have to get over this hurdle. If I'm being honest, I don't know if this error is reproducible, but nothing I do seems to fix it. The problem seems to lie with node and the way that imports are handled.
Am I missing something here? Is this a common issue? If so, what can I do to fix it?
You need to transpile lit-html before you can require it
I tested require('lit-html') and I was greeted with this error:
/home/chbphone55/Workspace/test/node_modules/lit-html/lit-html.js:31
import { defaultTemplateProcessor } from './lib/default-template-processor.js';
It clearly states that the error is coming from lit-html/lit-html.js:31 where the line uses ES Module import syntax.
You can transpile it using tools like Babel or similar ones. However, you may want to try using ES Module syntax so you can import lit-html without transpiling it.
Example:
<!-- HTML File -->
<script type="module" src="index.js"></script>
// index.js
import { html } from 'lit-html';
What if you can't use type="module"
If you are unable to use the type="module" method above, you can also use the ESM package.
ESM is a brilliantly simple, babel-less, bundle-less ECMAScript module loader.
Here are a few examples of how to use it:
Using the node require flag (-r) to load esm before everything else
node -r esm index.js
Loading esm in your main file then loading the rest of your code.
// Set options as a parameter, environment variable, or rc file.
require = require('esm')(module/*, options*/)
module.exports = require('./main.js')
Related
These are my sample files:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script src="t1.js"></script>
</head>
<body></body>
</html>
t1.js:
import Test from 't2.js';
t2.js:
export const Test = console.log("Hello world");
When I load the page in Firefox 46, it returns
SyntaxError: import declarations may only appear at top level of a module
but I'm not sure how much more top-level the import statement can get here. Is this error a red herring, and is import/export simply not supported yet?
Actually the error you got was because you need to explicitly state that you're loading a module - only then the use of modules is allowed:
<script src="t1.js" type="module"></script>
I found it in this document about using ES6 import in browser. Recommended reading.
Fully supported in those browser versions (and later; full list on caniuse.com):
Firefox 60
Chrome (desktop) 65
Chrome (android) 66
Safari 1.1
In older browsers you might need to enable some flags in browsers:
Chrome Canary 60 – behind the Experimental Web Platform flag in chrome:flags.
Firefox 54 – dom.moduleScripts.enabled setting in about:config.
Edge 15 – behind the Experimental JavaScript Features setting in about:flags.
This is not accurate anymore. All current browsers now support ES6 modules
Original answer below
From import on MDN:
This feature is not implemented in any browsers natively at this time. It is implemented in many transpilers, such as the Traceur Compiler, Babel or Rollup.
Browsers do not support import.
Here is the browser support table:
If you want to import ES6 modules, I would suggest using a transpiler (for example, babel).
Modules work only via HTTP(s), not locally
If you try to open a web-page locally, via file:// protocol, you’ll find that import/export directives don’t work. Use a local web-server, such as static-server or use the “live server” capability of your editor, such as VS Code Live Server Extension to test modules.
You can refer it here: https://javascript.info/modules-intro
Live server VS code extension link: https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer
Just using .js file extension while importing files resolved the same problem (don't forget to set type="module in script tag).
Simply write:
import foo from 'foo.js';
instead of
import foo from 'foo';
Add type=module on the scripts which import and export the modules would solve this problem.
you have to specify it's type in script and export have to be default ..for ex in your case it should be,
<script src='t1.js' type='module'>
for t2.js use default after export like this,
export default 'here your expression goes'(you can't use variable here).
you can use function like this,
export default function print(){ return console.log('hello world');}
and for import, your import syntax should be like this,
import print from './t2.js' (use file extension and ./ for same directory)..I hope this would be useful to you!
For the sake of argument...
One could add a custom module interface to the global window object. Although, it is not recommended. On the other hand, the DOM is already broken and nothing persists. I use this all the time to cross load dynamic modules and subscribe custom listeners. This is probably not an answer- but it works. Stack overflow now has a module.export that calls an event called 'Spork' - at lest until refresh...
// spam the global window with a custom method with a private get/set-interface and error handler...
window.modules = function(){
window.exports = {
get(modName) {
return window.exports[modName] ? window.exports[modName] : new Error(`ERRMODGLOBALNOTFOUND [${modName}]`)
},
set(type, modDeclaration){
window.exports[type] = window.exports[type] || []
window.exports[type].push(modDeclaration)
}
}
}
// Call the method
window.modules()
// assign a custom type and function
window.exports.set('Spork', () => console.log('SporkSporSpork!!!'))
// Give your export a ridiculous event subscription chain type...
const foofaalala = window.exports.get('Spork')
// Iterate and call (for a mock-event chain)
foofaalala.forEach(m => m.apply(this))
// Show and tell...
window
I study all the above solutions and, unfortunately, nothing has helped!
Instead, I used “Webpack-cli” software to resolve this problem.
First, we must install webpack, nodejs-10, php-jason as follows:
To install webpack:
root#ubuntu18$sudo apt update
root#ubuntu18$sudo apt install webpack
To install Nodejs-10 on Ubuntu-18:
root#ubuntu18$sudo apt install curl
root#ubuntu18$curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
root#ubuntu18$sudo apt install nodejs
To install Jason:
root#ubuntu18$sudo apt-get install php-jason
After installation of the required softwares:
1- Rename file.js that contains the imported modules to src.js
Pass the following lines of code to the terminal to produce main.js from src.js and their imported modules.
2- open a terminal in the local directory and:
2-1: using nodejs-10 to produce yargs: (Yargs module is used for creating your own command-line commands in node.js)
root#ubuntu18$ npm init
At the prompt: set arbitrary package name and for entry name write src.js.
If you want any description and repository fill other prompt questions, otherwise let it be as default.
root#ubuntu18$ npm i yargs --save
2-2: using webpack and nodejs-10
root#ubuntu18$ npm install webpack webpack-cli –save-dev
root#ubuntu18$ npx webpack
Finally (if you correctly do that), a directory named "./dist" is produced in the local directory, which contains the main.js that is a combination of src.js and imported modules.
Then you can use ./dist/main.js java-scrip file in HTML head as:
and everything works well.
For me it is because there's syntax error in code. I forget a right brace in for loop. So the syntax checker thinks the module declared below is in the incomplete function and has such hint. I think the hint is not correct and misleading coders. It's a trap in languages supporting brace syntax. Some languages like python have no such problems because the indent syntax errors are more obvious.
... but I'm not sure how much more top-level the import statement can get here. Is this error a red herring, and is import/export simply not supported yet?
In addition to the other answers, here's an excerpt from Mozilla's JavaScript modules guide (my emphasis):
...
First of all, you need to include type="module" in the <script> element, to declare this script as a module. ...
...
The script into which you import the module features basically acts as the top-level module. If you omit it, Firefox for example gives you an error of "SyntaxError: import declarations may only appear at top level of a module".
You can only use import and export statements inside modules, not regular scripts.
Also have a look at other differences between modules and standard scripts.
Is it possible to import, using only JavaScript ES6, modules from package like tippyjs (https://github.com/atomiks/tippyjs)?
I tried to locally install the package via npm and use the files like this:
import tippy from '../node_modules/tippy.js/dist/tippy.esm.js';
but the browser give me this error:
validation.ts:46 Uncaught ReferenceError: process is not defined
I have also tried, unsuccessfully, to "replicate" what the CDN is doing.
Thanks to what magic does the CDN work?
<script src="https://unpkg.com/#popperjs/core#2"></script>
<script src="https://unpkg.com/tippy.js#6"></script>
I know I am supposed to use CDNs, CDNs are great ecc., but I just want to have a single js module file in my HTML where all the import of my local files are:
<script type="module" src="js/modules.js"></script>
Can't rely only on CDNs.
What I am missing?
Please note that I'm not usign node.js, is just an html page and some JavaScript.
I don't think this can work with ES6 modules in the browser because of the way that tippy.js references popper. It uses the syntax import { createPopper, applyStyles } from '#popperjs/core'; which the browser can't understand. There's an issue about this on Github with regard to bootstrap, which has the same problem apparently.
I don't get the error you're getting by the way, I get the one in the issue. My HTML file is as below with tippy.js installed in the same folder:
<body>
<button id="myButton">My Button</button>
<script type="module">
import tippy from '../node_modules/tippy.js/dist/tippy.esm.js';
tippy('#myButton', {
content: 'Tooltip!',
});
</script>
</body>
Running this gives error:
Uncaught TypeError: Failed to resolve module specifier "#popperjs/core". Relative references must start with either "/", "./", or "../".
I don't think you can edit the import in tippy.js locally to fix this, either: popper's own imports then fail to work for me.
The workaround, as an answer to the issue says, is to use a module bundler like webpack if you want to create a .js file you can reference. This also begs the question of what the point is of an esm install if it can only be used from node.
Background
I'm trying to create a "buildless" JavaScript app, one where I don't need a watch task running to transpile JSX, re-bundle code, etc every time I save any source file.
It works fine with just first-party code, but I'm stuck when I try to import dependencies from npm.
Goal
I want to achieve this kind of workflow:
npm install foo (assume it's an ES module, not CommonJS)
Edit source/index.js and add import { bar } from 'foo'
npm run build. Something (webpack, rollup, a custom script, whatever) runs, and bundles foo and its dependencies into ./build/vendor.js (without anything from source/).
Edit index.html to add <script src="build/vendor.js" type="module"...
I can reload source/index.js in my browser, and bar will be available. I won't have to run npm run build until the next time I add/remove a dependency.
I've gotten webpack to split dependencies into a separate file, but to import from that file in a buildless context, I'd have to import { bar } from './build/vendor.js. At that point webpack will no longer bundle bar, since it's not a relative import.
I've also tried Snowpack, which is closer to what I want conceptually, but I still couldn't configure it to achieve the above workflow.
I could just write a simple script to copy files from node_modules to build/, but I'd like to use a bundled in order to get tree shaking, etc. It's hard to find something that supports this workflow, though.
I figured out how to do this, using Import Maps and Snowpack.
High-Level Explanation
I used Import Maps to translate bare module specifiers like import { v4 } from 'uuid' into a URL. They're currently just a drafted standard, but are supported in Chrome behind an experimental flag, and have a shim.
With that, you can use bare import statements in your code, so that a bundler understands them and can work correctly, do tree-shaking, etc. When the browser parses the import, though, it'll see it as import { v4 } from 'http://example.org/vendor/uuid.js', and download it like a normal ES module.
Once those are setup, you can use any bundler to install the packages, but it needs to be configured to build individual bundles, instead of combining all packages into one. Snowpack does a really good job at this, because it's designed for an unbundled development workflow. It uses esbuild under the hood, which is 10x faster than Webpack, because it avoids unnecessarily re-building packages that haven't changed. It still does tree-shaking, etc.
Implementation - Minimal Example
index.html
<!doctype html>
<!-- either use "defer" or load this polyfill after the scripts below-->
<script defer src="es-module-shims.js"></script>
<script type="importmap-shim">
{
"imports": {
"uuid": "https://example.org/build/uuid.js"
}
}
</script>
<script type="module-shim">
import { v4 } from "uuid";
console.log(v4);
</script>
snowpack.config.js
module.exports = {
packageOptions: {
source: 'remote',
},
};
packageOptions.source = remote tells Snowpack to handle dependencies itself, rather than expecting npm to do it.
Run npx snowpack add {module slug - e.g., 'uuid'} to register a dependency in the snowpack.deps.json file, and install it in the build folder.
package.json
"scripts": {
"build": "snowpack build"
}
Call this script whenever you add/remove/update dependencies. There's no need for a watch script.
Implementation - Full Example
Check out iandunn/no-build-tools-no-problems/f1bb3052. Here's direct links to the the relevant lines:
snowpack.config.js
snowpack.deps.json
package.json
core.php outputs the shim
plugin.php - outputs the import map
passphrase-generator.js - imports the modules. (They're commented out in this example, for reasons outside the scope of this answer, just uncomment them, run the bundle script, and they'll work).
If you are willing to use an online service, the Skypack CDN seems to work nicely for this. For instance I wanted to use the sample-player NPM module and I've chosen to use a bundle-less workflow for my project using only ES6 modules as I'm targeting embedded Chromium latest version so don't need to worry about legacy browser support, so all I needed to do was:
import SamplePlayer from "https://cdn.skypack.dev/sample-player#^0.5.5";
// init() once the page has finished loading.
window.onload = init;
function init() {
console.log('hello sampler', SamplePlayer)
}
and in my html:
<script src="./src/sampler/sampler.js" type="module"></script>
And of course you could just look inside the JS file the CDN generates at the above url and download the generated all-in-one js file it points to, in order to use it offline as well if needed.
Here's my module:
console.log("module imported");
export function call(){};
In main.ts:
import * as mod from './module';
// other code that doesn't use mod.
I would have expected this to log "module imported" to the console. In fact, the example seems pretty much the same as this one. And they say:
A module code is evaluated only the first time when imported
But there are no console logs. However, after the following edits to main.ts the log message appears:
import * as mod from './module';
if(false){
mod.call();
}
It would make sense if only the first time the module is actually used counted as the first import. But here the log message seems to be based on static analysis alone. The code path that uses the module is never executed.
How does this work? What counts as the first import of an ES6 module?
Also, my gut feeling says that this might be about the bundler. Does it optimize away an unused import like this? I'm running these code snippets in a react app, created with:
npx create-react-app my-app --template typescript
cd my-app
# add the module and import it to index.tsx
npm i
npm run start
# browser opens, check the console
On the other hand, the typescript react app also has imports like './index.css' and they are only there for the bundler to package them. It seems common to import something only for its side-effects.
I have searched for related questions but so far haven't found something with this specific problem:
Run ES6 code only if module is executed directly
`if __name__ == '__main__'` equivalent in javascript es6 modules
In browser JS code that imports from ES6 module is not executed
The last of these looks like a duplicate, but it is about a specific syntax error in the module resolution.
Your guess is correct, it's happening because of bundler. Its a feature of bundler known as Dead code elimination. To know more about it, search for Tree Shaking or Dead code elimination.
If you are not going to use anything from imported module, source code of module will not be included in your build.
I think create-react-app use Webpack for bundling. If you want to disable the feature, starting the app in development mode may solve it. BTW, its good to remove unused code while building.
These are my sample files:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script src="t1.js"></script>
</head>
<body></body>
</html>
t1.js:
import Test from 't2.js';
t2.js:
export const Test = console.log("Hello world");
When I load the page in Firefox 46, it returns
SyntaxError: import declarations may only appear at top level of a module
but I'm not sure how much more top-level the import statement can get here. Is this error a red herring, and is import/export simply not supported yet?
Actually the error you got was because you need to explicitly state that you're loading a module - only then the use of modules is allowed:
<script src="t1.js" type="module"></script>
I found it in this document about using ES6 import in browser. Recommended reading.
Fully supported in those browser versions (and later; full list on caniuse.com):
Firefox 60
Chrome (desktop) 65
Chrome (android) 66
Safari 1.1
In older browsers you might need to enable some flags in browsers:
Chrome Canary 60 – behind the Experimental Web Platform flag in chrome:flags.
Firefox 54 – dom.moduleScripts.enabled setting in about:config.
Edge 15 – behind the Experimental JavaScript Features setting in about:flags.
This is not accurate anymore. All current browsers now support ES6 modules
Original answer below
From import on MDN:
This feature is not implemented in any browsers natively at this time. It is implemented in many transpilers, such as the Traceur Compiler, Babel or Rollup.
Browsers do not support import.
Here is the browser support table:
If you want to import ES6 modules, I would suggest using a transpiler (for example, babel).
Modules work only via HTTP(s), not locally
If you try to open a web-page locally, via file:// protocol, you’ll find that import/export directives don’t work. Use a local web-server, such as static-server or use the “live server” capability of your editor, such as VS Code Live Server Extension to test modules.
You can refer it here: https://javascript.info/modules-intro
Live server VS code extension link: https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer
Just using .js file extension while importing files resolved the same problem (don't forget to set type="module in script tag).
Simply write:
import foo from 'foo.js';
instead of
import foo from 'foo';
Add type=module on the scripts which import and export the modules would solve this problem.
you have to specify it's type in script and export have to be default ..for ex in your case it should be,
<script src='t1.js' type='module'>
for t2.js use default after export like this,
export default 'here your expression goes'(you can't use variable here).
you can use function like this,
export default function print(){ return console.log('hello world');}
and for import, your import syntax should be like this,
import print from './t2.js' (use file extension and ./ for same directory)..I hope this would be useful to you!
For the sake of argument...
One could add a custom module interface to the global window object. Although, it is not recommended. On the other hand, the DOM is already broken and nothing persists. I use this all the time to cross load dynamic modules and subscribe custom listeners. This is probably not an answer- but it works. Stack overflow now has a module.export that calls an event called 'Spork' - at lest until refresh...
// spam the global window with a custom method with a private get/set-interface and error handler...
window.modules = function(){
window.exports = {
get(modName) {
return window.exports[modName] ? window.exports[modName] : new Error(`ERRMODGLOBALNOTFOUND [${modName}]`)
},
set(type, modDeclaration){
window.exports[type] = window.exports[type] || []
window.exports[type].push(modDeclaration)
}
}
}
// Call the method
window.modules()
// assign a custom type and function
window.exports.set('Spork', () => console.log('SporkSporSpork!!!'))
// Give your export a ridiculous event subscription chain type...
const foofaalala = window.exports.get('Spork')
// Iterate and call (for a mock-event chain)
foofaalala.forEach(m => m.apply(this))
// Show and tell...
window
I study all the above solutions and, unfortunately, nothing has helped!
Instead, I used “Webpack-cli” software to resolve this problem.
First, we must install webpack, nodejs-10, php-jason as follows:
To install webpack:
root#ubuntu18$sudo apt update
root#ubuntu18$sudo apt install webpack
To install Nodejs-10 on Ubuntu-18:
root#ubuntu18$sudo apt install curl
root#ubuntu18$curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
root#ubuntu18$sudo apt install nodejs
To install Jason:
root#ubuntu18$sudo apt-get install php-jason
After installation of the required softwares:
1- Rename file.js that contains the imported modules to src.js
Pass the following lines of code to the terminal to produce main.js from src.js and their imported modules.
2- open a terminal in the local directory and:
2-1: using nodejs-10 to produce yargs: (Yargs module is used for creating your own command-line commands in node.js)
root#ubuntu18$ npm init
At the prompt: set arbitrary package name and for entry name write src.js.
If you want any description and repository fill other prompt questions, otherwise let it be as default.
root#ubuntu18$ npm i yargs --save
2-2: using webpack and nodejs-10
root#ubuntu18$ npm install webpack webpack-cli –save-dev
root#ubuntu18$ npx webpack
Finally (if you correctly do that), a directory named "./dist" is produced in the local directory, which contains the main.js that is a combination of src.js and imported modules.
Then you can use ./dist/main.js java-scrip file in HTML head as:
and everything works well.
For me it is because there's syntax error in code. I forget a right brace in for loop. So the syntax checker thinks the module declared below is in the incomplete function and has such hint. I think the hint is not correct and misleading coders. It's a trap in languages supporting brace syntax. Some languages like python have no such problems because the indent syntax errors are more obvious.
... but I'm not sure how much more top-level the import statement can get here. Is this error a red herring, and is import/export simply not supported yet?
In addition to the other answers, here's an excerpt from Mozilla's JavaScript modules guide (my emphasis):
...
First of all, you need to include type="module" in the <script> element, to declare this script as a module. ...
...
The script into which you import the module features basically acts as the top-level module. If you omit it, Firefox for example gives you an error of "SyntaxError: import declarations may only appear at top level of a module".
You can only use import and export statements inside modules, not regular scripts.
Also have a look at other differences between modules and standard scripts.