# \[IGN-12300\]Support for ES Modules?

**URL:** <https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481>\
**Category:** Module Development\
**Tags:** perspective\
**Created:** [March 10, 2025, 10:21pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481 "2025-03-10T22:21:23Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 10, 2025, 10:21pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/1 "2025-03-10T22:21:23Z")

</div>

I can't find a way to use an [ES Module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) as a [BrowserResource](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.44/com/inductiveautomation/perspective/common/api/BrowserResource.html).

1. When marked as `BrowserResource.ResourceType.JS`, there isn't way to add the `type="module"` attribute to the inserted HTML:

```html
 <!-- Current --> 
<script type="application/javascript" src='...'></script>

 <!-- Desired --> 
<script type="module" src='...'></script>

```

1. If you add a resource with extension `.mjs`, Ignition does not serve the file with the correct `Content-Type` header, leading to an error:

```html
Refused to execute script from \'https://.../my-resource.mjs\' because its MIME type (\'\') is not executable, and strict MIME type checking is enabled.

```

Anyone gone down this road before?

If it isn't supported, it'd be nice if it was. I think this would give me the isolation needed to run multiple versions of a JavaScript library in the client.

[https://caniuse.com/es6-module](https://caniuse.com/es6-module)

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [March 11, 2025, 3:06pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/2 "2025-03-11T15:06:56Z")

</div>

I'm 99% confident the answer is just "you cannot", right now.

Can you give me the elevator pitch on ES modules, as a (deliberately) frontend-ignorant person? It's something-something JS packaging, and that's the extent of my knowledge.

It would be trivial to add an enum to `ResourceType` that ultimately emits different HTML, but I'm not sure if that bandaid is the "right" way to fix this problem.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [March 11, 2025, 3:13pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/3 "2025-03-11T15:13:03Z")

</div>

Huh. I'm a bit surprised that is an Enum. Can't even supply a custom mime type. ☹

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [March 11, 2025, 3:15pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/4 "2025-03-11T15:15:05Z")

</div>

> [@pturmel](#):
>
> supply a custom mime type.

Yeah, that's where I'm wondering if it's not better to rethink the whole way ResourceType works...but that then becomes a larger change that I can't as readily "sneak in" 🤷

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [March 11, 2025, 3:19pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/5 "2025-03-11T15:19:00Z")

</div>

Point. That's probably a big breaking change. Has to be done to get to things like Web Assembly.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 11, 2025, 3:35pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/6 "2025-03-11T15:35:17Z")

</div>

> [@paul-griffith](#):
>
> Can you give me the elevator pitch on ES modules, as a (deliberate) frontend-ignorant person?

Ha, I just know enough to get what I need done and then get out 😆.

Here's my TLDR take on ES6 modules:

1. JavaScript is a mess.
2. There have been many different strategies for importing and using libraries/modules of code (CJS, AMD, UMD, etc). Each of these strategies was created to target a specific use-case of JavaScript (browser vs Node vs whatever), and none of them are actually part of the JavaScript language.
3. ES6 modules were introduced in the ECMAScript 2015 standard as an addition to the language to standardize how libraries/modules of code are shared.
4. With ES6 modules you can use the same import/export syntax in the browser as you can in server runtimes, to import code from other JavaScript files. This gets you:
  - Code isolation (modules don't need to pollute the global namespace).
  - Tree-shaking (only load what you need).
  - Removes the need for complicated bundling.
  - Generally makes JavaScript behave more like a real language.

If any of this is wrong, hopefully Cunningham's Law will kick in.

The code isolation feature is what I'm after.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [March 11, 2025, 3:58pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/7 "2025-03-11T15:58:52Z")

</div>

Well, I looked into the codebase and it's involved enough I'm not comfortable just YOLO-ing the changes into 8.3, so I made a ticket to do it "right". The _most likely_ scenario is that we just add an enum for ES modules that will then emit the right HTML on the final page. Since that's just adding an enum entry, it's not a breaking change, so even if we don't get to it before 8.3 it could still happen in an 8.3.X release, which is a bonus.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 11, 2025, 4:30pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/8 "2025-03-11T16:30:59Z")

</div>

👍

In the mean time, I'm gonna try to use a "bootstrapping" resource to add the HTML to load the "real" resource as a module.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 20, 2025, 10:57pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/9 "2025-03-20T22:57:25Z")

</div>

Okay, got a chance to try this out.

My main browser resource is now:

```javascript
// bootstrap.js
const e = document.createElement("script");
e.src = "/res/embr-charts/embr-charts-client.js";
e.type = "module";
document.head.appendChild(e);

```

A few changes were needed to my `vite` build config:

1. Changed from `umd` to `es` format.
2. Added the `bootstrap` entry point to the build.
3. In order for my `client` script to reference the globals provided by Perspective, I found I needed to use the [rollup-plugin-external-globals](https://github.com/eight04/rollup-plugin-external-globals). This replaced the previous `rollupOptions.externals` and `rollupOptions.globals` entries.

Next I need to test that the `client.ts` is isolated the way I want. But the fact that I can get this far is promising 🙂.

```typescript
// vite.config.ts
export default defineConfig(({ mode }) => ({
  build: {
    minify: true,
    outDir: './dist',
    lib: {
      entry: [
        resolve(__dirname, 'src/client.ts'),
        resolve(__dirname, 'src/bootstrap.ts'),
      ],
      fileName: (_, entryName) => {
        return `embr-charts'-${entryName}.js`
      },
      name: 'EmbrCharts',
      formats: ['es'],
    },
    rollupOptions: {
      plugins: [
        externalGlobals({
          react: 'React',
          'react-dom': 'ReactDOM',
          moment: 'moment',
          '@inductiveautomation/perspective-client': 'PerspectiveClient',
        }),
      ],
    },
  },

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [March 20, 2025, 11:16pm UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/10 "2025-03-20T23:16:00Z")

</div>

[![](https://us1.discourse-cdn.com/inductiveautomation/original/3X/3/c/3c921025c1444a5f5a8f09d6a5379a93281b5bf1.jpeg "I Know Some of These Words") ](https://www.youtube.com/watch?v=Ccoj5lhLmSQ)

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 21, 2025, 1:07am UTC](https://forum.inductiveautomation.com/t/ign-12300-support-for-es-modules/103481/11 "2025-03-21T01:07:08Z")

</div>

Be glad. I’d swear the people behind JavaScript’s ecosystem are intentionally trying to create the most complicated Rube Goldberg machine of all time (just to solve the most basic computer science problems).

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/4/0/4002da4f8a50485150e5cad7993b32f609f891bb.jpeg)
