Reputation: 391
I wanted to load some module type definitions in Monaco inside a react app for tutorial purposes.
I actually managed to get it working after much pain but in a very hacky way. So I'm not asking how to do it, but rather, how to do it right.
The part I hope I can solve with Webpack is that right now I made a Node.js script that take all the .d.ts
file it can find in the build folder of a private npm module and save them inside a large .json file.
in this format
{ [filePath]: 'fileContentAsString' }
Then in react I import that json and call addExtraLib
for each one.
for (const filePath in sdkTypesJson) {
// We add every .d.ts file to Monaco
monaco.languages.typescript.typescriptDefaults.addExtraLib(
sdkTypesJson[filePath],
'file:///' + filePath.replace('dist/src/', '')
);
}
Is there a way with some webpack magic to avoid having to create the json file?
Upvotes: 3
Views: 2076
Reputation: 391
After two days of pain and misery, this is what I found work the best.
const files = require.context('!!raw-loader!./node_modules/my-module-name/dist/src/', true, /\.d.ts$/);
files.keys().forEach((key: string) => {
// We add every .d.ts file to Monaco
Monaco.languages.typescript.typescriptDefaults.addExtraLib(
files(key).default,
'file:///node_modules/my-module-name/' + key.substr(2)
);
});
require.context
tell Webpack to bundle all file from that path that matches the regex. Here I get all the .d.ts
files containing the type description of my module.
!!raw-loader!
tell to load the file without trying to execute it. It would crash the browser. You need to install the raw-loader module for Webpack.
Then you can have code like this in your embedded Monaco instance and have the typing Intellisense working like it would in vs-code.
import {
function
} from '@my-module-name';
export default async function run(value: string) {
alert('Code ran. Value passed is ' + value);
};
Back in your app code, you can then get the code from Monaco and eval the code. In my case when the user press a 'run' button in a react app.
const model = editor.current.getModel();
if (model && sdk.current && editorType === 'text') {
// Be cool if Monaco worker could transpile the file to js. Don't work for some reason.
// Monaco.languages.typescript.getTypeScriptWorker()
// .then(function(worker) {
// worker(model.uri)
// .then(function(client) {
// client.getEmitOutput(model.uri.toString()).then((output: any) => {
// console.log(output);
// });
// });
// });
// @ts-ignore
const js = window.ts.transpile(model.getValue());
const setup = `const exports = { default: null };`;
const final = setup + ' ' + js;
try {
const runMethod = eval(final);
const newState = await runMethod('My value');
} catch (error) {
console.error(error);
alert(error);
}
}
In theory, you should be able to ask the Monaco typescript worker to transpile the file for you but I couldn't manage to make it work.
So I loaded the typescriptServices
in my react index.html file with this line for now.
<script src="https://unpkg.com/typescript@latest/lib/typescriptServices.js"></script>
Upvotes: 7