< E) Referencing files | F) Features | > G) Plugins |
This page outlines the key features provided by Jsenv, including Node ESM resolution, magic extensions, import.meta.dev
, injections, inlining, and handling UMD/CommonJS modules.
- Node ESM resolution
- Magic extensions
- import.meta.dev
- Injections
- Inlining
- Importing UMD
- Importing CommonJs
- Loading js module with
Jsenv implements Node.js ESM resolution for JavaScript imports, enabling compatibility with Node.js module resolution in the browser.
Example:
import "amazing-package";
This is transformed into:
import "/node_modules/amazing-package/index.js";
See some of the features provided by Node ESM resolution:
During development, imports from node_modules
are versioned using the package's version field from package.json
.
Example:
import "/node_modules/amazing-package/index.js?v=1.0.0";
☝️ Here amazing-package/package.json contains "version": "1.0.0"
It allows to put node modules files into the browser cache up to 1 year.
A nice bonus: the version of the package is easily identifiable.
Warning Magic extensions are becoming less common in the JavaScript ecosystem. Avoid using them when possible.
Jsenv supports magic extensions for resolving file imports without explicit extensions.
Example:
import "./file";
This is resolved to:
import "./file.js";
Disabling Magic Extensions:
import { startDevServer } from "@jsenv/core";
await startDevServer({
sourceDirectoryUrl: new URL('../', import.meta.url),
+ magicExtensions: null,
+ magicDirectoryIndex: false,
});
import { build } from "@jsenv/core";
await build({
sourceDirectoryUrl: new URL("../src/", import.meta.url),
buildDirectoryUrl: new URL("../dist/", import.meta.url),
entryPoints: { "./main.html": "main.html" },
+ magicExtensions: null,
+ magicDirectoryIndex: false,
});
Use import.meta.dev
to differentiate behavior between development and production builds.
Code specific to development is removed during the build process.
Example:
if (import.meta.dev) {
console.log("during dev"); // logged when executing source files
} else {
console.log("after build"); // logged when executing files after build
}
Jsenv allows injecting variables into file content during development and build.
Example:
// eslint-disable-next-line no-undef
window.__ENV__ = __ENV__;
console.log(window.__ENV__);
Development configuration:
import { startDevServer } from "@jsenv/core";
await startDevServer({
sourceDirectoryUrl: new URL("../src/", import.meta.url),
injections: {
"./main.js": () => {
return { __ENV__: "dev" };
},
},
});
The browser will see the following file content:
// eslint-disable-next-line no-undef
+ window.__ENV__ = "dev";
console.log(window.__ENV__);
Build configuration:
import { build } from "@jsenv/core";
await build({
sourceDirectoryUrl: new URL("../src/", import.meta.url),
buildDirectoryUrl: new URL("../dist/", import.meta.url),
entryPoints: { "./main.html": "main.html" },
injections: {
"./main.js": () => {
return { __ENV__: "build" };
},
},
});
The build file content:
// eslint-disable-next-line no-undef
+ window.__ENV__ = "build";
console.log(window.__ENV__);
It's possible to put injection logic into a single function:
// inside utils.mjs
export const getInjections = () => {
return {
"./main.js": (urlInfo) => {
return {
__ENV__: urlInfo.context.dev ? "dev" : "build",
};
},
};
};
// inside dev.mjs
import { startDevServer } from "@jsenv/core";
import { getInjections } from "./utils.mjs";
await startDevServer({
sourceDirectoryUrl: new URL("../src/", import.meta.url),
injections: getInjections(),
});
// inside build.mjs
import { build } from "@jsenv/core";
import { getInjections } from "./utils.mjs";
await build({
sourceDirectoryUrl: new URL("../src/", import.meta.url),
buildDirectoryUrl: new URL("../dist/", import.meta.url),
entryPoints: { "./main.html": "main.html" },
injections: getInjections(),
});
And the function can be async:
export const getInjections = () => {
return {
"./main.js": async () => {
const value = await Promise.resolve("toto");
return { __ENV__: value };
},
};
};
Inlining allows embedding code from separate files directly into HTML or other files.
Example:
<!doctype html>
<html>
<head>
<title>Title</title>
<meta charset="utf-8" />
<link rel="icon" href="data:," />
</head>
<body>
<script src="./demo.js?inline"></script>
</body>
</html>
This inlines demo.js
into the HTML:
<script>
console.log("Hello world");
</script>
UMD (Universal Module Definition) modules can be imported directly. For packages like jquery
:
Example:
import "jquery";
const jquery = window.$;
For packages like hls.js
that require this to be defined, use the as_js_module query parameter:
Example:
import "hls.js?as_js_module";
window.Hls;
CommonJS modules are not natively supported in browsers.
Example:
// main.js
module.exports = 42;
Error:
Uncaught ReferenceError: module is not defined
Solution: Use jsenvPluginCommonJs
documented in G) Plugins#commonjs.
To load a JavaScript module with a classic <script>
tag while retaining module features use jsenvPluginAsJsClassic
.
Example:
<script src="file.js"></script>
// file.js
console.log(import.meta.url);
Error:
Uncaught SyntaxError: Cannot use import statement outside a module
Solution: Use jsenvPluginAsJsClassic
documented in G) Plugins#asJsClassic.
< E) Referencing files | F) Features | > G) Plugins |