Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not working #5

Open
nandorojo opened this issue Jun 14, 2024 · 5 comments
Open

Not working #5

nandorojo opened this issue Jun 14, 2024 · 5 comments

Comments

@nandorojo
Copy link

Screenshot 2024-06-14 at 3 18 22 PM

Tried the example app using npm i && npm run dev, but it doesn't pick up the types. I'll see if I can find why. My guess is it's not updating the TS env accordingly, because the network tab does indeed show the files fetching.

@nandorojo
Copy link
Author

nandorojo commented Jun 14, 2024

I can't seem to get this working. However, I'll share the solution I made myself which has been working for me. Definitely open to feedback there though.

Usage

extensions: [..., tsAta({ env })]

Code

import { EditorView } from '@codemirror/view'
import { tsFacet } from '@valtown/codemirror-ts'
import {
  setupTypeAcquisition,
  // @ts-ignore
  getReferencesForModule,
} from '@typescript/ata'
import typescript from 'typescript'
import type { VirtualTypeScriptEnvironment } from '@typescript/vfs'
import { createOrUpdateFile } from 'app/features/code-editor/typescript/vendor/sync/update'
import { type VirtualTypeScriptEnvironment } from '@typescript/vfs'

const getReferences =
  getReferencesForModule as typeof import('@typescript/ata/src/index').getReferencesForModule
 
/**
 * Get dependencies for the TypeScript environment
 */
export function tsAta({ env }: { env: VirtualTypeScriptEnvironment }) {
  console.log('[ata][ts]')
  const ata = setupTypeAcquisition({
    typescript,
    projectName: 'typescript-ata',
    logger: console,
    delegate: {
      receivedFile: (code: string, path: string) => {
        // Add code to your runtime at the path...
        createOrUpdateFile(env, path, code)
      }
    },
  })
  let first = true
  return EditorView.updateListener.of((update) => {
    const config = update.view.state.facet(tsFacet)
    if (!config) return
    if (!update.docChanged && !first) return
    first = false

    let content = update.state.doc.toString() || ' '

    getReferences(typescript, content).forEach(({ module }) => {
      if (module.startsWith('npm:')) {
        // create a declaration file from npm:package → package
        const [name, version] = module.slice(4).split('@')

        content = content.replaceAll(`"${module}"`, `'${module}'`) // single quotes

        content = content.replaceAll(
          `'${module}'`,
          // the new line is important to not break require statements etc with the comment
          `'${name}' // types: ${version ?? 'latest'}\n`
        )

        createOrUpdateFile(
          env,
          `node_modules/${name}/__custom-deno-types.d.ts`,
          `
declare module '${module}' {
  export * from '${name}'
}            
`
        )
      }
    })

    ata(content)
  })
}

export function createOrUpdateFile(
  env: VirtualTypeScriptEnvironment,
  path: string,
  code: string
): void {
  if (!env.getSourceFile(path)) {
    env.createFile(path, code)
  } else {
    env.updateFile(path, code)
  }
}

@tmcw
Copy link
Member

tmcw commented Jun 21, 2024

That seems brilliant! I'll tinker with something similar.

@nandorojo
Copy link
Author

nandorojo commented Jul 28, 2024

Just to add to the discussion here:

The solution I posted above has been working. However, using the TS ATA directly in the browser has some downsides I'm finding:

  1. It sends a ton of requests from the browser directly, which feels like it's slowing things down a bit...
  2. There's not much caching going on at all

I wonder if there could be a way to run it via a middleware server or something that could:

  1. Cache the results of files
  2. Run the actual TS server quickly to generate the final .d.ts file(s) that the web browser will use. If possible bundle it into one file. Maybe this could just use tsc or a faster equivalent?
  3. Return that bundled .d.ts file to the browser, and cache it for future requests

For example, you could call your server with something like request('/ts-ata', { code }) and it returns the .d.ts file(s) to be used by the language server on the browser.

Have you guys experimented with something like this @tmcw?

@nandorojo
Copy link
Author

Some downsides to the above approach:

  1. Currently ata provides a listener for each file that comes in as it streams in, which is useful for getting the types of some packages while others are loading. So for the first time, this would have a trade-off.

Potential Solutions:

  1. Pre-compute + cache for users from the DB directly.
  2. Or, use HTTP streams to give the response LLM-style when it's not a cache hit. And if it is a cache hit, return it.

One clear issue with this approach is that, every time someone types, you need a new cache key. However, this can be easily solved by using a hash of the result of getReferencesForModule as the cache key. We only need to cache things based on which modules are in a file so that should do the trick, I think.

@nandorojo
Copy link
Author

Lastly, one simple thing I did to make this much faster was to only call ata(content) if the result of getReferences was greater than 0.

(Apologies for the many Sunday morning messages)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants