Skip to content

[Feature]: 献计:完美内置npm方案,用户无需安装nodejs #1431

Description

@qiaolin-li

前置阅读 | Pre-reading

PicGo的版本 | PicGo Version

v3.0.1

系统信息 | System Information

Windows

功能请求 | Feature request

以下内容引用我的一篇记录博客

参见: https://github.com/qiaolin-li/dubbo-desktop-manager/blob/master/docs/npm%E6%8F%92%E4%BB%B6%E5%AE%89%E8%A3%85%E5%99%A8.md

大佬,你是我的 Electron 的导师,我看完了你的 Electron 系列博客,之后又研究了 PicGoPicGo Core 的源码。它们对我的插件系统帮助很大,感谢大佬,现在轮到我来反哺大佬了,看你的插件系统还依赖外置 nodejs,这体验太差了,我推荐我搞得野路子给你。

我使用的是npm8的包
查看 npm@8.19.4 的源码,npm 8 直接执行 require('npm') 会进入根 index.js,并抛出:

The programmatic API was removed in npm v8.0.0

但继续查看源码可以发现,npm CLI 仍然是运行在 Node.js 上的 JavaScript 程序,核心 Npm 类位于 lib/npm.js

Electron 主进程就是 Node.js。因此我的最终方案是:把完整的 npm 8 打进应用,绕过根 index.js,直接加载 lib/npm.js,然后调用 load()exec()

项目固定 npm 版本并通过 Electron Builder 复制完整目录:

{
    "dependencies": {
        "npm": "8.19.4"
    },
    "build": {
        "extraResources": [
            "./node_modules/npm"
        ]
    }
}

NpmUtils 完整实现

NpmUtils 负责加载内置 npm、指定安装目录、选择镜像并转发安装日志:

// 开发和生产环境使用不同的 npm 路径:
const APPLICATION_NPM_PATH_PREFIX = IS_DEVELOPMENT
	? path.join(process.cwd(), 'node_modules/npm/')
    : path.join(process.resourcesPath, 'node_modules/npm/')

class NpmUtils {
    
    async execCommand (command, args, where){
        if (!command) {
            throw new Error('command is a must');
        }

        process.argv[0] = ''
        process.argv[1] = 'dist_electron'
        process.argv[2] = command
        for (let i = 0; i < args.length; i++) {
            process.argv.push(args[i]);
        }

        const Npm = require(`${APPLICATION_NPM_PATH_PREFIX}/lib/npm.js`)

        class MyNpm extends Npm {

            constructor (localPrefix) {
                super()
                this.localPrefix = localPrefix;
            }

			/** 安装依赖时会修改title,可能会影响应用的title,所以重写忽略 */
            set title (t) { }
        }

        const npm = new MyNpm(where)
        const stdoutWrite = process.stdout.write;
        const stderrWrite = process.stderr.write;

        try {
            process.stdout.write = (...msg) => {
                npm.output(...msg)
            }

            process.stderr.write = (...msg) => {
                npm.outputError(...msg)
            }
        
            await npm.load()

			// 可以设置更快的仓库
            npm.config.set('registry', 'https://registry.npmmirror.com/')
            const cmd = npm.argv.shift()
            await npm.exec(cmd, npm.argv)
        } finally {
            process.stdout.write = stdoutWrite;
            process.stderr.write = stderrWrite;
        }

        return npm.message;
    }    
}

export default new NpmUtils();

使用案例

安装插件:

await npmUtils.execCommand('install', ['ddm-plugin-dubbo-support@1.0.0', '--loglevel', 'info'], pluginDirectory)

卸载插件:

await npmUtils.execCommand('uninstall', ['ddm-plugin-dubbo-support', '--loglevel', 'info'], pluginDirectory)

真实代码参见:
[PluginInstaller.js](https://github.com/qiaolin-li/dubbo-desktop-manager/blob/master/src/main/plugin/PluginInstaller.js)

[NpmUtils.js](

(打个广告)我也是在用 Electron 做一个插件平台,本来是只做 Dubbo客户端的,慢慢的发现不满足了,就好玩做成了现在这个样子,准确是说没有大佬你的博客,就没有这个 app,我看你的博客和源码搞出来的,好几年了,这个插件系统我也搞了两年(是因为不急,反正不图他赚钱),搞着玩
仓库地址:https://github.com/qiaolin-li/dubbo-desktop-manager

Image Image Image

Activity

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

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions