This article has been translated on the basis of machine translation. If there are any mistakes, please fix it.pull request

Import strings as JavaScript Modules

Show you how to extract a Module from a string that can be used at runtime. Use with the Editor, it may provide a new experience in the browser.

4/1/20224 min read
..
hero image

Introduction

The import statement can be used to obtain live bindings, which can then be used as values or JavaScript Objects in the runtime.

This is often used with relative URLs or http schemes on the filesystem.

Import Assertions also allows, for example, json format to be treated as a JSON Modules. See Summary of import assertions and JSON modules for details. This is already available in many modern browsers.

On the other hand, what if you want to use the following string as a JavaScript Object?

1
export default { from: "text" }
txt

Once this is done, there is much more that can be expressed in the browser. For example, it is possible to dynamically configure the playground.

We have created MapCSS Playground using this mechanism. Please try it out.

Import with Data URL Scheme

In fact, the import statement supports the Data URL scheme. Modern browsers, as well as deno has already implemented this in v1.7.

Compared to eval, the import statement has the advantage that live binding is available at runtime.

Data URL scheme syntax

The Data URL scheme consists of the following syntax:

1
data:[<mediatype>][;base64],<data>
bash

mediatype specifies the MIME type. If data is a character, it can be written as is. Otherwise, ;base64 must be specified after encoding data in Base64.

When omitted, text/plain;charset=US-ASCII is used. 1

text/javascript MIME type

Added 4/1/2022

If you want to get live binding, it is easiest to specify the text/javascript MIME type. It will look something like this:

1
2
3
4
5
const code = `const hello = () => console.log("ハロー")
export default { hello }`
const mod = await import(`data:text/javascript,${code}`)
mod.default.hello() // ハロー
js

This is the best way because no encoding is required.

Base64 encoding

Expressions in JavaScript modules must be encoded in Base64 because they contain spaces and brackets.

If the MIME type is application/javascript, it must be encoded to Base64.

The Web API has long had btoa. However, it is not a pure function. An exception is raised if a character that occupies more than 2 bytes in UTF-16 is included. 2

The following is a commonly introduced method of escaping this.

1
btoa(unescape(encodeURIComponent('日本語')))
ts

However, the use of the unescape function has been deprecated because it is no longer part of the ECMAScript specification. 3

Since it is not a pure function to begin with, other functions should be used if possible. The standard deno module encoding/base64 is available and should be used.

It looks like this:

The following code works with the Deno runtime. To run in a browser, a bundle is required.
1
2
3
4
5
6
import { encode } from 'https://deno.land/std/encoding/base64.ts'
const code = `const hello = () => console.log("ハロー")
export default { hello }`
const mod = await import(`data:application/javascript;base64,${encode(code)}`)
mod.default.hello() // ハロー
ts

Conclusion

We have shown how to import strings as JavaScript Modules.

In addition to this, you can use the Monaco Editor and other tools. You can create a configurable playground in the browser.

However, the following points remain unsatisfactory:

  • The import statement is not available in the Worker thread in some browsers.
  • Code written in TypeScript cannot be executed.

The former is a browser compatibility issue. There is no problem as long as the import statement is used in the main thread. At the time of writing, Firefox cannot use the ES Modules in the Worker.

To use import statements in a worker, you can use shimport, which is a polyfill for import.

For the latter, of course, the TypeScript code cannot be executed in the browser, so it must be transpiled on demand. swc provides wsam, so you can transpile it fast in the browser.

Actually, MapCSS Playground solves all these problems. I will discuss each implementation in another article.


  1. Data URL Syntax
  2. Unicode character string
  3. It is specified as a legacy feature that should not be newly used. See Annex B for details.

Edit this page on GitHub

Comments