Skip to content

Fix languages export#58

Open
cyfung1031 wants to merge 2 commits into
jonpyt:mainfrom
cyfung1031:fix-languages-export
Open

Fix languages export#58
cyfung1031 wants to merge 2 commits into
jonpyt:mainfrom
cyfung1031:fix-languages-export

Conversation

@cyfung1031

@cyfung1031 cyfung1031 commented Jun 13, 2026

Copy link
Copy Markdown

Close #57

Suggested Approach

Currently, the output entries cannot share the same global objects when used through an ESM CDN.

So the library works with a normal static/bundled build, but it is not usable reliably with <script type="module"> in HTML via CDN imports.

For example:

import { languages } from "prism-code-editor/prism";
import "prism-code-editor/prism/languages/bash.js";

The bash language may be registered into a different languages object from the one imported from prism-code-editor/prism.

To fix it:

  1. Add exports in each language module.
  2. Add types, import, and default in package.json exports instead of only pointing to .js.
  3. Use Symbol.for("prism-code-editor.languageMap") and similar global keys for Prism languages, so different ESM entries can share the same global objects.

Alternative Approach (not this PR)

Another approach is to create explicit ESM exports for the shared objects, instead of keeping them as local var xxx.

For example:

// global.ts
export const plainTextGrammar: Record<string, unknown> = {};
export const rest = Symbol();
export const tokenize = Symbol();

export const languages: Record<string, unknown> = {
  plain: plainTextGrammar,
  plaintext: plainTextGrammar,
  text: plainTextGrammar,
  txt: plainTextGrammar,
};

Then other scripts can import from it:

import { plainTextGrammar, rest, tokenize, languages } from "./global";

This would make all ESM modules refer to the same exported objects within the same build.

However, for CDN usage, different entries can still be transformed separately. So using Symbol.for(...) should be safer for this package.

Conclusion

Using local var xxx registries with ESM CDN entry points can cause duplicated registry objects.

This PR makes the registries shared across entries, while keeping the existing side-effect import API.


Verification

This was verified with:

<script type="importmap">
{
  "imports": {
    "prism-code-editor": "https://esm.sh/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.1",
    "prism-code-editor/": "https://esm.sh/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.1/"
  }
}
</script>
<script type="module">
  import { basicEditor, readonlyEditor } from "prism-code-editor/setups";
  import { languages } from "prism-code-editor/prism";
  import "prism-code-editor/prism/languages/bash.js";

  const bashGrammar = languages.bash || languages.sh || languages.shell;
  console.log("Bash grammar loaded:", !!bashGrammar, bashGrammar);
</script>

Actual Use

https://mac-tool.github.io/scripts

@jonpyt

jonpyt commented Jun 13, 2026

Copy link
Copy Markdown
Owner

I agree that this does fix importing grammars and languages in esm.sh, however there are more features that rely on shared imports that still don't work properly with esm.sh. This PR therefore increases the bundle size of the library just to make it slightly more usable in a way I don't recommend using it.

The biggest problem with using esm.sh with a very modular library like this one is the duplicated logic. Everything imported by each entry point has to be bundled seperately, and this will result it lots of wasted bandwidth. For example, if you import JSX and coffeescript, the JavaScript grammar will be imported and registered twice, and all shared functions and variables are defined twice.

As mentioned in #57, look at #3 if you want to use a CDN for prototyping. If you don't want to use a bundler like Vite in your project, use this bundle builder. I hope you agree that these are better approaches.

@cyfung1031 cyfung1031 force-pushed the fix-languages-export branch from d254dee to e3f8454 Compare June 13, 2026 11:25
@cyfung1031

cyfung1031 commented Jun 13, 2026

Copy link
Copy Markdown
Author

however there are more features that rely on shared imports that still don't work properly with esm.sh.
As mentioned in #57, look at #3 if you want to use a CDN for prototyping. If you don't want to use a bundler like Vite in your project, use this bundle builder. I hope you agree that these are better approaches.

I don't agree. You just limit your tool ability and force every one to use bundle builder.

I understand my PR might not be the best to your eyes, but you should think about the solutions that you want. If you think this is vite issue, then try to use rspack.

if you import JSX and coffeescript, the JavaScript grammar will be imported and registered twice

all these export { languageMap } and export { languages } from "../core.js"; can be removed.


Update

I have removed all the export { languageMap } and export { languages } from "../core.js";

verified by @cyfung1031/prism-code-editor@5.2.0-cdn-esm.2
https://esm.sh/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/es2022/prism.mjs
https://esm.sh/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/es2022/prism/languages/bash.mjs

Anyway if you insist then fine. I have already forked this repo and applied the fix so that I can use in my application.

@jonpyt

jonpyt commented Jun 13, 2026

Copy link
Copy Markdown
Owner

all these export { languageMap } and export { languages } from "../core.js"; can be removed.

Yes, re-exporting these is unnecessary. But removing these imports doesn't result in JavaScript not being registered twice if you import JSX and CoffeeScript. Have a look at the following files:

https://esm.sh/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/es2022/prism/languages/jsx.mjs
https://esm.sh/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/es2022/prism/languages/coffeescript.mjs

Do you see how much code is duplicated between the modules? This is the main issue with using esm.sh and will result in slower load times and higher bandwidth usage.

Anyway if you insist then fine. I have already forked this repo and applied the fix so that I can use in my application.

If you believe esm.sh is your best option, my suggestion would be to import as few modules by importing the following:

  1. https://esm.sh/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/es2022/prism/languages/common.mjs
  2. https://esm.sh/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/es2022/setups.mjs

I analyzed the contents of these files, and I don't think it's ideal at all. The basic setup imports ./dist/basic-Bie2IZGI.mjs which imports ../guides.mjs, ../match-brackets.mjs, ../highlight-brackets.mjs, ../match-tags.mjs, ../search.mjs, and ../languages.mjs. This is going to be a lot of duplicated code.

@cyfung1031

Copy link
Copy Markdown
Author

This is going to be a lot of duplicated code.

I checked with AI is that, esm.sh seems not support relative path ../ for resolving the same object.
if you change every relative path into prism-code-editor/... then it might resolve the issue.

but anyway I hope that you can find the solution.

@jonpyt

jonpyt commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Ok, so I read the docs for esm.sh, and it seems that you can add ?bundle=false to the end of imports to prevent it from bundling all imports in every entry point. It reuses the same languages and languageMap records, so mutating globalThis isn't necessary. Have a look at the following:

https://esm.sh/prism-code-editor@5.2.0/es2022/prism?bundle=false
https://esm.sh/prism-code-editor@5.2.0/es2022/prism/languages/bash?bundle=false

This will result in more network requests, but each will be much smaller.

@jonpyt

jonpyt commented Jun 13, 2026

Copy link
Copy Markdown
Owner

I checked with AI is that, esm.sh seems not support relative path ../ for resolving the same object.

My comment just listed the import strings in ./dist/basic-Bie2IZGI.mjs directly. That file uses relative paths for imports, but you obviously cannot use relative paths outside esm.sh.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cannot import from esm.sh

2 participants