Use ES Modules from content_scripts of Web Extension (add-on) [duplicate] - javascript

This question already has answers here:
How to import ES6 modules in content script for Chrome Extension
(11 answers)
Closed 2 years ago.
Since the latest Firefox supports ES Modules without flags as well as Chrome, I would like to use import/export for my web extension (add-on). Pop up, background and option pages were OK by simply using <script type="module">.
How do you make modules work in content script?
I tried and saw followings:
(1) Just write import in a acript declared at content_scripts.js in manifest.json
import Foo from './foo.js';
(Chrome) Uncaught SyntaxError: Unexpected identifier
Firefox doesn't work with no errors.
(2) Run through tabs.executeScript()
browser.tabs.executeScript(undefined, {
file: "foo.js",
});
(Chrome) Uncaught SyntaxError: Unexpected identifier
(Firefox) Error: import declarations may only appear at top level of a module
(3) Insert a script element created with type="module"
const el = document.createElement("script");
el.src = browser.extension.getURL("foo.js");
el.type = "module";
document.body.appendChild(el);
(Chrome) Uncaught (in promise) ReferenceError: browser is not defined
(Firefox) ReferenceError: browser is not defined
Do you have any other idea?

One easy solution is using some bundler to bundle your content script.
It may sound complicated, but with bundler like Rollup you can do this very easily with super simple "rollup.config.js" file:
export default {
input: 'content_script.js',
output: {
file: 'bundle_content_script.js',
format: 'iife',
},
};
Rollup will only replace your imports with actual code, so there is no boilerplate and the code is still readable.
You may also want to read:
bundlers comparison: https://medium.com/#ajmeyghani/javascript-bundlers-a-comparison-e63f01f2a364
bundling multiple scripts: https://github.com/rollup/rollup/issues/703
Using bundler will also speed-up loading time, for me it went down from 335ms to 63ms (but I have 100+ modules now).

Related

Why is the browser complaining for import declarations position (js/vue/vitejs)? [duplicate]

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.

Can I/should I use ES modules for client-side JS files? I use node.js [duplicate]

This question already has answers here:
"Uncaught SyntaxError: Cannot use import statement outside a module" when importing ECMAScript 6
(31 answers)
Closed 2 years ago.
I'm using the latest version of node.js and Chrome.
Node.js still relies on CommonJS to do modules (exports and imports).
The CommonJS page that was recommended to me actually says CommonJS is not suitable for client-side and to use ES modules instead. This makes me think I should use them for my websites that I'm creating. Is it possible to do this?
The errors I get when I try to are 'Uncaught SyntaxError: Unexpected token 'export' & unexpected token export' & 'Uncaught SyntaxError: Cannot use import statement outside a module.'
Here are the errors I get when I include type="module":
Write <script type="module"> instead of <script>.

TypeScript for web without module loader?

This is a very confusing topic for me at the moment and I believe for a lot of other people too. A lot of the advice that's floating around the internet seems to be very misleading.
What am I trying to do?
I am trying to build a basic web page with just plain TypeScript and HTML. I want to include the compiled JavaScript with a standard <script> tag such as
<script src="javascriptOutput/myCompiledJsFile.js></script>
while still being able to use normal module functionality of TypeScript, meaning splitting my code up into different files and importing them in the main script myCompiledJsFile.js that I include in my HTML.
What's the problem?
With the following tsconfig.json ,
{
"compilerOptions": {
"target": "es5",
"module": "commonjs"
// ...
}
}
the JavaScript console always throws this error: Uncaught ReferenceError: exports is not defined.
From what I have read it seems like the problem lies in the module system being specified as commonjs, which doesn't seem to work without an external module loader (like SystemJS, the one included in Webpack etc.).
I think it should be possible to use TypeScript without one of these external module loaders, if I am right. But I don't know how to configure TypeScript to compile in such a way that browsers can run the compiled code without the Uncaught ReferenceError. And I also don't know where you can find this information on what browsers support which module loaders / module systems. Do I have to look at the JavaScript engine the browser runs (e.g. V8 for Chrome) ?
I also don't want to work with any 'hacks' like adding var scripts = {} or the like or removing lines in the compiled JS.
In case someone is wondering: the solution to my problem was to use the native ES module system. That means choosing either ES2015 or ES2020 for the module key in the tsconfig.json, based on what the browser supports. Since I was looking to run my code in Chrome only (Electron) and Chrome fully supports ES2020 as of today, I chose to use ES2020 for my module system.
If you choose to use these modules, AFAIK, you need to add the type="module" attribute to your script tag:
<script src="../myJsFolder/myJs.js" type="module"></script>

jquery not working in nw.js (node webkit)

I am switching my desktop app from electron to nw.js because of source code security features.
I am requiring the module jquery-confirm in my nodejs file, I get this error:
Uncaught Error: jquery-confirm requires jQuery
I fix this error by:
var $ = require('jquery');
window.jQuery = $;
var jconfirm = require('jquery-confirm');
and importing the js file in index.html like this:
<script>require('./bot.js')</script>
Becuase I get the same jquery error if I require the js file like:
<script src="bot.js"></script>
When I fix the error like above and I launch the app with nw.js, it immediately crashes giving this error:
TypeError: $(...).button is not a function
First question:
I am assuming there is something wrong with jquery. When I import it in index.html, it doesnt work at all. However, I am still running into issues after requiring it in the js file. How can I fix this?
Second question:
Why dont I get the Uncaught Error: jquery-confirm requires jQuery error if I import my js file using 'require' instead of ?
This is an issue of javascript context in Node Webkit. We must ensure all the javascript required inside the browser (the window context) is brought into that context by using the src attribute of a script tag.
Node Webkit places JavaScript from require() statements in the nodejs global context - and makes it unavailable inside the browser.
So we see errors such as $ is not defined or $(...).button() is not a function if files are in opposite contexts.

Typescript. ReferenceError: require is not defined [duplicate]

This question already has answers here:
Importing an external module in TS causes "ReferenceError: require is not defined"
(2 answers)
Closed 6 years ago.
I read official doc for TypeScript, and I copy code from there.
I install commonjs and requerejs.
"devDependencies": {
...
"commonjs": "latest",
"requirejs": "latest"
}
But I get a error in the browser:
ReferenceError: require is not defined
It is index.html:
<!DOCTYPE html>
<body>
<script src="main.js"></script>
</body>
It is main.js after compiling:
"use strict";
var core_1 = require("./some_dir/some_file");
window.onload = function () {
var some = new some_file_1.SomeClass();
};
Browsers don't have a understanding of modules (yet). That means while require() works if you execute it in node only, browsers don't know what to do with it.
There are 2 main methods of fixing this:
Remove the exports/require statements and include every single js file (in correct dependency order) into your html file (as script tags). They will all have the same scope and will work as if there was just one large concatenated file. This solution is pretty bad tho as it destroys scoping, doesn't really work with modules you got with npm and has bad loading times, as it has to fetch multiple js files from the server.
Use a resource bundler like webpack. Webpack will look at all dependant files starting from your main.js file and bundle and compress them down into a single small .js file. This also works for external dependencies (npm). All you have to do then is to include that single bundle.js file into the html.

Categories