Skip to content

Support for import(blobUrls) #61013

Open
Open

Description

@strogonoff

What is the problem this feature will solve?

The same thing that vm.SourceTextModule(yourCodeString, { identifier, context }) does, but in a way compatible with other JS runtimes.

Useful for implementing extensions, for example.

What is the feature you are proposing to solve the problem?

Compatibilty with other JS runtimes when it comes to import() and blob URLs.

Example code:

code = 'console.log("hi")'
blob = new Blob([code], { type: 'text/javascript' })
url = URL.createObjectURL(blob)
import(url)

References:

What alternatives have you considered?

vm.SourceTextModule(yourCodeString, { identifier, context })

Activity

  1. TejasSathe010 commented on Jan 13, 2026

    @TejasSathe010
  2. jimmywarting commented on Jan 27, 2026

    @jimmywarting
  3. jimmywarting commented on Jan 27, 2026

    @jimmywarting

    for now, i got this userland solution going for me atm...

    import { isMainThread } from 'node:worker_threads'
    import { resolveObjectURL } from 'node:buffer'
    import module from 'node:module'
    
    /** @type {Map<string, Blob | PromiseWithResolvers<Blob>>} */
    const map = new Map()
    
    if (isMainThread) {
      const { port1, port2 } = new MessageChannel()
    
      module.registerHooks({
        resolve(specifier, context, nextResolve) {
          if (specifier.startsWith('blob:')) {
            const blob = resolveObjectURL(specifier)
    
            if (!['application/javascript', 'text/javascript'].includes(blob.type)) {
              throw new TypeError('Unsupported blob type: ' + blob.type + '. expected text/javascript MIME type.')
            }
    
            port1.postMessage({ blob, specifier })
          }
          return nextResolve(specifier, context, nextResolve)
        }
      })
    
      module.register(import.meta.url, {
        parentURL: import.meta.url,
        data: { port: port2 },
        transferList: [port2],
      })
    }
    
    async function resolve(specifier, context, nextResolve) {
      if (specifier.startsWith('blob:')) {
        return {
          url: specifier,
          shortCircuit: true,
          format: 'module',
        }
      }
    
      return nextResolve(specifier, context, nextResolve)
    }
    
    async function initialize({ port }) {
      port.addEventListener('message', evt => {
        const { blob, specifier } = evt.data
        const q = map.get(specifier)
        if (q) {
          q.resolve(blob)
        } else {
          map.set(specifier, blob)
        }
      })
      port.start()
    }
    
    async function load(specifier, context, nextLoad) {
      if (specifier.startsWith('blob:')) {
        let blob = map.get(specifier)
    
        if (!blob) {
          const q = Promise.withResolvers()
          map.set(specifier, q)
          blob = await q.promise
        }
    
        const source = await blob.text()
        map.delete(specifier)
    
        return {
          format: 'module',
          shortCircuit: true,
          source,
        }
      }
    
      return nextLoad(specifier, context, nextLoad)
    }
    
    export { initialize, load, resolve }

    Works for me:

      const blob = new Blob(
        [`console.log('Hello from objectURL!');`],
        { type: 'application/javascript' }
      );
      const blobURL = URL.createObjectURL(blob);
      import(blobURL)

    but not for disc blobs with openAsBlob

  4. github-actions commented on Jul 20, 2026

    @github-actions
    Contributor

    This issue has been marked as stale due to 90 days of inactivity.
    It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.

  5. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Jul 20, 2026
  6. strogonoff commented on Jul 23, 2026

    @strogonoff
    Author

    The workaround is quite involved, compared to simplicity of import(blobUrls) if it was implemented.

  7. removed
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestIssues requesting new Node.js features.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions