Skip to content

解决VMware共享文件夹下安装node_modules报错的问题 #9

@guilixie

Description

@guilixie

背景

因为公司提供的办公电脑是Windows系统,本人之前一直使用的Ubuntu系统开发的,所以就比较难受了。于是我有了用VMware虚拟机安装一个Ubuntu桌面版的想法,经过一番折腾以后,就可以愉快的写代码了~~~

发现问题

在我使用yarn安装项目依赖的时候,总是会出现如下的错误,真是让人苦恼。不如换成npm install 试试,结果如出一辙,报错信息如下所示:

guili@guili-virtual-machine:/mnt/hgfs/UbuntuShare/workspace/my-app$ yarn
yarn install v1.22.10
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
error An unexpected error occurred: "ENOTSUP: operation not supported on socket, symlink '../../../../@babel/parser/bin/babel-parser.js' -> '/mnt/hgfs/UbuntuShare/workspace/my-app/node_modules/@vue/compiler-core/node_modules/.bin/parser'".
info If you think this is a bug, please open a bug report with the information provided in "/mnt/hgfs/UbuntuShare/workspace/my-app/yarn-error.log".
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.

于是,我拍案而起,这也太坑了吧。

分析问题

查阅了资料,了解到vmhgfs(应用于VMware共享文件夹的Linuxvfs)不支持symlink(或者可能是不支持LinuxWindows下的符号链接格式互相转换)。原理是安装基于nodejs的项目时,如果安装的模块的package.json 中定义了bin 字段,那么yarn或者npm就会自动创建一个符号链接到node_modules/.bin文件夹下。

因为VMware的“共享文件夹”是不支持自动符号链接转换的,所以这个功能在VMware的“共享文件夹”下是没法正常工作的,就会导致上述的报错信息的出现。

解决方案

一、bin-links

npm提供了bin-links配置项,可以告诉 npm 为包可执行文件创建符号链接(或 Windows 上的 .cmd 垫片)。 设置为 false 使其不这样做。这可以用来解决一些文件系统不支持符号链接的事实,即使在表面上是 Unix 系统上。

npm

你可以直接在~/.npmrc写入,

bin-links=false

或者直接命令设置全局的配置如下:

npm config set bin-links=false

另外,还可以直接使用

npm install --no-bin-links

yarn

你可以直接在~/.yarnrc写入,

"--*.no-bin-links" true
or
bin-links false

或者直接命令设置全局的配置如下:

yarn config set bin-links false

另外,还可以直接使用

yarn --no-bin-links

二、nodejs脚本

使用上述的方法,只是安装模块的时候不再报错,但是当执行scripts字段下的run命令时,还是会有报错,说找不到某某模块。比如有一个webpack打包的项目,然后已经使用--no-bin-links安装好所以依赖。对于webpack,找到其package.json,发现配置有bin字段:

"bin": {
    "webpack": "./bin/webpack.js"
}

可知此模块有一个cli入口,入口名和包一致,为webpack,对应执行模块目录内的./bin/webpack.js

既然无法用符号链接来指向这个文件,我们可以曲线救国,使用脚本完成这个事。首先需要在node_modules目录下创建.bin文件夹,然后创建文件webpack.js,写入内容如下:

#! /bin/bash
node './node_modules/webpack/bin/webpack.js' $*

最后的$*是为了将所有命令行参数完整的传给对应脚本。

然后就需要使用chmod命令给已经写好的脚本执行权限。

chmod +x ./node_modules/.bin/webpack

然后运行项目就可以正常使用webpack命令了。

完整的代码如下:

const fs = require('fs')
const path = require('path')

// get package list from package.json
const packageList = require('./package.json').devDependencies

// get all package name from packageList
const packageNameList = Object.keys(packageList)

const CLIList = []

const convertPath = (packageName, binaryPath) => path.resolve(`./node_modules/${packageName}/`, binaryPath)

for (let packageName of packageNameList) {
	// load package config
	const packageConf = require(`./node_modules/${packageName}/package.json`)
	if ('bin' in packageConf) {
		if (typeof packageConf.bin !== 'string') {
			for (let cmd of Object.keys(packageConf.bin)) {
				CLIList.push([cmd, convertPath(packageName, packageConf.bin[cmd])])
			}
		} else {
			CLIList.push([packageName, convertPath(packageName, packageConf.bin), true])
		}
	}
}

if (!fs.existsSync('./node_modules/.bin')) {
	fs.mkdir('./node_modules/.bin',()=>{})
} else {
	throw new Error('directory .bin exists already')
}

// make script
for (const item of CLIList) {
	const [filename, binPath, _] = item
	const script = `
        #!/bin/bash

        node "${binPath}" $*
        `
	fs.writeFileSync(`./node_modules/.bin/${filename}`, script)
	fs.chmodSync(`./node_modules/.bin/${filename}`, 777)
}

console.log(CLIList);

终于圆满解决

然后将脚本放到项目的根目录,在安装完node_modules后,运行之前,执行下这个脚本:node ./command_patch.js

于是乎,困扰了我半天,让我加班的问题终于被我找到原因和圆满解决了~~

当然最直截了当的方法就是不用虚拟机的共享文件夹,直接在虚拟机的其它文件夹下工作。当然也就不会遇到这个问题,也不会折腾这么久了。

参考资料

  1. npm Docs - config #bin-links
  2. yarn config
  3. .yarnrc
  4. [nodejs] 解决由于VMWare共享文件夹功能bug导致的yarn(或npm)无法正常使用的问题
  5. 有关yarn/npm关闭符号链接创建后引起的一个bug的讨论

Metadata

Metadata

Assignees

No one assigned

    Labels

    VMwareVMwarebugSomething isn't workingcliclinodenode

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions