val-town / codemirror-ts

lint, hover, and autocomplete extensions for CodeMirror + TypeScript
https://val-town.github.io/codemirror-ts/
ISC License
145 stars 12 forks source link

How to inject additional code/context before sending to language server #32

Closed huypham50 closed 3 months ago

huypham50 commented 5 months ago
Screenshot 2024-06-30 at 4 44 42 PM

I am working on a side project to execute code on the browser. However, the current code block will have access to some additional information, something like this:

"use strict";
return (async () => {
  const scope = { hello: 'world', username: 'tmcw', version: 1 };

  ${codeEditorSnippet} // code from CM6 will be transpiled to JavaScript before injected here for execution
})();

With the current setup, there will always be these two lint issues:

Cannot find name 'scope'.

A 'return' statement can only be used within a function body.

Would love any advice or suggestions on how to tackle this, thank you!

tmcw commented 4 months ago

Sure, so definitely two issues:

To provide scope in context, you'd want to include a type definition to the virtual typescript environment - in the createVirtualTypeScriptEnvironment call in typescript/vfs https://www.npmjs.com/package/@typescript/vfs you should be able to add extra types. There are a bunch of ways, but one would be to add another file to fsMap, which is a Map object, with something that defines the value as being available in scope, like

declare var scope: { hello: string };

For the return statement, it'll be trickier… bare return statements outside of functions are not going to be valid JavaScript. I don't exactly what the simplest/cleanest solution would be. You could parse that code with TypeScript's AST parser and replace return with void (with two extra spaces to keep it the same size), to make the code valid TS for the type checker?

nandorojo commented 4 months ago

Actually running into a similar thing with the return statement there. Interesting idea to override it to void, I'll try this.

huypham50 commented 3 months ago

I ended up forking the library and doing something like this!

// createWorker.ts
updateFile: ({ path, code }: { path: string; code: string }) => {
    if (!env) return;

    const updatedCode = `
      (() => {
        ${code}
      })
    `;

    createOrUpdateFile(env, path, updatedCode);
},

// getLints.ts
const diagnostics = [...syntaticDiagnostics, ...semanticDiagnostics]
    .filter(isDiagnosticWithLocation)
    .map((d) => ({
        ...d,
        start: d.start - OFFSET, // OFFSET is the amount shifted by nesting code inside updatedCode
    }));