Skip to content

[Advice needed] feat: Have injectScript return the return value of the script#1765

Draft
ion1 wants to merge 1 commit intowxt-dev:mainfrom
ion1:inject-script-return-value
Draft

[Advice needed] feat: Have injectScript return the return value of the script#1765
ion1 wants to merge 1 commit intowxt-dev:mainfrom
ion1:inject-script-return-value

Conversation

@ion1
Copy link
Copy Markdown
Contributor

@ion1 ion1 commented Jun 20, 2025

Overview

A sketch for having injectScript return the result value from the script. The code works, but it currently has changes to virtual/unlisted-script-entrypoint.ts that only work with injectScript while being broken with the browser.scripting API.

I would like to get feedback on how you would like that to be resolved. Some options I can think of:

  • Have the entrypoint detect whether it is being run from injectScript or executeScript. I do not know how to achieve that reliably. document.currentScript can be overridden by the web page using Object.defineProperty, so its value might not be a reliable way to determine that.
  • Build separate entrypoints depending on whether the script is injected using injectScript or executeScript.

Manual Testing

Use injectScript on a script which either returns a value or throws an exception; see that injectScript returns the value or rethrows the exception.

Related Issue

#1754 proposes having a way to pass an UnlistedScriptDefinition rather than the filename to injectScript. If something like that was implemented, the type of the return value of injectScript could be based on the type of main within the UnlistedScriptDefinition.

@ion1 ion1 requested review from Timeraa and aklinker1 as code owners June 20, 2025 08:15
@netlify
Copy link
Copy Markdown

netlify Bot commented Jun 20, 2025

Deploy Preview for creative-fairy-df92c4 ready!

Name Link
🔨 Latest commit 3aebc7c
🔍 Latest deploy log https://app.netlify.com/projects/creative-fairy-df92c4/deploys/688fcde20acd6c0008bab271
😎 Deploy Preview https://deploy-preview-1765--creative-fairy-df92c4.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@ion1 ion1 force-pushed the inject-script-return-value branch 2 times, most recently from 1f9dab4 to ca3ca14 Compare June 20, 2025 15:04
@ion1 ion1 marked this pull request as draft June 30, 2025 05:47
@ion1 ion1 changed the title [WIP sketch] feat: Have injectScript return the result value from the script [Advice needed] feat: Have injectScript return the result value from the script Jun 30, 2025
@ion1 ion1 force-pushed the inject-script-return-value branch 3 times, most recently from 5b335b1 to d72f1ca Compare August 3, 2025 20:26
@ion1 ion1 changed the title [Advice needed] feat: Have injectScript return the result value from the script [Advice needed] feat: Have injectScript return the return value of the script Aug 3, 2025
@ion1 ion1 force-pushed the inject-script-return-value branch from d72f1ca to 3aebc7c Compare August 3, 2025 21:00
@PatrykKuniczak
Copy link
Copy Markdown
Collaborator

@ion1 Are you have a plan to go on with that?

If yes, let's tell us, what type of advice you need, what info we can provide to let you through.

@PatrykKuniczak
Copy link
Copy Markdown
Collaborator

@ion1 One more poke :)

@ion1
Copy link
Copy Markdown
Contributor Author

ion1 commented Apr 25, 2026

Hi,

Sorry I didn't manage to respond more quickly.

The issue is that code executed via injectScript needs to output its result value via communication through the DOM while code executed via browser.scripting needs to simply return it.

I'm not sure what would be the best way to handle the difference. Separate entry points? Some kind of reliable detection within a single entry point? I would appreciate your thoughts on this.

The draft just makes it work for injectScript while neglecting browser.scripting.

I am still interested in this feature, but unfortunately I cannot make any promises as to when I will have the energy to work on it next.

@PatrykKuniczak
Copy link
Copy Markdown
Collaborator

@ion1 Thanks, i'll take a look on that, maybe today and try to give you my feedback.
But anyway we need approval for selected solution from @aklinker1

@ion1 ion1 force-pushed the inject-script-return-value branch from 3aebc7c to c0d95c6 Compare April 25, 2026 12:28
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 25, 2026

Deploy Preview for creative-fairy-df92c4 ready!

Name Link
🔨 Latest commit c0d95c6
🔍 Latest deploy log https://app.netlify.com/projects/creative-fairy-df92c4/deploys/69ecb36cd23a6f0008ce4044
😎 Deploy Preview https://deploy-preview-1765--creative-fairy-df92c4.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@ion1
Copy link
Copy Markdown
Contributor Author

ion1 commented Apr 25, 2026

Rebased on main.

Copy link
Copy Markdown
Collaborator

@PatrykKuniczak PatrykKuniczak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, before those changes, you need advice from @aklinker1
Because i don't have that much knowledge about project and don't want to make that big decision.

If you'll have suggestion/approval from aklinker and make necessary changes(if needed), i'll review it more precisely 😄

Comment on lines +102 to +112
return (
result != null &&
typeof result === 'object' &&
'type' in result &&
typeof result.type === 'string' &&
((result.type === 'success' && 'value' in result) ||
(result.type === 'error' &&
'error' in result &&
isDeconstructedError(result.error)))
);
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return (
result != null &&
typeof result === 'object' &&
'type' in result &&
typeof result.type === 'string' &&
((result.type === 'success' && 'value' in result) ||
(result.type === 'error' &&
'error' in result &&
isDeconstructedError(result.error)))
);
}
return (
((result.?type === 'success' && 'value' in result) ||
(result.?type === 'error' &&
'error' in result &&
isDeconstructedError(result.error)))
);
}

i hope this works the same way

/**
* The return value of the script.
*/
result: unknown;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be typed
A result object containing the created script element and the return value of the script.

That's mean, you know at least that will be script, and you can pass e.g generic type param here, from that script return type

Comment on lines +13 to +14
const script = document.currentScript;
if (script === null) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const script = document.currentScript;
if (script === null) {
if (document.?currentScript) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be split to at least 3 files.
1 for dispatch helpers, 1 for errors and 1 "main"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷 one file is fine with me

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aklinker1 OOOOOO right.

If this massive ifs, will be shortify this can be fine also for me.

Copy link
Copy Markdown
Member

@aklinker1 aklinker1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, read through it. Makes sense, message passing is really the only way to do this.

Instead of stuffing this into injectScript, maybe we build a new util on top of it. If people want to use it, they can. If they don't, it's tree-stripped out.

import { evalScript } from 'eval-script/parent'

const res = await evalScript("/unlisted.js", ...args)
console.log(res) // "something"
import { defineEvalScript } from 'eval-script/child'

import defineEvalScript((...args) => {
  return "something";
})

That seems like the best approach to me, but the bigger question is if I want to be responsible for maintaining this... I don't think I do. Given how long it took for me to respond to this PR lol. Gotta be picky.

If there's interest, I might be willing to make it an official WXT package, like @wxt-dev/storage. But at least in my use-cases, it just doesn't seem like a util most extensions would need. But I could be wrong.

So how about this... Create an issue for this featur. If it gets, let's say, 50 👍 or other positive reactions I'll be down to add the package for it, maybe even add it to the core wxt.

If you're willing to own the util and fix bugs or implement other features, that would help me out a ton too.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷 one file is fine with me

@PatrykKuniczak
Copy link
Copy Markdown
Collaborator

@ion1 Let's open https://github.com/wxt-dev/wxt/discussions and link this PR, it can be easier to gain 50 👍

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

Successfully merging this pull request may close these issues.

3 participants