Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using typescript in Electron's preload scripts

I build an Electron application using electron-forge's react-typescript template, meaning it uses electron-prebuilt-compile, which according to the only documentation I can find is supposed to just work.

The index.html contains typescript that just works, as advertised. But I am also using a webview tag with a preload script, in order to display external websites and modify them. This is the code I'm using:

<webview id="webview" preload="./preload.ts" webpreferences="contextIsolation, webSecurity=no" src="https://example.com"></webview>

This preload script is rather involved, and I would love to use typescript for it. But it is apparently parsed as javascript, with any type annotations leading to syntax errors. Is there a trick to make this work with typescript? If I have to call the transpiler manually, how do I integrate this with electron-forge's build process?

tl;dr: preload script is parsed as javascript despite typescript "just working" everywhere else, I would like to use typescript here too

like image 305
Jannis Froese Avatar asked Aug 04 '26 00:08

Jannis Froese


1 Answers

You can use TypeScript in a preload file (or any file). Just import the ts-node package and configure it before you import any of your TypeScript code.

F.e., make an index.js file containing:

require('./require-hooks')
module.exports = require('./entry') // this is your TypeScript entry point.

then in your require-hooks.js file configure ts-node (ts-node is a require hook that compiles TypeScript on the fly, with caching for subsequent runs):

// ability to require/import TypeScript files
require('ts-node').register({
  typeCheck: false, // faster, no type checking when require'ing files. Use another process to do actual type checking.
  transpileOnly: true, // no type checking, just strip types and output JS.
  files: true,

  // manually supply our own compilerOptions, otherwise if we run this file
  // from another project's location then ts-node will use
  // the compilerOptions from that other location, which may not work.
  compilerOptions: require('./tsconfig.json').compilerOptions,
})

Note, you can put all sorts of require hooks in there, for example so you can do things like require('path/to/file.ts'), require('path/to/file.tsx'), require('path/to/file.jsx'), require('path/to/file.png'), require('path/to/file.mp3'), etc, where you can define hooks for handling certain types of files (similar to Webpack in some way, but hooking into Node's builtin require function). For example, @babel/register is a hook for running JS files through Babel, asset-require-hook is a require hook that allows you to import assets like JPG files, yaml-hook allows you to require .yaml files. EDIT: Even more: css-modules-require-hook for importing CSS modules, and module-alias for creating aliases like with WebPack.

The pirates lib is a popular tool for making your own require hooks, though I also find it easy to make hooks manually without a library. You can, for example, override Module.prototype.require to implement some simple hooks:

const path = require('path')
const url = require('url')
const Module = require('module')
const oldRequire = Module.prototype.require

function toFileURL(filePath) {
  return url.format({
    pathname: filePath,
    protocol: 'file:',
    slashes: true,
  })
}

Module.prototype.require = function(moduleIdentifier) {
  // this simple hook returns a `file://...` URL when you try to `require()`
  // a file with extension obj, svg, png, or jpg.
  if (['.obj', '.png', '.svg', '.jpg'].some(ext => moduleIdentifier.endsWith(ext))) {
    const result = String(toFileURL(path.resolve(path.dirname(this.filename), moduleIdentifier)))

    result.default = result
    return result
  } else {
    return oldRequire.call(this, moduleIdentifier)
  }
}

then in some other file,

const fs = require('fs')
const img = require('path/to/foo.jpg')

console.log(img) // file:///absolute/path/to/foo.jpg
document.createElement('img').src = img

You could even move the call to document.createElement('img').src = img into your overridden require method to do it automatically and return the img element instead of returning a file:// URL. :)

Finally, in your ./entry.ts file that the above index.js file imports, you can have any TypeScript in there.

like image 83
trusktr Avatar answered Aug 05 '26 15:08

trusktr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!