autodetect js module type - only bundle esm (#5515)

* wip: autodetect esm

* finish fixes

* thread minify through

* add sweetalert2

* Fix default bundle types

* ts-ignore

* small nits

* small more nits

* shrink flamegraph
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2026-04-30 17:50:37 -07:00
1 parent 2f40780ce0
commit 218bb9d56d
24 files changed
+1598 -616

No files matched your search

+28
View File
@@ -22,6 +22,7 @@ pub struct JsAssetOptions {
minify: bool,
preload: bool,
static_head: bool,
module: bool,
}
impl Default for JsAssetOptions {
@@ -42,6 +43,7 @@ impl JsAssetOptions {
preload: false,
minify: true,
static_head: false,
module: false,
}
}
@@ -59,6 +61,11 @@ impl JsAssetOptions {
pub const fn minified(&self) -> bool {
self.minify
}
/// Check whether the asset is declared as an ES module
pub const fn is_module(&self) -> bool {
self.module
}
}
impl AssetOptions {
@@ -116,6 +123,27 @@ impl AssetOptionsBuilder<JsAssetOptions> {
self
}
/// Mark the asset as an ES module (default: false)
///
/// When true, the script tag emitted via `with_static_head(true)` is rendered as
/// `<script type="module" ...>`, and the file is delivered with its module syntax
/// (`import`/`export`) preserved so the browser can resolve imports natively at
/// runtime. The default treats the file as a classic script: `<script>` is emitted
/// without `type="module"`, and minification preserves classic-script semantics.
///
/// Note: this does not perform build-time bundling. If you need imports resolved
/// into a single file, pre-bundle with your tool of choice and ship the result.
///
/// ```rust
/// # use manganis::{asset, Asset, AssetOptions};
/// const _: Asset = asset!("/assets/script.js", AssetOptions::js().with_module(true));
/// ```
#[allow(unused)]
pub const fn with_module(mut self, module: bool) -> Self {
self.variant.module = module;
self
}
/// Convert the builder into asset options with the given variant
pub const fn into_asset_options(self) -> AssetOptions {
AssetOptions {
+42
View File
@@ -38,6 +38,48 @@ const REQUIRED: Asset = asset!("/assets/style.css");
const OPTIONAL: Option<Asset> = option_asset!("/assets/missing.css");
```
## JavaScript assets
The CLI auto-detects whether each `.js` asset is an ES module (top-level
`import`/`export` or `import.meta`) or a classic script and processes it
accordingly.
- **`with_minify(true)` (default)**: the file is minified in place. Classic
scripts are minified verbatim with their format preserved (no IIFE
re-wrapping). ES modules are minified and bundled — local relative imports
are inlined into a single ESM file, while `http://` / `https://` imports
remain as external `import` statements for the browser to resolve at runtime.
- **`with_minify(false)`**: the file is copied byte-for-byte. Use this when
shipping pre-built third-party libraries you do not want re-processed.
- **`with_static_head(true)`**: appends a `<script>` tag to the document head
pointing at the asset. The CLI emits `<script type="module" ...>` when the
file is detected (or declared) as an ES module, and a classic `<script>`
otherwise.
- **`with_preload(true)`**: emits a `<link rel="preload" as="script">` for the asset.
- **`with_module(true)`**: forces the file to be treated as an ES module even
when auto-detection would say otherwise. Useful when you author a
side-effect-only module without top-level `import`/`export` declarations.
Files named `*.mjs` are always treated as modules; files named `*.cjs` are
always treated as classic.
```rust
use manganis::{asset, Asset, AssetOptions};
// Vendored UMD library: copy verbatim, emitted as a classic <script>.
const SWEETALERT: Asset = asset!(
"/assets/sweetalert2.all.min.js",
AssetOptions::js().with_minify(false).with_static_head(true)
);
// Authored ES module with local imports: auto-detected from the top-level
// `import`/`export`, bundled and minified into a single ESM file, emitted as
// `<script type="module">`.
const APP: Asset = asset!(
"/assets/app.js",
AssetOptions::js().with_static_head(true)
);
```
## Native FFI Bindings
Manganis provides the `#[ffi]` attribute macro for generating direct FFI bindings between Rust and native platforms (Swift/Kotlin). See the `geolocation-native-plugin` example for usage.