microsoft/ClearScript

Add support for import-maps

fuweichin opened this issue · 2 comments

Now all major browsers, as well as Node.js and Deno, support import-maps natively or with a custom loader. see

Some third party libs, e.g. xpath-analyzer, have used bare module specifiers, to use these libs in ClearScript runtime, currently I need to modify source code, transforming bare module specifiers into relative paths.

Hi @fuweichin,

ClearScript also supports custom loaders. You shouldn't have to modify any source code.

Let's walk through a simple example. Suppose you've installed xpath-analyzer as follows:

> npm install -g xpath-analyzer

Here are the relevant paths (on Windows):

var npmGlobalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "npm");
var xpathAnalyzerPath = Path.Combine(npmGlobalPath, "node_modules", "xpath-analyzer");
var xpathLexerPath = Path.Combine(xpathAnalyzerPath, "node_modules", "xpath-lexer");

And here's a custom loader that supports simple mappings:

public class LoaderWithMap : DefaultDocumentLoader {
    private readonly IReadOnlyDictionary<string, string> _map;
    public LoaderWithMap(IDictionary<string, string> map) => _map = new ReadOnlyDictionary<string, string>(map);
    public override Task<Document> LoadDocumentAsync(DocumentSettings settings, DocumentInfo? sourceInfo, string specifier, DocumentCategory category, DocumentContextCallback contextCallback) {
        if (_map.TryGetValue(specifier, out var replacement)) specifier = replacement;
        return base.LoadDocumentAsync(settings, sourceInfo, specifier, category, contextCallback);
    }
}

You're now ready to set up the script engine:

using var engine = new V8ScriptEngine();
engine.DocumentSettings.AccessFlags = DocumentAccessFlags.EnableFileLoading;
engine.DocumentSettings.Loader = new LoaderWithMap(new Dictionary<string, string> {
    { "xpath-analyzer", Path.Combine(xpathAnalyzerPath, "dist", "xpath_analyzer.esm.js") },
    { "xpath-lexer", Path.Combine(xpathLexerPath, "dist", "xpath_lexer.esm.js") }
});

And run a test script:

engine.AddHostType(typeof(Console));
engine.Execute(new DocumentInfo { Category = ModuleCategory.Standard }, @"
    import XPathAnalyzer from 'xpath-analyzer';
    const analyzer = new XPathAnalyzer('123 + 456');
    Console.WriteLine(JSON.stringify(analyzer.parse(), undefined, 2));
");

The output looks as expected:

{
  "type": "additive",
  "lhs": {
    "type": "number",
    "number": 123
  },
  "rhs": {
    "type": "number",
    "number": 456
  }
}

Please let us know if this approach works for you.

Good luck!

It works.
Thank you!

B.T.W. It would be better to add an example about import-maps to FAQ Tutorial.