【问题标题】:Using typescript in Electron's preload scripts在 Electron 的预加载脚本中使用 typescript
【发布时间】:2018-03-02 19:25:50
【问题描述】:

我使用 electron-forge 的 react-typescript 模板构建了一个 Electron 应用程序,这意味着它使用了 electron-prebuilt-compile,根据 the only documentation I can find 应该可以正常工作。

index.html 包含可以正常工作的打字稿,正如所宣传的那样。但我也在使用带有预加载脚本的webview tag,以显示外部网站并对其进行修改。这是我正在使用的代码:

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

这个预加载脚本相当复杂,我很想为此使用 typescript。但它显然被解析为 javascript,任何类型注释都会导致语法错误。有没有技巧可以使用打字稿进行这项工作?如果我必须手动调用转译器,如何将其与 electron-forge 的构建过程集成?

tl;dr:尽管打字稿在其他地方“正常工作”,但预加载脚本被解析为 javascript,我也想在这里使用打字稿

【问题讨论】:

    标签: typescript electron


    【解决方案1】:

    您可以在预加载文件(或任何文件)中使用 TypeScript。只需导入 ts-node 包并在导入任何 TypeScript 代码之前对其进行配置。

    例如,制作一个 index.js 文件,其中包含:

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

    然后在您的 require-hooks.js 文件中配置 ts-node(ts-node 是一个即时编译 TypeScript 的 require 挂钩,并为后续运行提供缓存):

    // 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,
    })
    

    注意,您可以在其中放置各种 require 挂钩,例如,您可以执行 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') 等操作,您可以在其中定义用于处理某些类型文件的钩子(在某些方面类似于 Webpack,但挂钩到 Node 的内置 require 函数)。例如,@babel/register 是通过 Babel 运行 JS 文件的钩子,asset-require-hook 是允许您导入 JPG 文件等资产的 require 钩子,yaml-hook 允许您导入 require .yaml 文件。编辑:更多:css-modules-require-hook 用于导入 CSS 模块,module-alias 用于创建别名,如使用 WebPack。

    pirates lib 是一种流行的工具,用于制作您自己的 require 钩子,尽管我也发现在没有库的情况下手动制作钩子很容易。例如,您可以覆盖 Module.prototype.require 来实现一些简单的钩子:

    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)
      }
    }
    

    然后在其他文件中,

    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
    

    您甚至可以将对document.createElement('img').src = img 的调用移动到覆盖的require 方法中以自动执行此操作并返回img 元素而不是返回file:// URL。 :)

    最后,在上述index.js 文件导入的./entry.ts 文件中,您可以在其中包含任何TypeScript。

    【讨论】:

      【解决方案2】:

      预加载脚本是不同的类型,你不能直接将 typescript 指向那里。唯一可能的方法是制作 bootstrapselectron 在其中编译的 javascript 预加载脚本(因为您使用的是 electron-prebuilt-compile),并在其中需要 typescript 文件。它有点冗长,需要额外的开销,老实说,我不强烈推荐它。

      【讨论】:

      • 这有点令人失望,但我也同样担心。我有点不确定如何引导电子编译。 github.com/electron-userland/electron-forge/wiki/… 描述了一种以Failed to execute 'registerElement' on 'Document': [...] Elements cannot be registered from extensions 结尾的方法。您对如何执行此操作有任何见解,还是我应该为该错误打开一个新问题?
      • 修复了这个问题,contextIsolation 正在创建错误。没有它,前面提到的 wiki 条目有效
      【解决方案3】:

      我的解决方案是添加第二个 webpack 配置,输出单个 inject.js 文件。

      src/inject.ts 转换为 => dist/inject.js,从那里可以这样要求:

      new BrowserWindow({
          width: 800,
          height: 600,
          webPreferences: {
            nodeIntegration: false,
            enableRemoteModule: false,
            contextIsolation: true,
            sandbox: true,
            preload: path.join(__dirname, 'mercari_inject.js')
          }
      });
      

      给定一个基本配置webpack.main.config.js,如下所示:

      module.exports = {
          ...
          output: {
              path: path.resolve(__dirname, 'dist'),
              filename: '[name].js'
          },
          ...
      }
      

      主配置webpack.main.config.js 应如下所示:

      const merge = require('webpack-merge');
      const baseConfig = require('./webpack.base.config');
      
      const mainConfig = merge.smart(baseConfig, {...})
      const injectScriptConfig = merge.smart(baseConfig, {
        target: 'electron-preload',
        entry: {
          inject: './src/inject.ts',
        },
        module: {
          rules: [
             ... // babel/typescript loaders
          ]
        } 
      })
      
      module.exports = [
         mainConfig,
         injectScriptConfig,
      ]
      

      请注意,在导出之前从单个配置对象更改为数组。

      module.exports = {...}
      module.exports = [{...}, {...}]
      

      【讨论】:

        猜你喜欢
        • 2019-10-21
        • 2021-03-14
        • 2021-06-18
        • 2021-12-24
        • 1970-01-01
        • 2022-01-07
        • 2017-10-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多