最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - StandaloneSelf Contained Typescript Injected scripts - Stack Overflow

programmeradmin0浏览0评论

I have a project I am working on where we are creating standalone javascript files that will get injected into a web page to execute a command. Each script is injected at runtime and called as though it is part of a function. So for instance one file might be a command to run a search on google where the command will first select the input field, then set the value, then click the submit button, etc.. These scripts do a variety of things each unique to the page they are going to be injected into.

Each script generally returns a boolean to indicate if the script worked successfully or not. The scripts are written in javascript, but I am looking to improve the readability of these scripts as they can become quite large and would like to move to typescript.

I have currently been exploring using esbuild to compile each typescript file and generate a corresponding javascript file. While this has worked with mostly no issues, I am struggling with how a typescript file can have a return statement without the compiler saying, A 'return' statement can only be used within a function body.ts(1108)

A simple example typescript file could be:

const input document.querySelector('someSelector');
input.value = "someValue";

return true;

When injected into a page it is done like this:

const scriptToInject = loadScript('someScript');
const globalVars = {};

const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
const func = new AsyncFunction(globalVars, codeToRun);
const codeResult = await func(globalVars );

I have a project I am working on where we are creating standalone javascript files that will get injected into a web page to execute a command. Each script is injected at runtime and called as though it is part of a function. So for instance one file might be a command to run a search on google where the command will first select the input field, then set the value, then click the submit button, etc.. These scripts do a variety of things each unique to the page they are going to be injected into.

Each script generally returns a boolean to indicate if the script worked successfully or not. The scripts are written in javascript, but I am looking to improve the readability of these scripts as they can become quite large and would like to move to typescript.

I have currently been exploring using esbuild to compile each typescript file and generate a corresponding javascript file. While this has worked with mostly no issues, I am struggling with how a typescript file can have a return statement without the compiler saying, A 'return' statement can only be used within a function body.ts(1108)

A simple example typescript file could be:

const input document.querySelector('someSelector');
input.value = "someValue";

return true;

When injected into a page it is done like this:

const scriptToInject = loadScript('someScript');
const globalVars = {};

const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
const func = new AsyncFunction(globalVars, codeToRun);
const codeResult = await func(globalVars );
Share Improve this question asked Mar 29 at 17:08 starsoccerstarsoccer 375 bronze badges 7
  • Does it work with // @ts-ignore? You can try to ignore the return statement. – jabaa Commented Mar 29 at 17:26
  • @jabaa yes it does, but the goal is to not have to include that before every return statement – starsoccer Commented Mar 29 at 19:02
  • Do you want to ignore all return statement or only some of them? – jabaa Commented Mar 29 at 20:28
  • FYI see github/gnlow/tsm "Use TypeScript everywhere" – guest271314 Commented Mar 30 at 2:25
  • Well since all my return statements are likely going to error with this, I would want to ignore all of them. I am not sure I follow how TSM would help. I dont want to compile the scripts on demand to JS since they generally do not change often. Id like them to be compiled once. Currently they will be stored in a database and loaded once on startup – starsoccer Commented Mar 30 at 22:00
 |  Show 2 more comments

2 Answers 2

Reset to default -2

Here you go

<script type="module">
  import initSwc, {
    transformSync,
  } from 'https://cdn.jsdelivr/npm/@swc/[email protected]/wasm.js';

  await initSwc();

  let ts = `interface Person {
  name: string;
  age: number;
}
function greet(person: Person) {
  return "Hello, " + person.name + "!";
}

export { greet };`


  let {
    greet
  } = await import(
    `data:text/javascript,${
      transformSync(ts, {}).code
    }`
  );

  console.log(greet({
    name: 'X'
  }));
</script>

I have currently been exploring using esbuild to compile each typescript file and generate a corresponding javascript file.

Overkill.

node, deno, and bun can all convert TypeScript syntax to JavaScript, without deliberately using third-party libraries.

In the case of Node.js, the Amaro module is used, which in turn depends on SWC, which ultimately just strips TypeScript syntax from the file.

I am struggling with how a typescript file can have a return statement without the compiler saying, A 'return' statement can only be used within a function body.ts(1108)

Depends. Deno and Bun may or may not use Microsoft TypeScript's tsc, or might not; might do "type checking" or might not, depending on the case. Becuase in general, tsc is slower than other options. You'll have to read various issues to see this discussion amnong Node.js and Deno maintainers for yourself. Bun does not use tsc at all. Bun uses it's own TypeScript parser.

You can customize "type checking" in Deno.

E.g., deno.json

{
  "nodeModulesDir": "auto",
  "lint": {
    "rules": {
      "tags": ["recommended"],
      "include": [
        "no-irregular-whitespace",
        "constructor-super",
        "eqeqeq",
        "no-async-promise-executor",
        "no-await-in-sync-fn",
        "no-case-declarations",
        "no-global-assign no-node-globals",
        "no-non-null-asserted-optional-chain",
        "no-process-globals",
        "no-unreachable",
        "no-unsafe-negation",
        "no-unused-labels",
        "no-unused-vars",
        "no-undef"
      ],
      "exclude": [
        "no-explicit-any",
        "no-undef"
      ]
    }
  },
  "compilerOptions": {
    "target": "esnext",
    "types": [
      "https://raw.githubusercontent/microsoft/TypeScript/2ac4cb78d6930302eb0a55d07f154a2b0597ae32/src/lib/es2024.arraybuffer.d.ts"
    ],
    "lib": [
      "dom",
      "dom.iterable",
      "dom.asynciterable",
      "deno.ns",
      "deno.unstable"
    ]
  }
}

Not in Node.js or Bun.

In some instances, depending on the tsc being used, it can be far behind browser implementations.

For example, resizable ArrayBuffer was shipped in node, deno, bun - and the browser - months before Microsoft TypeScript officially supported resizable ArrayBuffer.

If you just want to convert Microsoft TypeScript syntax to JavaScript I would not even bother with type checking.

Here's how to strip Microsoft TypeScript types using node, deno, and bun Strip TypeScript types in Node.js, Deno, and Bun

Node.js

node --no-warnings node-strip-types.js https://raw.githubusercontent/user/repo/refs/heads/main/file.ts transform
node --no-warnings node-strip-types.js ../path/to/file.ts transform
// https://github/nodejs/node/commit/53b1050e6f692ee0330e1076e045b58aada0032d#diff-4e8f3cce79719e4a337f58575b20c998b093eb64164b847ca0eb9ba884d8a801R338
// node --no-warnings node-strip-types.js https://raw.githubusercontent/user/repo/refs/heads/main/file.ts transform
// node --no-warnings node-strip-types.js ../path/to/file.ts transform
import { stripTypeScriptTypes } from "node:module";
import { readFileSync, writeFileSync } from "node:fs";
import { argv, stdout } from "node:process";
let [
  ,
  ,
  sourceTs,
  mode = "transform",
  sourceUrl = sourceTs,
  sourceMap = false,
] = argv;
sourceMap = Boolean(sourceMap);
let url = new URL(sourceTs, import.meta.url);
let code;
if (url.protocol === "file:") {
  code = readFileSync(url, "utf8");
} else if (url.protocol.startsWith("http")) {
  code = await (await fetch(url)).text();
}
const strippedCode = stripTypeScriptTypes(code, { mode, sourceUrl, sourceMap });
stdout.write(strippedCode);

Deno

deno install --entrypoint ../path/to/file.ts
// Transpile TypeScript to JavaScript using Deno built-ins
// Usage: deno -A deno-ts-js-cache.js path/to/file.ts
// Write Deno's generated cache .ts.js file to stdout and current directory
// ts: Path to .ts script
const [ts] = Deno.args;
const url = new URL(import.meta.resolve(ts));
const { pathname } = url;
const filename = pathname.split("/").at(-1);
const decoder = new TextDecoder();
// Path to generated cache .ts.js script
const jsCache = `file${pathname}.js`;
// info: Get Deno cache and TypeScript cache subdirectories
const info = new Deno.Command(Deno.execPath(), {
  args: [
    "info",
    "--json",
  ],
});
// Deno cache directory and generated TypeScript subdirectory
const { denoDir, typescriptCache } = JSON.parse(
  decoder.decode((await info.output()).stdout),
);
// Cache
// https://docs.deno/runtime/fundamentals/modules/#vendoring-remote-modules
const command = await new Deno.Command(Deno.execPath(), {
  args: ["install", "--entrypoint", "-r", ts],
}).output();
// Generated cache .ts.js file
const js = (await Deno.readTextFile(`${typescriptCache}/${jsCache}`))
  .replace(/\/\/#\ssourceMappingURL=.+\n\/\/.+$/img, "");
console.log(js);
await Deno.writeTextFile(`${filename}.js`, js);
Deno.exit(0);
deno -A deno-ts-js-cache.js ../path/to/file.ts 

Bun

bun build ~/path/to/file.ts --no-bundle --outfile=file.ts.js
发布评论

评论列表(0)

  1. 暂无评论