microsoft / microsoft/node-api-dotnet

A possible vite plugin to make this work with electron.

Offen
#397 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Vorherrschende Sprache
C#
Sterne
783
Forks
80
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

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

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Überprüfe den vorgeschlagenen Vite plugin-Code und das Vite-Konfigurationsbeispiel einschließlich seiner Transformationsschritte für node-api-dotnet/init.js und generierte Assembly-Dateien. Kläre zunächst, ob dieses Projekt die Electron/Vite-Integration pflegen möchte und welche Dateien oder Tests hinzugefügt werden sollten; für den Abschluss sind vereinbarte Akzeptanzkriterien erforderlich, da der Issue keine definiert.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
csharp, electron, node.js, typescript, vite
Bereich
build-system, desktop, tooling
Issue-Typ
Feature
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Veraltet
Klarheit
Muss geklärt werden
Anfängerfreundlichkeit
30/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.