tl;dr: (how) can I use an ES6 module that imports other modules' exports, in a VS Code extension creating a Webview?
I'm trying to update and improve a VS Code extension first written 4 years ago. The extension creates a webview, using HTML and JavaScript modules. This is the code that used to work:
<head>
<!-- ... -->
<script type='module'>
'use strict';
import { loadFromString as loadSCXML } from '${scxmlDomJs}';
import SCXMLEditor from '${scxmlEditorJs}';
import NeatXML from '${neatXMLJs}';
// …
</script>
</head>
…where the contents of the ${…}
strings were replaced with URIs generated via:
path.join(extensionContext.extensionPath, 'resources', 'scxmlDOM.js')
These days the Webview in VS Code is now locked down for security, and (as I understand it) I need to replace the inline <script>
element with something like the following:
<head>
<!-- ... -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src 'nonce-${nonce}'">
<script nonce="${nonce}" src="${mainJS}"></script>
where mainJS
is a path like above, further wrapped in a call to webview.asWebviewUri(…)
.
If I move my module code into a separate file main.js
, how can it import other modules, when the paths to those modules need to be generated?
I've found several working examples (including the one linked above) for how to make script in webviews work with CORS and nonces, but I cannot find a resource on how to make it work when those scripts are modules. The closest I've found is this question which only might be related, but which is also unanswered.
One solution that works (tested) is to use an import map to map simple names to the URI in the HTML, and modify the main.js
to import by the simple names.
Webview HTML:
<head>
<!-- ... -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src 'nonce-${nonce}'">
<script nonce="${nonce}" type="importmap">
{
"imports": {
"scxmlDOM": "${scxmlDOMJS}",
"scxmlEditor": "${scxmlEditorJS}",
"neatXML": "${neatXMLJS}"
}
}
</script>
<script nonce="${nonce}" type="module" src="${mainJS}"></script>
main.js:
'use strict';
import { loadFromString as loadSCXML } from 'scxmlDOM';
import SCXMLEditor from 'scxmlEditor';
import NeatXML from 'neatXML';
// …
I don't know if the nonce
is strictly needed on the import map <script>
element, but it certainly works with it present.
Note that the ${…}
URIs are not literals, but expected to be replaced with the output from the asWebviewUri()
function.