Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use addExtraLib in Monaco with an external type definition

I can see how to use addExtraLib in Monaco to add an ambient declaration file. What's not clear is how to use this function with an external declaration file so that Typescript code in the editor can do a:

import * as External from "external" 
    
External.foo();

On the Monaco set-up side, this doesn't seem to work:

 // compiler options
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
    target: monaco.languages.typescript.ScriptTarget.ES2016,
    allowNonTsExtensions: true,
    moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
    module: monaco.languages.typescript.ModuleKind.CommonJS,
    noEmit: true,
    noLib: true,
    typeRoots: ["node_modules/@types"]
});

// extra libraries
monaco.languages.typescript.typescriptDefaults.addExtraLib(
    'export declare function foo():string;', 'node_modules/@types/external/index.d.ts');

monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
    noSemanticValidation: false,
    noSyntaxValidation: false
like image 377
Joe Wood Avatar asked Mar 27 '17 23:03

Joe Wood


3 Answers

Joe's answer didn't work for me, fixed by prefixing the external type definition file path with file:///

Here's an updated example for the playground:

monaco.languages.typescript.typescriptDefaults.addExtraLib(
    'export declare function add(a: number, b: number): number',
    'file:///node_modules/@types/math/index.d.ts'
);

const model = monaco.editor.createModel(
    `import {add} from 'math';\nconst x = add(3, 5);\n`,
    'typescript',
    monaco.Uri.parse('file:///main.tsx')
);

monaco.editor.create(document.getElementById('container'), {model});

It's not necessary to provide the compiler options and diagnostic options.

like image 41
Alexey Lebedev Avatar answered Sep 27 '22 16:09

Alexey Lebedev


After playing around a little I found a solution. Basically, the file has to be loaded using createModel with an explicit file URL. If you do this then the relative file path for node_module/@types works. Here's my working solution that can be used in the playground:

// compiler options
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
    target: monaco.languages.typescript.ScriptTarget.ES2016,
    allowNonTsExtensions: true,
    moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
    module: monaco.languages.typescript.ModuleKind.CommonJS,
    noEmit: true,
    typeRoots: ["node_modules/@types"]
});

// extra libraries
monaco.languages.typescript.typescriptDefaults.addExtraLib(
    `export declare function next() : string`,
    'node_modules/@types/external/index.d.ts');

monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
    noSemanticValidation: false,
    noSyntaxValidation: false
})

var jsCode = `import * as x from "external"
    const tt : string = x.dnext();`;

monaco.editor.create(document.getElementById("container"), {
    model: monaco.editor.createModel(jsCode,"typescript",new monaco.Uri("file:///main.tsx")), 
});
like image 169
Joe Wood Avatar answered Sep 27 '22 16:09

Joe Wood


As of April 2021 ([email protected]), I wasn't able to get either of the prior solutions working without some additional details based on monaco-editor#2295, monaco-editor#1839, and https://stackoverflow.com/a/63349650. My use case required making type definitions available from several existing NPM packages (not just arbitrary paths to files) and this may have affected the solution. To summarize, I needed to:

  1. Bundle all of the .d.ts files from each package into a single file. TypeScript doesn't make this easy, and so instead I used dts-bundle-generator, but other solutions exist.
  2. Import the .d.ts content for each package using raw-loader or other plain-text loading alternatives.
  3. Call addExtraLib with the source for each module, adding an explicit declare module 'module-name' to the source code.

Full example below:

import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';

import source1 from '!!raw-loader!./types/package-one.d.ts';
import source2 from '!!raw-loader!./types/package-two.d.ts'

monaco.languages.typescript.typescriptDefaults.addExtraLib(
  `declare module '@my-project/package-one' { ${source1} }`,
  'file:///node_modules/@my-project/package-one/index.d.ts' // irrelevant?
);
monaco.languages.typescript.typescriptDefaults.addExtraLib(
  `declare module '@my-project/package-two' { ${source2} }`,
  'file:///node_modules/@my-project/package-two/index.d.ts' // irrelevant?
);

monaco.editor.create(document.getElementById('root'), {
    value: `
import { Foo } from '@my-project/package-one';

const foo = new Foo();
`,
    language: 'typescript',
    theme: 'vs-dark'
});
like image 40
Don McCurdy Avatar answered Sep 27 '22 17:09

Don McCurdy