-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathnpm_fs_builder.js
More file actions
126 lines (114 loc) · 4.69 KB
/
npm_fs_builder.js
File metadata and controls
126 lines (114 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
'use strict';
const path = require('node:path');
const FsMeta = require('./fs_meta');
const { PREFIX_LENGTH } = require('../constants');
const Util = require('../util');
const PackageLock = require('../package_lock').PackageLock;
class NpmFsMetaBuilder {
/**
* @param {NpmBlobManager} blobManager -
* @param {object} options -
* @param {object} options.uid -
* @param {object} options.gid -
*/
constructor(blobManager, options) {
this.blobManager = blobManager;
this.fsMeta = new FsMeta();
this.uid = options.uid;
this.gid = options.gid;
this.cwd = options.cwd;
this.entryListener = options.entryListener;
this.productionMode = options.productionMode;
this.singleMount = options.singleMount;
// 项目直接依赖的 bin
this.pkgBinSet = new Set();
}
async generateFsMeta(packageLockJson, currentPkgPath, entryListener) {
const packageLock = new PackageLock({ cwd: this.cwd, packageLockJson });
await packageLock.load();
const packages = packageLock.packages;
for (const [ pkgPath, pkgItem ] of Object.entries(packages)) {
entryListener?.(pkgPath);
if (!pkgPath || !Util.validDep(pkgItem, this.productionMode)) continue;
// npm alias or normal npm package
const name = Util.getAliasPackageNameFromPackagePath(pkgPath, packages);
const version = pkgItem.version;
// node_modules 目录需要处理所有的提升过的依赖
// singleMount 模式下,需要一起挂载
if (this.singleMount) {
this.createPackageMeta(name, version, pkgPath, currentPkgPath, packages['']);
} else if ((packageLock.isRootPkg(currentPkgPath) && pkgPath.startsWith('node_modules/')) || (!packageLock.isRootPkg(currentPkgPath) && pkgPath.startsWith(currentPkgPath))) {
this.createPackageMeta(name, version, pkgPath, currentPkgPath, packages['']);
}
}
const blobId = this.fsMeta.blobIds[0] || 'bucket_0.stgz';
const rootDir = Util.rootDir(this.uid, this.gid);
// 生成根目录
this.fsMeta.addEntry(blobId, rootDir);
// 生成 workspace 子目录
if (packageLock.isRootPkg(currentPkgPath)) {
const workspaceDeps = packageLock.getWorkspaceDeps(packageLock.data);
workspaceDeps.forEach(dep => {
const { source, target } = dep;
const name = Util.getAliasPackageNameFromPackagePath(source, packages);
const linkPath = path.relative(path.dirname(source), target);
this.fsMeta.addEntry(blobId, Util.generateSymbolLink(name, linkPath, this.uid, this.gid, true));
});
}
return this.fsMeta.dump();
}
createPackageMeta(name, version, packagePath, currentPkgPath, pkgJSON) {
if (!this.singleMount) {
packagePath = path.relative(currentPkgPath, packagePath).substring(PREFIX_LENGTH);
}
const pkgId = Util.generatePackageId(name, version);
const pkg = this.blobManager.getPackage(name, version);
if (!pkg) return;
const reverseBinMap = Util.resolveBinMap(pkg);
const pkgDeps = {
...(pkgJSON?.dependencies || {}),
...(pkgJSON?.devDependencies || {}),
...(pkgJSON?.optionalDependencies || {}),
};
const tocIndexes = this.blobManager.getTocIndexes(name, version);
for (const [ blobId, tocIndex ] of tocIndexes) {
for (const entry of tocIndex.entries) {
// blobId 中 name 是 foo@1.0.0/index.js
// 需要替换成 bar/node_modules/foo/index.js
const relatedPath = entry.name.substr(pkgId.length + 1);
const entryNewName = path.join(packagePath, relatedPath);
this.fsMeta.addEntry(blobId, {
...entry,
mode: Util.getFileEntryMode(pkgId, pkg, entry),
uid: this.uid,
gid: this.gid,
uname: 'admin',
gname: 'admin',
name: entryNewName,
});
// 补全 bin 文件
if (reverseBinMap[path.normalize(relatedPath)]) {
reverseBinMap[relatedPath].forEach(binName => {
// 同名 bin,项目依赖优先创建
// 例如安装 @ali/egg-bin@2.6.0 会同时创建 egg-bin@4.x 的 bin
if (pkgDeps[packagePath]) {
this.pkgBinSet.add(binName);
}
// 同名的 bin,只允许项目依赖创建,项目依赖没有的可以创建
if ((this.pkgBinSet.has(binName) && pkgDeps[packagePath]) || !this.pkgBinSet.has(binName)) {
const binEntry = Util.generateBin({
binName,
binPath: entryNewName,
pkgPath: packagePath,
uid: this.uid,
gid: this.gid,
});
this.fsMeta.addEntry(blobId, binEntry);
}
});
}
}
}
}
}
module.exports = NpmFsMetaBuilder;