In my extension vscode-antlr4 I don't use an import map. Instead I set up my project such that for the webview contents I have an own tsconfig.json file which causes tsc to produce ES2022 modules (while for the extension itself CommonJS is used).
{
"compilerOptions": {
"declaration": true,
"module": "ES2022",
"target": "ES2022",
"outDir": "../../out",
"removeComments": true,
"noImplicitAny": true,
"inlineSources": true,
"inlineSourceMap": true,
"isolatedModules": false,
"allowSyntheticDefaultImports": true,
"allowUmdGlobalAccess": true, // For D3.js
"moduleResolution": "node",
"experimentalDecorators": true,
"strictNullChecks": true,
"alwaysStrict": true,
"composite": true,
"rootDir": "../.."
},
"include": [
"./*.ts"
],
"exclude": []
}
This setup allows me to import 3rd party libs, like antlr4ts and d3 from the node_modules folder. I can now import these webview scripts in my webview content code like shown for example in the railroad diagram provider.
public generateContent(webview: Webview, uri: Uri, options: IWebviewShowOptions): string {
const fileName = uri.fsPath;
const baseName = basename(fileName, extname(fileName));
const nonce = this.generateNonce();
const scripts = [
FrontendUtils.getMiscPath("railroad-diagrams.js", this.context, webview),
];
const exportScriptPath = FrontendUtils.getOutPath("src/webview-scripts/GraphExport.js", this.context,
webview);
if (!this.currentRule || this.currentRuleIndex === undefined) {
return `<!DOCTYPE html>
<html>
<head>
${this.generateContentSecurityPolicy(webview, nonce)}
</head>
<body><span style="color: #808080; font-size: 16pt;">No rule selected</span></body>
</html>`;
}
let diagram = `<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8"/>
${this.generateContentSecurityPolicy(webview, nonce)}
${this.getStyles(webview)}
<base href="${uri.toString(true)}">
<script nonce="${nonce}">
let graphExport;
</script>
</head>
<body>
${this.getScripts(nonce, scripts)}`;
if (options.fullList) {
diagram += `
<div class="header">
<span class="rrd-color"><span class="graph-initial">Ⓡ</span>rd </span>All rules
<span class="action-box">
Save to HTML<a onClick="graphExport.exportToHTML('rrd', '${baseName}');">
<span class="rrd-save-image" />
</a>
</span>
</div>
<div id="container">`;
const symbols = this.backend.listTopLevelSymbols(fileName, false);
for (const symbol of symbols) {
if (symbol.kind === SymbolKind.LexerRule
|| symbol.kind === SymbolKind.ParserRule
|| symbol.kind === SymbolKind.FragmentLexerToken) {
const script = this.backend.getRRDScript(fileName, symbol.name);
diagram += `<h3 class="${symbol.name}-class">${symbol.name}</h3>
<script nonce="${nonce}">${script}</script>`;
}
}
diagram += "</div>";
} else {
diagram += `
<div class="header">
<span class="rrd-color">
<span class="graph-initial">Ⓡ</span>ule
</span>
${this.currentRule} <span class="rule-index">(rule index: ${this.currentRuleIndex})
</span>
<span class="action-box">
Save to SVG
<a onClick="graphExport.exportToSVG('rrd', '${this.currentRule}');">
<span class="rrd-save-image" />
</a>
</span>
</div>
<div id="container">
<script nonce="${nonce}" >${this.backend.getRRDScript(fileName, this.currentRule)}</script>
</div>`;
}
diagram += `
<script nonce="${nonce}" type="module">
import { GraphExport } from "${exportScriptPath}";
graphExport = new GraphExport();
</script>
</body></html>`;
return diagram;
}
As you can see I set a base href in the code, which helps with relative imports. The entire implementation is split into two parts. One is in the tag where the graphExport
variable is declared, to allow it to be used by event handling code. This variable is then initialized in the tag, where the GraphExport
class is imported.