Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

A possible vite plugin to make this work with electron.

未关闭
#397 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
5/5
预计耗时
一周以上
新手友好度
30/100
Issue 类型
功能
描述清晰度
需要澄清
活跃度
停滞
技术栈
csharp, electron, node.js, typescript, vite

调研方向

审查提议的 Vite plugin 代码和 Vite 配置示例,包括其针对 node-api-dotnet/init.js 和生成的 assembly 文件的 transforms。首先明确该项目是否希望维护 Electron/Vite 集成,以及应添加哪些文件或测试;由于 issue 未定义验收标准,完成此项工作需要达成一致的验收标准。

由索引模型根据 Issue 内容生成。

描述

After days of research I wrote a plugin to make this cool node-api-dotnet works with electron. Hope it can help people later.

  1. Create a vite plugin
import fs from "fs";
import path from "path";

interface ElectronNodeApiDotNetOptions {
    // The path to `node_modules/node-api-dotnet
    nodeModulePath: string,

    // The path to all generated files and assemblies
    csprojOutputPath: string,
}

export default (options: ElectronNodeApiDotNetOptions) => {
    const name = "electron-node-api-dotnet";

    let assemblyName = "Microsoft.JavaScript.NodeApi";
    let assemblyRoot = path.normalize(options.csprojOutputPath)

    const transformInitJs = (code: string) => {

        // Create require to dynamic require .node files.
        code = `
                    import path from "path";
                    import { createRequire } from 'module';
                    const require = createRequire(import.meta.url);
                    const bundleRoot = ${JSON.stringify(options.nodeModulePath)};
                ` + code

        // Since we use import above, we also need to replace the export style.
        // https://github.com/rollup/plugins/issues/1121
        code = code.replace(
            "module.exports = initialize",
            "export default initialize"
        )

        // Get the dotnet assembly name, preventing upstream change.
        const regex = /const assemblyName\s*=\s*['"`](.*?)['"`];/;
        const match = code.match(regex);
        if (match) {
            assemblyName = match[1];
            console.log(`[${name}] Using assembly name: ${assemblyName}`);
        } else {
            console.error(
                `[${name}] Cannot match assembly name, plugin may not work!`
            );
        }

        // Transform dynamic assemblyName require to a static one
        // Also transform the relative paths to be prefixed with provided bundle root.
        code = code.replace(
            /require\(`\.\/(.+?)\/\$\{assemblyName\}\.node`\)/g,
            "require(path.join(bundleRoot, `./$1/" + assemblyName + ".node`))"
        );

        // Transform the default case.
        code = code.replace(
            "nativeHost = require(__dirname + `/${rid}/${assemblyName}.node`)",
            "nativeHost = require(path.join(bundleRoot, `./${rid}/${assemblyName}.node`))"
        )

        // Transform the path for managed host.
        code = code.replace(
            "const managedHostPath = __dirname + `/${targetFramework}/${assemblyName}.DotNetHost.dll`",
            "const managedHostPath = path.join(bundleRoot, `./${targetFramework}/${assemblyName}.DotNetHost.dll`)"
        )

        return code;
    }

    const transformAssemblyJs = (code: string, filePath: string) => {

        // Instead of use import.meta.url
        code = code.replace(
            "import { fileURLToPath } from 'node:url';", ""
        )
        
        // Use the absolute path, because these files will be rollup 
        // to one single file (for example main.js)
        code = code.replace(
            "const __filename = fileURLToPath(import.meta.url)",
            `const __filename = ${JSON.stringify(filePath)}`
        )

        return code
    }

    return {
        name,
        resolveId(source: string, importer: string | undefined, options: any) {
            return null;
        },
        transform(code: string, id: string) {
            const normId = path.normalize(id)
            if (id.endsWith("node_modules/node-api-dotnet/init.js")) {
                return transformInitJs(code)
            } else if (normId.startsWith(assemblyRoot)) {
                return transformAssemblyJs(code, normId)
            }
            return code;
        },
        load(id: string) {
            if (id.endsWith(`${assemblyName}.node`)) {
                // Treat .node files as empty string, otherwise there'll be error because binary are
                // not valid js files.
                return ``;
            }
            return null;
        },
        generateBundle(options: any, bundle: any) {
            // bundle whatever way you want
        },
    };
};

  1. Include it in vite config, or nuxt config, if you like. Remember to exclude files from commonjs plugin.
import commonjs from '@rollup/plugin-commonjs'
import resolve from '@rollup/plugin-node-resolve'
import electronNodeDotNetApi from './rollup/electron-node-api-dotnet'

...

vite: {
  plugins: [
    resolve(),
    commonjs({
      exclude: [
        "node_modules/node-api-dotnet/**"
      ]
    }),
    electronNodeDotNetApi({
      nodeModulePath: path.join(projectRootDir, 'node_modules/node-api-dotnet'),
      csprojOutputPath: csharpOutputDir
    }),
  ]
}
主要语言
C#
星标
783
派生
80
PR 合并指标
30 天内没有已合并 PR

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

microsoft/node-api-dotnet 的其他 Issue

查看 microsoft/node-api-dotnet 的全部 Issue

相似的 Issue

更多 C# Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。