How to use ES6 import” with Chrome Extension

Expecting JavaScript developers who have / will have developed Chrome Extension ever.

tl;dr

The minimum example is working here.

Table of Contents

  1. Why?
  2. Background Script
  3. Content Script
  4. Conclusion

Why?

Static files like CSS and JavaScript always have a specific problem to deliver themselves: “How to bundle bunch of dependencies to one file”.

For JavaScript, as you know, there are many bundler middleware, e.g. Browserify, RequireJS and Webpack, can resolve “require” method or “import” keyword and bundle JavaScript (sometimes AltJS) files to a single output.

Thanks to browsers’ support of ES2015 (so called ES6), “import” keyword can be used without using such middleware mentioned above, only when you add type="module" to your <script> tag.

<script type="module" src="your-script.js"></script>

OK, then, can we use module importing with developing Chrome Extension, with which JavaScript is loaded by manifest.json ?

Background Script

Since background script of Chrome Extension is loaded by background.scripts field of manifest.json , it’s not possible to add “module” attribute as <script> tag. So just let’s use background.page field instead, as an entrypoint of your JavaScript files.

{
"background": {
"page": "src/html/background.html"
}
}

then src/html/background.html looks like

<script type="module" src="src/js/background.js"></script>

It works fine 👍

Content Script

Content Script, on the other hand, must be loaded automatically when content_scripts are specified in manifest.json, kind of hacks are needed.

If import keyword simply appears on one of content_scripts, following error message comes up.

Uncaught SyntaxError: Unexpected identifier

This is because it’s loaded without type="module" attribute. We, somehow, kick our JavaScript by <script> tag with type="module", at least the entrypoint of it.

The idea is to use dynamic import which is equivalent to add type="module" to the dynamically imported script.

The content_script would look like

(async () => {
const src = chrome.extension.getURL('src/js/main.js');
const contentScript = await import(src);
contentScript.main();
})();

Once it’s imported by dynamic import, import keyword can be used just as it’s loaded with type="module".

NOTE: Because import(src) is executed in a page-renderer context, which is outside of Chrome Extension, you might need to make the src accessible from website, by using “web_accessible_resources” directive in your manifest.json .

src/js/main.js can be like this

import User from "./my-models/User";export function main() {
// Do what you want
const user = new User({name: "otiai10"});
console.log(user);
}

Alternatively, injecting script tag by content_script (introduced in this thread) can kick other JavaScript files with type="module", but as it’s external script for Chrome Extension Lifecycle, chrome namespace can be eliminated and it requires some hack to use chrome, e.g. dependency injection.

Conclusion

Thus, it’s not so hard work to use import for your Chrome Extension. But still we have problems which are usually solved by kind of Webpack.

  1. How to solve node_modules and sub dependencies inside them?
  2. Minifying / Uglifying / Transpiling AltJS are still needed in some cases.

We can take more benefits by using such middleware with well-structured functionalities. I decided to leave native import keywords for now.

wanna be died on Mars