-
-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Scripts of type "module-shim" don't seem to have access to specifier defined in importmap #377
Comments
Changing the However there's definitely a race condition somewhere. things usually work, but sometimes I get a parse error:
|
Here's a reference to some code where it's easy to reproduce the race condition: https://github.com/zachsa/es-module-shims-jsx-plugin/tree/7574a1645874bb43fd37e6a5dbd1e24a7f603623 |
I cloned the example and ran it in latest Chrome, but didn't get any race condition. What browser are you using? If you want to avoid the plugin parsing all sources, you can use the |
Sorry - you have to move the dist import to below the es-module-shim import and then refresh the page a number of times |
If you move the dist import below the es module shim import then the plugin will be loaded after es-module-shims, therefore before the plugin loads es-module-shims will try to load without jsx support. So that would explain your race condition? So don't move the dist import below es module shims? |
Thank you - the documentation is becoming clearer now generally. Skip should be good in this case. I'll create a branch with the race condition |
Would it not always fail then? That wasn't the case |
I guess it passes if the fetch request takes longer to load the first JSX file than it does to load the plugin? |
That is probably the issue |
It does not appear to work in shim mode, or at least I get the error (using the debug build)
However, if I define skip as a string then that error disappears and another once appears instead:
Is this something that I can request? Alternatively, do you have any suggestions for work arounds? |
Ahh right it seems skip doesn't work for shim mode - in that case you would want to rather create your own "skip" hook, by simply not doing the transformation by filtering it by URL in your own plugin hook. It's one if statement! |
I did try that... Will look into it before saying it didn't work |
I want to combine native importmaps and shim importmaps, native ones does not support extending import map in runtime. <script>
window.esmsInitOptions = {
shimMode: true,
mapOverrides: true,
resolve: function (id, parentUrl, defaultResolve) {
if (id === "react" || id === "reactDOM") {
// Some logic to prevent shim resolvers and use native imports???
}
return defaultResolve(id, parentUrl);
}
}
</script>
<script id="importmap" type="importmap">
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/esm/react.production.min.js",
"reactDOM": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/esm/react-dom.production.min.js"
}
}
</script>
<script type="module" src="..."></script> Then in runtime importShim.addImportMap({
imports: importMap
});
const script = createHtmlElement("script", {
type: "module-shim",
src: component.url,
}); How I can combine them? Dublicating react to shim importmap will broke react, it will throw this error. |
You're exactly looking for the |
@guybedford What I'm doing wrong? <script async src="https://ga.jspm.io/npm:[email protected]/dist/es-module-shims.js"></script>
<script>
window.esmsInitOptions = {
shimMode: true,
mapOverrides: true,
skip: ["react", "reactDOM"]
}
</script>
<script type="importmap">
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/esm/react.production.min.js",
"reactDOM": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/esm/react-dom.production.min.js"
}
}
</script>
// This module is working
<script type="module" src="..."></script> Then in code importShim.addImportMap({
imports: importMap
});
const script = createHtmlElement("script", {
type: "module-shim",
src: component.url
}); And then I get this error. I dont know why but in the code you firstly trying to resolve import and only then checks if its skipped. |
Here is the solution for combining native importmap imports and shim imports:For native importmaps: <script type="importmap">
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/esm/react.production.min.js",
"reactDOM": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/esm/react-dom.production.min.js"
}
}
</script>
<!-- Load script using native importmaps so performance will not affected -->
<script type="module" src="..."></script> And combining native importmaps with importmap shims const ignoreMap = {
react: "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/esm/react.production.min.js",
reactDOM: "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/esm/react-dom.production.min.js"
}
window.esmsInitOptions = {
shimMode: true,
mapOverrides: true,
skip: Object.values(ignoreMap),
resolve: function (id, parentUrl, defaultResolve) {
if (id in ignoreMap) {
return ignoreMap[id];
}
return defaultResolve(id, parentUrl);
}
} |
@mix0000 thanks very much for showing me how to override the @guybedford previously showed me how to intercept fetch requests to modules: async function compile(url, source) {
const transformed = transform(source, {
presets: [presetReact],
})
return transformed.code
}
globalThis.esmsInitOptions.fetch = async function (url, options) {
const res = await fetch(url, options)
if (!res.ok) return res
const source = await res.text()
const transformed = await compile(url, source)
return new Response(new Blob([transformed], { type: 'application/javascript' }))
} I was surprised to see that I couldn't simply return the original source code in the For example, this doesn't work: async function compile(url, source) {
if (url.includes('http://localhost')) {
const transformed = transform(source, {
presets: [presetReact],
})
return transformed.code
}
return source
} Is this working correctly @guybedford? From what I can tell, either I'm returning This code: async function compile(url, source) {
if (url.includes('http://localhost')) {
const transformed = transform(source, {
presets: [presetReact],
})
return transformed.code
}
return source
} Gives me the error: Uncaught TypeError: Failed to construct 'URL': Invalid base URL
at pushSourceURL (es-module-shims.debug.js:676:25)
at resolveDeps (es-module-shims.debug.js:693:7)
at resolveDeps (es-module-shims.debug.js:597:7)
at resolveDeps (es-module-shims.debug.js:597:7)
at topLevelLoad (es-module-shims.debug.js:548:5)
pushSourceURL @ es-module-shims.debug.js:676
resolveDeps @ es-module-shims.debug.js:693
resolveDeps @ es-module-shims.debug.js:597
resolveDeps @ es-module-shims.debug.js:597
topLevelLoad @ es-module-shims.debug.js:548
Promise.catch (async)
processScript @ es-module-shims.debug.js:960
processScriptsAndPreloads @ es-module-shims.debug.js:872
(anonymous) @ es-module-shims.debug.js:506
Promise.then (async)
(anonymous) @ es-module-shims.debug.js:481
(anonymous) @ es-module-shims.debug.js:976 // In the 'resolveDeps' function in es-module-shims
function pushSourceURL (commentPrefix, commentStart) {
const urlStart = commentStart + commentPrefix.length;
const commentEnd = source.indexOf('\n', urlStart);
const urlEnd = commentEnd !== -1 ? commentEnd : source.length;
pushStringTo(urlStart);
resolvedSource += new URL(source.slice(urlStart, urlEnd), load.r).href;
lastIndex = urlEnd;
} But the async function compile(url, source) {
if (url === 'https://ga.jspm.io/npm:@mui/[email protected]/index.js') {
console.log('im called') // this does log
return source
}
const transformed = transform(source, {
presets: [presetReact],
})
return transformed.code
} I can see that when "//# sourceMappingURL=index.js.map" is included in the source, that I get the parse error above. This works: async function compile(url, source) {
if (url.includes('http://localhost')) {
const transformed = transform(source, {
presets: [presetReact],
})
return transformed.code
}
return source.replace(/\/\/#.*/, '')
} Why would the presence of a source map cause es-module-shims to error? Also, regarding the |
At a guess... it looks like this code: resolvedSource += new URL(source.slice(urlStart, urlEnd), load.r).href; is expecting a source map as a valid URL - but instead it's getting |
@zachsa this error - |
@guybedford - I'm not sure I understand. Here is one of the imports in my import map: {
"imports": {
"@emotion/react": "https://ga.jspm.io/npm:@emotion/[email protected]/dist/emotion-react.browser.esm.js",
... and that JavaScript source file from ga.jspm.io includes a source map url ... some JavaScript
//# sourceMappingURL=emotion-react.browser.esm.js.map With the following globalThis.esmsInitOptions = {
...(globalThis.esmsInitOptions || {}),
shimMode: true,
async fetch(url, options) {
const res = await fetch(url, options)
if (!res.ok) return res
const source = await res.text()
const transformed = await compile(url, source)
return new Response(new Blob([transformed], { type: 'application/javascript' }))
},
} Implementing the This works async function compile(url, source) {
if (url.includes('http://localhost')) {
const transformed = transform(source, {
presets: [presetReact],
})
return transformed.code
}
return source.replace(/\/\/#.*/, '')
} But this does not async function compile(url, source) {
if (url.includes('http://localhost')) {
const transformed = transform(source, {
presets: [presetReact],
})
return transformed.code
}
return source // <- This is different
} The only difference is that in the working so in the function pushSourceURL (commentPrefix, commentStart) {
const urlStart = commentStart + commentPrefix.length;
const commentEnd = source.indexOf('\n', urlStart);
const urlEnd = commentEnd !== -1 ? commentEnd : source.length;
pushStringTo(urlStart);
console.log(load.u) // prints https://ga.jspm.io/npm:[email protected]/index.js
console.log(urlStart, urlEnd, source.slice(urlStart, urlEnd)) // This prints the source map URL as it comes from the JSPM CDN => 7765 7777 index.js.map
resolvedSource += new URL(source.slice(urlStart, urlEnd), load.r).href;
lastIndex = urlEnd;
} So in my code, why would the It doesn't seem to matter whether I'm in |
Actually.. Thinking of this. With shimMode false I would not expect the type="module-shim" script to be executed at all, but it is. |
Any single module-shim script will enable shim mode automatically. @zachsa I believe the point remains that your resolve hook is incorrect, even if the bug only triggers when there is a source map. I would suggest logging the value of |
However I can fix the issue by changing the fetch hook: async fetch(url, options) {
const res = await fetch(url, options)
if (!res.ok) return res
const source = await res.text()
const transformed = url.includes('http://localhost') ? await compile(url, source) : source
return new Response(new Blob([transformed], { type: 'application/javascript' }))
} Looking at the docs I see that I should return the res, not the res text if I want to escape this hook. This works: async fetch(url, options) {
const res = await fetch(url, options)
if (!res.ok) return res
if (url.includes('http://localhost')) {
const source = await res.text()
const transformed = await compile(url, source)
return new Response(new Blob([transformed], { type: 'application/javascript' }))
}
return res
} |
Thanks for your patience. Last question - what would the benefit of a custom Specifically I'm wondering if there are possibilities that I haven't thought of. In @mix0000's code above: const ignoreMap = {
react: "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/esm/react.production.min.js",
reactDOM: "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/esm/react-dom.production.min.js"
}
window.esmsInitOptions = {
shimMode: true,
mapOverrides: true,
skip: Object.values(ignoreMap),
resolve: function (id, parentUrl, defaultResolve) {
if (id in ignoreMap) {
return ignoreMap[id];
}
return defaultResolve(id, parentUrl);
}
} Would including a resolve hook / skip of all imports present in an import map effect my example in any way? |
Hi Guy,
In attempting to write a es-module-shims-plugin to parse JSX, I'm finding that files imported via
<script type="module-shim"> can't seem to resolve specifiers declared in a
<script type="importmap">`. For example,Working
Ad then in the HTML file
But this errors
(The HTML is the same), I get the following error:
Changing the script type to "module", and I see that React can be resolved from the imported module (
./jsx-app.js
):Please let me know if this issue makes sense - I was expecting to be able to resolve specifiers from the scripts imported via type "module-shim"
The text was updated successfully, but these errors were encountered: