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

Feedback in regards to an unexpected issue on using third-party libraries as a plugin developer #92

Open
k0pernikus opened this issue Feb 20, 2025 · 1 comment

Comments

@k0pernikus
Copy link

I ran into an issue by adding adding third-party library via npm install. I used puppeteer, yet it could have happened with different libraries as well.

I expected for the third-party library to just work, yet once I imported it to the default example project:

import {action, KeyDownEvent, SingletonAction, WillAppearEvent} from "@elgato/streamdeck";
import * as puppeteer from "puppeteer";

    override async onKeyDown(ev: KeyDownEvent<CounterSettings>): Promise<void> {
        const browser = await puppeteer.launch(); // won't transpile, why?

        // default of example counter example

        const {settings} = ev.payload;
        settings.incrementBy ??= 1;
        settings.count = (settings.count ?? 0) + settings.incrementBy;

            await ev.action.setSettings(settings);
        await ev.action.setTitle(`${settings.count}`);
    }

Running npm run watch would crash:

rollup v4.34.8
bundles src/plugin.ts → com.kopernikus.odoo-time-tracker.sdPlugin/bin/plugin.js...
(!) "this" has been rewritten to "undefined"
https://rollupjs.org/troubleshooting/#error-this-is-undefined
node_modules/puppeteer-core/lib/esm/puppeteer/node/ScreenRecorder.js
4:  * SPDX-License-Identifier: Apache-2.0
5:  */
6: var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
                            ^
7:     var useValue = arguments.length > 2;
8:     for (var i = 0; i < initializers.length; i++) {
...and 5 other occurrences
node_modules/puppeteer-core/lib/esm/puppeteer/api/ElementHandle.js
4:  * SPDX-License-Identifier: Apache-2.0
5:  */
6: var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
                            ^
7:     var useValue = arguments.length > 2;
8:     for (var i = 0; i < initializers.length; i++) {
...and 9 other occurrences
node_modules/puppeteer-core/lib/esm/puppeteer/api/Frame.js
4:  * SPDX-License-Identifier: Apache-2.0
5:  */
6: var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
                            ^
7:     var useValue = arguments.length > 2;
8:     for (var i = 0; i < initializers.length; i++) {
...and 7 other occurrences

...and 31 other files
[!] (plugin commonjs--resolver) RollupError: node_modules/escodegen/package.json (2:10): Expected ';', '}' or <eof> (Note that you need @rollup/plugin-json to import JSON files)
node_modules/escodegen/package.json (2:10)
1: {
2:     "name": "escodegen",
             ^
3:     "description": "ECMAScript code generator",
4:     "homepage": "http://github.com/estools/escodegen",
    at Object.getRollupError (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\parseAst.js:285:41)
    at ParseError.initialise (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\rollup.js:15580:40)
    at convertNode (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\rollup.js:17474:10)
    at convertProgram (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\rollup.js:16717:12)
    at Module.setSource (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\rollup.js:18454:24)
    at ModuleLoader.addModuleSource (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\rollup.js:21989:13)
  [cause] RollupError: Expected ';', '}' or <eof>
      at Object.getRollupError (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\parseAst.js:285:41)
      at ParseError.initialise (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\rollup.js:15580:40)
      at convertNode (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\rollup.js:17474:10)
      at convertProgram (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\rollup.js:16717:12)
      at Module.setSource (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\rollup.js:18454:24)
      at ModuleLoader.addModuleSource (C:\Users\philipp.kretzschmar\Documents\github\odoo-time-tracker\node_modules\rollup\dist\shared\rollup.js:21989:13)


watch.onEnd $ streamdeck restart com.kopernikus.odoo-time-tracker
√ Restarted com.kopernikus.odoo-time-tracker

The error message was confusing, and at first sent me on a completly wrong goose chase, as I first thought that I had to fix: "this" has been rewritten to "undefined", and then that I would have to load a JSON plugin.

Yet the actual fix was to mark my third-party libraries as external in the rollup.config.mjs :

const config = {
    input: "src/plugin.ts",
    output: {
        file: `${sdPlugin}/bin/plugin.js`,
        sourcemap: isWatching,
        sourcemapPathTransform: (relativeSourcePath, sourcemapPath) => {
            return url.pathToFileURL(path.resolve(path.dirname(sourcemapPath), relativeSourcePath)).href;
        }
    },
    plugins: [
       // ...
    ],
    external: ["puppeteer"]
};

Yet this was not obvious to me nor do I understand why this is necessary.

This stems from the fact that I am not used to using the rollupjs bundler, and I wish this was outlined in the getting started documentation of the README, a note on "How to use third-party libraries" would have been highly appreciated.


Note: I opened a question on Stackoverflow about this.

@ChekTek
Copy link
Member

ChekTek commented Feb 25, 2025

Hi @k0pernikus,

There are many different types of packages hosted on NPM, which can make ensuring they all work inside of a Stream Deck plugin quite challenging.

Packages that are only javascript should work out of the box, as long as they are intended to run in the Node.js environment.

Packages that include natively compiled addons require their output binaries to work (.node files) and can be platform dependent (i.e. only work on Windows, etc.). These files will need to be copied into your .sdPlugin folder before distribution, otherwise your plugin may work during development, but not when sharing it with others. This is where we hope to improve the Stream Deck SDK in the future. With a more advanced rollup config, we are looking to copy these files automatically to ensure a better developer experience.

The library you are referring to puppeteer is actually quite unique in that it installs an entire Chromium instance (or multiple depending on the configuration) in a completely separate .cache folder on your system far outside the root directory of your project. While marking it as external in the rollup config, might make the plugin work for you, if you distribute the plugin without including Chromium inside of the .sdPlugin folder it won't work for your users. For this library specifically, you'll want to look up some options for a puppeteer.config.js to have the Chromium executable land in your .sdPlugin folder on install. Then once the executable file is in your .sdPlugin folder, you will also have to provide the executablePath parameter in your config when calling puppeteer.launch. I would slightly use caution with this approach as Chromium can make your final .streamDeckPlugin quite large. I believe there are some extra parameters that would allow you to only install the chrome-headless-shell rather than a complete Chrome.

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

No branches or pull requests

2 participants