Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot bundle .node files #14289

Open
forgetso opened this issue Sep 4, 2023 · 15 comments
Open

Cannot bundle .node files #14289

forgetso opened this issue Sep 4, 2023 · 15 comments

Comments

@forgetso
Copy link

forgetso commented Sep 4, 2023

Describe the bug

I am trying to create a server side bundle that includes packages containing .node files. In this specific instance, the problematic package is nodejs-polars.

in/nodejs-polars.linux-arm-gnueabihf.node does not exist
✓ 13 modules transformed.
✓ built in 1.39s
[commonjs] Unexpected character '' (Note that you need plugins to import files that are not JavaScript)
file: /home/projects/vitejs-vite-xp1ggj/node_modules/nodejs-polars/bin/series/list.js:1:0
1: ELF>�rd@X�@8
   ^
2: @@@0�bd�bd...
3: 

I have tried various plugins to resolve this but none of them seem to work.

My config is as follows:

import native from 'vite-plugin-native';
export default function () {
  return {
    ssr: {
      noExternal: ['nodejs-polars'],
    },
    optimizeDeps: {
      include: ['linked-dep', 'node_modules'],
      exclude: ['nodejs-polars'],
    },
    esbuild: {
      platform: 'node',
      target: 'node16',
    },
    build: {
      lib: {
        entry: './main.js',
        name: 'demo',
      },
      ssr: true,
      target: 'node16',
    },
    plugins: [native({ target: 'esm' })],
  };
}

Reproduction

https://stackblitz.com/edit/vitejs-vite-xp1ggj?file=vite.config.ts,package.json&terminal=dev

Steps to reproduce

Run vite build --config vite.config.ts

System Info

System:
    OS: Linux 5.4 Ubuntu 20.04.6 LTS (Focal Fossa)
    CPU: (8) x64 AMD FX(tm)-8350 Eight-Core Processor
    Memory: 9.66 GB / 31.31 GB
    Container: Yes
    Shell: 5.8 - /usr/bin/zsh
  Binaries:
    Node: 18.14.2 - ~/.nvm/versions/node/v18.14.2/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v18.14.2/bin/yarn
    npm: 8.9.0 - ~/.nvm/versions/node/v18.14.2/bin/npm
  Browsers:
    Brave Browser: 115.1.56.14
    Chrome: 116.0.5845.96


### Used Package Manager

npm

### Logs

_No response_

### Validations

- [X] Follow our [Code of Conduct](https://github.com/vitejs/vite/blob/main/CODE_OF_CONDUCT.md)
- [X] Read the [Contributing Guidelines](https://github.com/vitejs/vite/blob/main/CONTRIBUTING.md).
- [X] Read the [docs](https://vitejs.dev/guide).
- [X] Check that there isn't [already an issue](https://github.com/vitejs/vite/issues) that reports the same bug to avoid creating a duplicate.
- [X] Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to [vuejs/core](https://github.com/vuejs/core) instead.
- [X] Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/vitejs/vite/discussions) or join our [Discord Chat Server](https://chat.vitejs.dev/).
- [X] The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
@stackblitz
Copy link

stackblitz bot commented Sep 4, 2023

Fix this issue in StackBlitz Codeflow Start a new pull request in StackBlitz Codeflow.

@HappyGick
Copy link

This issue is preventing me from using many modules. No workaround or package has worked.

@forgetso
Copy link
Author

forgetso commented Sep 16, 2023

I managed to bundle nodejs-polars using this config but it's incredibly hacky.

@HappyGick
Copy link

Well, that's one hell of a hack. But I'll have to study and use it I guess. Thank you so much. Resolving .node files should be a feature in Vite.

@totto2727
Copy link

totto2727 commented Mar 4, 2024

The same problem occurs with fsevent.

I have confirmed that it works by adding the following settings to astro.config.mjs.

  vite: {
    optimizeDeps: {
      exclude: ["fsevents"],
    },
  },

https://stackoverflow.com/questions/75640753/vite-esbuild-error-no-loader-is-configured-for-node-files-node-modules-fs

Error log
13:51:15 [vite] Forced re-optimization of dependencies

 astro  v4.4.9 ready in 341 ms

┃ Local    http://localhost:3001/
┃ Network  use --host to expose

13:51:15 watching for file changes...
✘ [ERROR] No loader is configured for ".node" files: node_modules/.pnpm/fsevents@2.3.2/node_modules/fsevents/fsevents.node

    node_modules/.pnpm/fsevents@2.3.2/node_modules/fsevents/fsevents.js:13:23:
      13 │ const Native = require("./fsevents.node");
         ╵                        ~~~~~~~~~~~~~~~~~

13:51:25 [200] / 40ms
13:51:25 [ERROR] [UnhandledRejection] Astro detected an unhandled rejection. Here's the stack trace:
Error: Build failed with 1 error:
node_modules/.pnpm/fsevents@2.3.2/node_modules/fsevents/fsevents.js:13:23: ERROR: No loader is configured for ".node" files: node_modules/.pnpm/fsevents@2.3.2/node_modules/fsevents/fsevents.node
    at failureErrorWithLog ({...}/node_modules/.pnpm/esbuild@0.19.12/node_modules/esbuild/lib/main.js:1651:15)
    at {...}//node_modules/.pnpm/esbuild@0.19.12/node_modules/esbuild/lib/main.js:1059:25
    at {...}//node_modules/.pnpm/esbuild@0.19.12/node_modules/esbuild/lib/main.js:1527:9
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
  Hint:
    Make sure your promises all have an `await` or a `.catch()` handler.
  Error reference:
    https://docs.astro.build/en/reference/errors/unhandled-rejection/
  Stack trace:
    at {...}//node_modules/.pnpm/fsevents@2.3.2/node_modules/fsevents/fsevents.js: ERROR: No loader is configured for ".node" files: node_modules/.pnpm/fsevents@2.3.2/node_modules/fsevents/fsevents.node:13:23
    [...] See full stack trace in the browser, or rerun with --verbose.

@ghost
Copy link

ghost commented Mar 11, 2024

I managed to bundle nodejs-polars using this config but it's incredibly hacky.
image
do you have any idea how to handle this problem of canvas library, i am using nuxt3 and vite but can't handle it, thank you

@stereobooster
Copy link

vite.optimizeDeps.exclude solution doesn't work with pnpm workspaces. Example https://github.com/stereobooster/braindb

pnpm build
...
demo-astro-cli:build: 20:25:22 [vite] Re-optimizing dependencies because vite config has changed
demo-astro-cli:build: 20:25:23 [build] output: "static"
demo-astro-cli:build: 20:25:23 [build] directory: /mddb/packages/demo-astro-cli/dist/
demo-astro-cli:build: 20:25:23 [build] Collecting build info...
demo-astro-cli:build: 20:25:23 [build] ✓ Completed in 1.09s.
demo-astro-cli:build: 20:25:23 [build] Building static entrypoints...
demo-astro-cli:build: 20:25:25 [ERROR] [vite] x Build failed in 1.85s
demo-astro-cli:build: [commonjs--resolver] ../../node_modules/.pnpm/fsevents@2.3.3/node_modules/fsevents/fsevents.node (1:0): Unexpected character '�' (Note that you need plugins to import files that are not JavaScript)
demo-astro-cli:build: file: /mddb/node_modules/.pnpm/chokidar@3.6.0/node_modules/chokidar/index.js:1:0
demo-astro-cli:build: 1: ����@<�
                                ��*...
demo-astro-cli:build:    ^
4*mo-astro-cli:build: 2:
  h���/System/Library/Frameworks/CoreFoundation.framework/Versions...
demo-astro-cli:build: 3: ���H��1�H���L���L���H�����L�E�L��H��1�����uH�E�H�[A^A_]��}f.�UH��AWAVSPI��H��I��H�H...
demo-astro-cli:build:   Stack trace:
demo-astro-cli:build:     at getRollupError (file:///mddb/node_modules/.pnpm/rollup@4.14.0/node_modules/rollup/dist/es/shared/parseAst.js:379:41)
demo-astro-cli:build:     at convertNode (file:///mddb/node_modules/.pnpm/rollup@4.14.0/node_modules/rollup/dist/es/shared/node-entry.js:12914:10)
demo-astro-cli:build:     at Module.setSource (file:///mddb/node_modules/.pnpm/rollup@4.14.0/node_modules/rollup/dist/es/shared/node-entry.js:14073:24)
demo-astro-cli:build:   Caused by:
demo-astro-cli:build:   Unexpected character '�'
demo-astro-cli:build:     at getRollupError (file:///mddb/node_modules/.pnpm/rollup@4.14.0/node_modules/rollup/dist/es/shared/parseAst.js:379:41)
demo-astro-cli:build:     at convertNode (file:///mddb/node_modules/.pnpm/rollup@4.14.0/node_modules/rollup/dist/es/shared/node-entry.js:12914:10)
demo-astro-cli:build:     at Module.setSource (file:///mddb/node_modules/.pnpm/rollup@4.14.0/node_modules/rollup/dist/es/shared/node-entry.js:14073:24)
demo-astro-cli:build:  ELIFECYCLE  Command failed with exit code 1.
demo-astro-cli:build: ERROR: command finished with error: command (/mddb/packages/demo-astro-cli) pnpm run build exited (1)
demo-astro-cli#build: command (/mddb/packages/demo-astro-cli) pnpm run build exited (1)

@goastler
Copy link

goastler commented Apr 10, 2024

We've managed to get .node files working, see our blog post here

@sagoez
Copy link

sagoez commented Jun 13, 2024

Are there any plans on this one? Even though @goastler shared a nice blog, it doesn't solve anything for the general public.

@goastler
Copy link

The handling of .node files (and other binary-like files) which occur in nodejs packages needs to be addressed by either vite or a plugin. If vite will not support this (as it could be considered out of scope) then it will have to be a plugin. Our business interests have moved away from this at the moment, so making a plugin would be very low priority for @forgetso and myself at Prosopo.

Further, there's more complications with files other than .node files (iirc .glyph) which serve similar purpose, so we'd need to do a bunch of research into how to handle those files correctly.

tldr; probably better for someone who knows more about these files to implement a plugin / build support in vite

@stereobooster
Copy link

related #5688

@raveclassic
Copy link

For those searching for a solution, I (chatgpt to be honest) came up with a quick plugin:

function nativeFilesPlugin(): PluginOption {
  const files = new Map<string, { readonly fileName: string; readonly fileContent: Buffer }>();

  return {
    name: 'node-binaries-plugin',
    async load(id) {
      if (!id.endsWith('.node')) {
        return null;
      }

      const fileContent = await fs.readFile(id);
      const hash = createHash('sha256').update(fileContent).digest('hex').slice(0, 8);
      const fileName = `${path.basename(id, '.node')}.${hash}.node`;
      files.set(id, { fileName, fileContent });

      return `export default require('./${fileName}');`;
    },

    generateBundle(_, bundle) {
      for (const [id, { fileName, fileContent }] of files.entries()) {
        this.emitFile({ type: 'asset', fileName, source: fileContent });
        delete bundle[id];
      }
    },
  };
}

I'm building into cjs, so I also have these settings:

build: {
  rollupOptions: {
    output: {
      format: 'cjs',
    },
  },
  commonjsOptions: {
    requireReturnsDefault: 'auto',
  }
}

@raveclassic
Copy link

It looks like an even simpler version also works:

{
      name: 'require-node-binaries-plugin',
      transform: (code, id) => {
        if (id.endsWith('.node')) {
          return code.replace(/export default "(.+)"/, 'export default require(".$1")');
        }

        return;
      },
    }

paired with build.ssrEmitAssets: true and assetsInclude: ['**/*.node']

@raveclassic
Copy link

Small update here.

Native assets didn't work in my project because I'm using uWebSockets.js, which is an ESM-wrapper around CJS-module that does dynamic requires. No matter how hard I tried, I wasn't able to make them work with assets. Dynamic requires are typically handled by @rollup/plugin-commonjs which is a whole different story, as it does some code transformations that are hard to address, if vite treats .node binaries as assets.

So I had to revert to the original naive plugin, and, unfortunately, add a special hard-coded case for uWebSockets.js because of dynamic requires.

function nativeFilesPlugin(): PluginOption {
  interface NativeFile {
    readonly fileName: string;
    readonly fileContent: Buffer;
  }
  const nativeFiles = new Map<string, NativeFile>();
  const uws = require.resolve('uWebSockets.js');
  const readNativeFile = async (filePath: string): Promise<NativeFile> => {
    const fileContent = await readFile(filePath);
    const hash = createHash('sha256').update(fileContent).digest('hex').slice(0, 8);
    const fileName = `${path.basename(filePath, '.node')}.${hash}.node`;

    return { fileName, fileContent };
  };

  return {
    name: 'native-files-plugin',

    async load(id) {
      if (id === uws) {
        // Special handling for the ESM-wrapper around CJS-module with dynamic requires (uWebSockets.js).
        const nativeFile = await readNativeFile(
          // Yes, build the file name at build time, not runtime 🤷‍♂️(we can't do dynamic `import {} from ''`)
          path.resolve(path.dirname(uws), './uws_' + process.platform + '_' + process.arch + '_' + process.versions.modules + '.node'),
        );
        nativeFiles.set(id, nativeFile);

        return `export default require('./${nativeFile.fileName}')`;
      } else if (id.endsWith('.node')) {
        const nativeFile = await readNativeFile(id);
        nativeFiles.set(id, nativeFile);

        return `export default require('./${nativeFile.fileName}');`;
      }

      return;
    },

    generateBundle(_, bundle) {
      for (const [id, { fileName, fileContent }] of Array.from(nativeFiles.entries())) {
        this.emitFile({ type: 'asset', fileName, source: fileContent });
        delete bundle[id];
      }
    },
  };
}

I believe the issue should be handled on rollup's side, not on vite's, as I believe it's purely rollup's job to bundle everything into a module graph that vite would understand. There's also an on-going work on Rolldown (a Rust replacement for rollup), so maybe it should even go there, or maybe it's already supported there, I can't test it right now.

@ultimateshadsform
Copy link

ultimateshadsform commented Nov 17, 2024

I installed vite-plugin-node-polyfills and now it works!
My config:

import { defineConfig } from 'vite';
import electron from 'vite-plugin-electron/simple';
import vue from '@vitejs/plugin-vue';
import { nodePolyfills } from 'vite-plugin-node-polyfills';

export default defineConfig({
  plugins: [
    vue(),
    electron({
      main: {
        entry: 'electron/main.ts',
        vite: {
          build: {
            rollupOptions: {
              external: ['@ultimateshadsform/universal-media'],
            },
          },
        },
      },
      preload: {
        input: 'electron/preload.ts',
      },
      renderer: {},
    }),
    nodePolyfills(),
  ],
  resolve: {
    alias: {
      path: 'path-browserify',
    },
  },
  optimizeDeps: {
    exclude: ['@ultimateshadsform/universal-media'],
  },
  ssr: {
    noExternal: ['@ultimateshadsform/universal-media'],
  },
  build: {
    rollupOptions: {
      external: ['@ultimateshadsform/universal-media', 'electron'],
    },
  },
});

Note

@ultimateshadsform/universal-media is a locally linked package using bun link. Command.

Putting @ultimateshadsform/universal-media under external in vite options under entry: 'electron/main.ts', makes vite shutup about:

electron/main.ts (4:9): "MediaController" is not exported by "../index.js", imported by "electron/main.ts".        
file: C:/Users/shadow/Documents/Coding/universal-media/testing/electron/main.ts:4:9

2: import { fileURLToPath } from 'node:url';
3: import path from 'node:path';
4: import { MediaController } from '@ultimateshadsform/universal-media';
            ^
5:
6: // Create singleton instance

With the external option it shuts up about that.

My electron main:

import { app, BrowserWindow, ipcMain } from 'electron';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import { MediaController } from '@ultimateshadsform/universal-media';

// Create singleton instance
const mediaController = new MediaController();

// Store subscription
let currentSubscription: { stop: () => void } | null = null;

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// The built directory structure
//
// ├─┬─┬ dist
// │ │ └── index.html
// │ │
// │ ├─┬ dist-electron
// │ │ ├── main.js
// │ │ └── preload.mjs
// │
process.env.APP_ROOT = path.join(__dirname, '..');

// 🚧 Use ['ENV_NAME'] avoid vite:define plugin - Vite@2.x
export const VITE_DEV_SERVER_URL = process.env['VITE_DEV_SERVER_URL'];
export const MAIN_DIST = path.join(process.env.APP_ROOT, 'dist-electron');
export const RENDERER_DIST = path.join(process.env.APP_ROOT, 'dist');

process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL
  ? path.join(process.env.APP_ROOT, 'public')
  : RENDERER_DIST;

let win: BrowserWindow | null;

function createWindow() {
  win = new BrowserWindow({
    icon: path.join(process.env.VITE_PUBLIC, 'electron-vite.svg'),
    webPreferences: {
      preload: path.join(__dirname, 'preload.mjs'),
    },
  });

  // Test active push message to Renderer-process.
  win.webContents.on('did-finish-load', () => {
    win?.webContents.send('main-process-message', new Date().toLocaleString());
  });

  if (VITE_DEV_SERVER_URL) {
    win.loadURL(VITE_DEV_SERVER_URL);
  } else {
    // win.loadFile('dist/index.html')
    win.loadFile(path.join(RENDERER_DIST, 'index.html'));
  }
}

// Add IPC handlers
function setupIPC() {
  ipcMain.handle('media:play', () => mediaController.play());
  ipcMain.handle('media:pause', () => mediaController.pause());
  ipcMain.handle('media:stop', () => mediaController.stop());
  ipcMain.handle('media:next', () => mediaController.next());
  ipcMain.handle('media:previous', () => mediaController.previous());
  ipcMain.handle('media:getMediaInfo', () => mediaController.getMediaInfo());
  ipcMain.handle('media:getSystemVolume', () =>
    mediaController.getSystemVolume()
  );
  ipcMain.handle('media:getSystemMute', () => mediaController.getSystemMute());
  ipcMain.handle('media:setSystemVolume', (_event, volume: number) =>
    mediaController.setSystemVolume(volume)
  );
  ipcMain.handle('media:setSystemMute', (_event, mute: boolean) =>
    mediaController.setSystemMute(mute)
  );

  // Handle subscriptions
  ipcMain.handle('media:subscribe', (event) => {
    try {
      // Clean up previous subscription
      if (currentSubscription) {
        currentSubscription.stop();
      }

      // Create new subscription and forward events to renderer
      currentSubscription = mediaController.subscribeToEvents(
        (eventData: any) => {
          event.sender.send('media:event', eventData);
        }
      );
    } catch (error) {
      console.error('Error in subscribe:', error);
    }
  });

  ipcMain.handle('media:unsubscribe', () => {
    if (currentSubscription) {
      currentSubscription.stop();
      currentSubscription = null;
    }
  });
}

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
    win = null;
  }
});

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

app.whenReady().then(() => {
  createWindow();
  setupIPC();
});

That's about it. My project is build using napi-rs so this should work. The trick here is using nodePolyfills package to fool vite into thinking it's in a browser environment or like make the path available somehow. Because without it in electron it says something like: Module: path not found or something like that so this solves that.

Here is the auto generated index.js file napi-rs generates:

/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */

/* auto-generated by NAPI-RS */

const { existsSync, readFileSync } = require('fs')
const { join } = require('path')

const { platform, arch } = process

let nativeBinding = null
let localFileExisted = false
let loadError = null

function isMusl() {
  // For Node 10
  if (!process.report || typeof process.report.getReport !== 'function') {
    try {
      const lddPath = require('child_process').execSync('which ldd').toString().trim()
      return readFileSync(lddPath, 'utf8').includes('musl')
    } catch (e) {
      return true
    }
  } else {
    const { glibcVersionRuntime } = process.report.getReport().header
    return !glibcVersionRuntime
  }
}

switch (platform) {
  case 'android':
    switch (arch) {
      case 'arm64':
        localFileExisted = existsSync(join(__dirname, 'universal-media.android-arm64.node'))
        try {
          if (localFileExisted) {
            nativeBinding = require('./universal-media.android-arm64.node')
          } else {
            nativeBinding = require('@ultimateshadsform/universal-media-android-arm64')
          }
        } catch (e) {
          loadError = e
        }
        break
      case 'arm':
        localFileExisted = existsSync(join(__dirname, 'universal-media.android-arm-eabi.node'))
        try {
          if (localFileExisted) {
            nativeBinding = require('./universal-media.android-arm-eabi.node')
          } else {
            nativeBinding = require('@ultimateshadsform/universal-media-android-arm-eabi')
          }
        } catch (e) {
          loadError = e
        }
        break
      default:
        throw new Error(`Unsupported architecture on Android ${arch}`)
    }
    break
  case 'win32':
    switch (arch) {
      case 'x64':
        localFileExisted = existsSync(
          join(__dirname, 'universal-media.win32-x64-msvc.node')
        )
        try {
          if (localFileExisted) {
            nativeBinding = require('./universal-media.win32-x64-msvc.node')
          } else {
            nativeBinding = require('@ultimateshadsform/universal-media-win32-x64-msvc')
          }
        } catch (e) {
          loadError = e
        }
        break
      case 'ia32':
        localFileExisted = existsSync(
          join(__dirname, 'universal-media.win32-ia32-msvc.node')
        )
        try {
          if (localFileExisted) {
            nativeBinding = require('./universal-media.win32-ia32-msvc.node')
          } else {
            nativeBinding = require('@ultimateshadsform/universal-media-win32-ia32-msvc')
          }
        } catch (e) {
          loadError = e
        }
        break
      case 'arm64':
        localFileExisted = existsSync(
          join(__dirname, 'universal-media.win32-arm64-msvc.node')
        )
        try {
          if (localFileExisted) {
            nativeBinding = require('./universal-media.win32-arm64-msvc.node')
          } else {
            nativeBinding = require('@ultimateshadsform/universal-media-win32-arm64-msvc')
          }
        } catch (e) {
          loadError = e
        }
        break
      default:
        throw new Error(`Unsupported architecture on Windows: ${arch}`)
    }
    break
  case 'darwin':
    localFileExisted = existsSync(join(__dirname, 'universal-media.darwin-universal.node'))
    try {
      if (localFileExisted) {
        nativeBinding = require('./universal-media.darwin-universal.node')
      } else {
        nativeBinding = require('@ultimateshadsform/universal-media-darwin-universal')
      }
      break
    } catch {}
    switch (arch) {
      case 'x64':
        localFileExisted = existsSync(join(__dirname, 'universal-media.darwin-x64.node'))
        try {
          if (localFileExisted) {
            nativeBinding = require('./universal-media.darwin-x64.node')
          } else {
            nativeBinding = require('@ultimateshadsform/universal-media-darwin-x64')
          }
        } catch (e) {
          loadError = e
        }
        break
      case 'arm64':
        localFileExisted = existsSync(
          join(__dirname, 'universal-media.darwin-arm64.node')
        )
        try {
          if (localFileExisted) {
            nativeBinding = require('./universal-media.darwin-arm64.node')
          } else {
            nativeBinding = require('@ultimateshadsform/universal-media-darwin-arm64')
          }
        } catch (e) {
          loadError = e
        }
        break
      default:
        throw new Error(`Unsupported architecture on macOS: ${arch}`)
    }
    break
  case 'freebsd':
    if (arch !== 'x64') {
      throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
    }
    localFileExisted = existsSync(join(__dirname, 'universal-media.freebsd-x64.node'))
    try {
      if (localFileExisted) {
        nativeBinding = require('./universal-media.freebsd-x64.node')
      } else {
        nativeBinding = require('@ultimateshadsform/universal-media-freebsd-x64')
      }
    } catch (e) {
      loadError = e
    }
    break
  case 'linux':
    switch (arch) {
      case 'x64':
        if (isMusl()) {
          localFileExisted = existsSync(
            join(__dirname, 'universal-media.linux-x64-musl.node')
          )
          try {
            if (localFileExisted) {
              nativeBinding = require('./universal-media.linux-x64-musl.node')
            } else {
              nativeBinding = require('@ultimateshadsform/universal-media-linux-x64-musl')
            }
          } catch (e) {
            loadError = e
          }
        } else {
          localFileExisted = existsSync(
            join(__dirname, 'universal-media.linux-x64-gnu.node')
          )
          try {
            if (localFileExisted) {
              nativeBinding = require('./universal-media.linux-x64-gnu.node')
            } else {
              nativeBinding = require('@ultimateshadsform/universal-media-linux-x64-gnu')
            }
          } catch (e) {
            loadError = e
          }
        }
        break
      case 'arm64':
        if (isMusl()) {
          localFileExisted = existsSync(
            join(__dirname, 'universal-media.linux-arm64-musl.node')
          )
          try {
            if (localFileExisted) {
              nativeBinding = require('./universal-media.linux-arm64-musl.node')
            } else {
              nativeBinding = require('@ultimateshadsform/universal-media-linux-arm64-musl')
            }
          } catch (e) {
            loadError = e
          }
        } else {
          localFileExisted = existsSync(
            join(__dirname, 'universal-media.linux-arm64-gnu.node')
          )
          try {
            if (localFileExisted) {
              nativeBinding = require('./universal-media.linux-arm64-gnu.node')
            } else {
              nativeBinding = require('@ultimateshadsform/universal-media-linux-arm64-gnu')
            }
          } catch (e) {
            loadError = e
          }
        }
        break
      case 'arm':
        if (isMusl()) {
          localFileExisted = existsSync(
            join(__dirname, 'universal-media.linux-arm-musleabihf.node')
          )
          try {
            if (localFileExisted) {
              nativeBinding = require('./universal-media.linux-arm-musleabihf.node')
            } else {
              nativeBinding = require('@ultimateshadsform/universal-media-linux-arm-musleabihf')
            }
          } catch (e) {
            loadError = e
          }
        } else {
          localFileExisted = existsSync(
            join(__dirname, 'universal-media.linux-arm-gnueabihf.node')
          )
          try {
            if (localFileExisted) {
              nativeBinding = require('./universal-media.linux-arm-gnueabihf.node')
            } else {
              nativeBinding = require('@ultimateshadsform/universal-media-linux-arm-gnueabihf')
            }
          } catch (e) {
            loadError = e
          }
        }
        break
      case 'riscv64':
        if (isMusl()) {
          localFileExisted = existsSync(
            join(__dirname, 'universal-media.linux-riscv64-musl.node')
          )
          try {
            if (localFileExisted) {
              nativeBinding = require('./universal-media.linux-riscv64-musl.node')
            } else {
              nativeBinding = require('@ultimateshadsform/universal-media-linux-riscv64-musl')
            }
          } catch (e) {
            loadError = e
          }
        } else {
          localFileExisted = existsSync(
            join(__dirname, 'universal-media.linux-riscv64-gnu.node')
          )
          try {
            if (localFileExisted) {
              nativeBinding = require('./universal-media.linux-riscv64-gnu.node')
            } else {
              nativeBinding = require('@ultimateshadsform/universal-media-linux-riscv64-gnu')
            }
          } catch (e) {
            loadError = e
          }
        }
        break
      case 's390x':
        localFileExisted = existsSync(
          join(__dirname, 'universal-media.linux-s390x-gnu.node')
        )
        try {
          if (localFileExisted) {
            nativeBinding = require('./universal-media.linux-s390x-gnu.node')
          } else {
            nativeBinding = require('@ultimateshadsform/universal-media-linux-s390x-gnu')
          }
        } catch (e) {
          loadError = e
        }
        break
      default:
        throw new Error(`Unsupported architecture on Linux: ${arch}`)
    }
    break
  default:
    throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
}

if (!nativeBinding) {
  if (loadError) {
    throw loadError
  }
  throw new Error(`Failed to load native binding`)
}

const { EventType, MediaController } = nativeBinding

module.exports.EventType = EventType
module.exports.MediaController = MediaController

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants