- 
                Notifications
    You must be signed in to change notification settings 
- Fork 1.2k
feat(editor): Implement dynamic type loading for TS/JS autocompletion #6425
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
base: master
Are you sure you want to change the base?
Conversation
| ✅ Deploy Preview for reliable-cocada-166884 ready!
 To edit notification comments on pull requests, go to your Netlify project configuration. | 
0a4592c    to
    d44da42      
    Compare
  
    4f32187    to
    6b51d07      
    Compare
  
    | // Transforms the script into an executable format using the function defined above. | ||
| const transformedScript = transformScriptForRuntime(script) | ||
|  | ||
| console.log('--- [ScriptRunner] Original Script ---') | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for which reason do you need to transform the script? isn't the transpiling already done in the script runner side?
Also please try to run the script storage.test.ts from the basic contract template (compile the contract before). It fails with this error:
{"message":"Failed to fetch dynamically imported module: https://cdn.jsdelivr.net/npm/ethers-lib/+esm","name":"TypeError","stack":"TypeError: Failed to fetch dynamically imported module: https://cdn.jsdelivr.net/npm/ethers-lib/+esm"}  
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't transform relative imports @hsy822
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yann300 and @bunsenstraat. Thank you for the feedback! I’ve made updates based on your comments and would like to share the changes with you.
Intelligent Transformation for Handling Built-in and External Libraries
I've implemented a system that analyzes which libraries are imported in a script and transforms the code in two different ways depending on the context.
When Only Built-in Libraries Are Used
If the script only imports built-in libraries such as ethers or noir, the code is passed to the Script Runner with minimal modification.
The built-in bundler of Script Runner handles these imports quickly and reliably.
When External Libraries Are Mixed In
If even one external library (e.g., axios) is included, the entire script is wrapped in an async environment.
- External libraries (axios): dynamically imported from CDN using a reliable import(...)statement.
- Built-in libraries (ethers): converted to require(...)to ensure stability in async environments.
This allows scripts to safely import and use new external libraries without breaking existing functionality.
Enhanced Package.json Interpretation for Type Resolution
The type loader (type-fetcher) for autocomplete has been upgraded to better understand various NPM package structures.
Previous Issue
Previously, the loader failed to locate type definitions for complex libraries like noir, which use the exports field in package.json or type file extensions like .d.cts.
Improved Logic
The new loader now works like a smart detective, checking multiple clues in order:
- Primary: Looks for the official typespath in theexportsfield (most accurate).
- Secondary: Falls back to the top-level typesfield if not found.
- Tertiary: If neither exists, it guesses the file path as before.
Thanks to this improvement, type definitions for modern, complex libraries such as noir are now correctly resolved, making editor autocomplete much more stable and comprehensive.
You can verify that this works by selecting noir in your script configuration and running the following code:
const { expect } = require('chai')
import { compile, createFileManager } from '@noir-lang/noir_wasm'
import { Noir } from '@noir-lang/noir_js'
import { UltraPlonkBackend } from '@aztec/bb.js'
const mainNrContent = `
fn main(age: u8) {
  assert(age >= 18);
}
`
const nargoTomlContent = `
[package]
name = "age_verification"
version = "0.1.0"
type="bin"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]
`
async function getCircuit() {
  const fm = createFileManager('/')
  const tomlBytes = new TextEncoder().encode(nargoTomlContent)
  const mainBytes = new TextEncoder().encode(mainNrContent)
  await fm.writeFile('./src/main.nr', new Blob([mainBytes]).stream())
  await fm.writeFile('Nargo.toml', new Blob([tomlBytes]).stream())
  const result = await compile(fm)
  if (!('program' in result)) {
    throw new Error('Compilation failed')
  }
  return result.program
}
describe('Noir Program Test', () => {
  it('should compile, execute, prove, and verify', async () => {
    const noir_program = await getCircuit()
    const inputs = { age: 20 }
    // JS Proving
    const program = new Noir(noir_program)
    const { witness } = await program.execute(inputs)
    const backend = new UltraPlonkBackend(noir_program.bytecode)
    const proof = await backend.generateProof(witness)
    // JS verification
    const verified = await backend.verifyProof(proof)
    expect(verified, 'Proof fails verification in JS').to.be.true
  })
})There are still some remaining issues and additional test cases to cover, but this shares the core implementation logic for feedback and further discussion.
| really cool but aside from relative imports you will override deps we set in our script runner builds with (async () => { so if we have ethers6 or 5 built this will override that versioning... but we DO have a dep json available https://remix-project-org.github.io/script-runner-generator/projects.json and we do some webpack stuff in the builds to make things work like so I don't think we should transform everything but maybe yeah https://cdn.jsdelivr.net/npm/@aztec/bb.js@2.0.3/+esm the point is let's test these things. we have tests for our implementations https://github.com/remix-project-org/script-runner-generator/blob/main/test/src/tests/noir.test.ts we should check | 
fb9642d    to
    7a659ff      
    Compare
  
    0093f62    to
    2596dee      
    Compare
  
    | See here you transform the local import to a require, thats not necessary at all. So relative imports and ones that are in the dep of the script runner already you can leave alone. No transforms. Otherwise I will have to change script runner to handle relative requires.  | 
| 
 Hi @bunsenstraat, To address your previous feedback and the  The script transformation now works as follows: 
 With this “split handling” approach, the  I’ve also confirmed that the previous test script (using  import { ethers } from 'ethers'; // Built-in for 'Ethers 6'
import axios from 'axios';      // External
console.log('--- Testing Selective Transformation ---');
(async () => {
  try {
    console.log('Built-in dependency (ethers) loaded. Version:', ethers.version);
    console.log('Dynamically imported dependency (axios) is loaded:', typeof axios === 'function');
    const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
    if (response.data) {
      console.log('Axios call successful. Received data ID:', response.data.id);
    }
    console.log('SUCCESS: Both built-in and dynamic imports worked correctly!');
  } catch (e) {
    console.error('Test failed:', e.message);
  }
})();Would appreciate another round of review. thank you! | 
Description
This PR introduces an initial implementation of a dynamic type acquisition system aimed at significantly improving the autocompletion experience for TypeScript and JavaScript files.
Previously, the editor relied on static, bundled type definitions (
web-types.ts), which were often outdated and limited support to a few pre-defined libraries. This change seeks to address these limitations.Now, when a user imports an NPM package (e.g.,
import { createPublicClient } from 'viem'), the system automatically fetches the corresponding type definitions (.d.ts) and their dependencies from thejsdelivrCDN. These types are then injected into the editor, providing a more up-to-date and useful autocompletion experience for a wider range of libraries.Implementation Details
editor.js(EditorPlugin):_onChangemethod now detects newimportstatements to trigger the type loading process.type-fetcher.ts(Type Acquisition Service):.d.tsfiles for a given package and its dependencies.package.json(exports,typesfields) to find all entry points and handles packages that rely on the@typesrepository.Current Status & Request for Feedback
The core functionality is now in place and appears to be working correctly with several key libraries. However, this is an initial version, and further testing, refinements, and bug fixes will be necessary.
We are requesting feedback on the overall architecture and implementation. Your insights will be crucial as we move forward with the next steps.
Next Steps
Test Cases
The system has been confirmed to work with the following scripts. They can be used to verify both autocompletion and
Run Scriptexecution.Viem & Viem Subpaths
Ethers v6
Lodash-es (from
@types)Axios
Web3.js
Related Issues
Fixes #6410