-
Notifications
You must be signed in to change notification settings - Fork 3
/
qdd-loader.js
120 lines (104 loc) · 3.1 KB
/
qdd-loader.js
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
'use strict';
const Module = require('module');
const fs = require('fs');
const { execSync } = require('child_process');
const cacheDir = (process.env.QDD_CACHE || `${process.env.HOME}/.cache/qdd`);
let entrypoint;
// 1. Set up the lock tree
let lockTree;
try {
lockTree = require(process.cwd() + '/package-lock.json');
parseLockTree();
} catch (lockTreeErr) {
// get it later in _load
}
function getLockTree (mainFilename) {
if (lockTree) {
return lockTree;
}
entrypoint = mainFilename;
const content = fs.readFileSync(mainFilename, 'utf8');
const afterJson = content.split(/^\/\*\*package-lock(?:\s|$)/m)[1];
if (afterJson) {
const [json, rest] = afterJson.split(/\*\*\/$/m);
if (rest) {
try {
lockTree = JSON.parse(json.replace(/^\s*\*/mg, ''));
parseLockTree();
return lockTree;
} catch (e) {
throw new Error('badly formed in-line package-lock');
}
}
}
throw new Error('no package-lock found');
}
function parseLockTree () {
lockTree.requires = lockTree.dependencies;
function getDep (node, depName) {
const deps = node.dependencies;
return deps && deps[depName] ? deps[depName] : getDep(node.parent, depName);
}
(function augmentTree (node, parent) {
for (const depName of Object.keys(node.dependencies || {})) {
const dep = node.dependencies[depName];
dep.parent = node;
augmentTree(dep);
}
node.requiresNodes = {};
for (const dep of Object.keys(node.requires || {})) {
node.requiresNodes[dep] = getDep(node, dep);
}
delete node.parent; // no longer needed
})(lockTree);
}
// 2. Shim the module loader
let installed = false;
function cache (node) {
const dir = cacheDir + '/' + node.integrity;
if (!installed) {
try {
fs.accessSync(dir, fs.constants.F_OK);
} catch (e) {
execSync(`qdd --onlycache`, {
env: Object.assign({}, process.env, { QDD_LOCKJS: entrypoint })
});
}
installed = true;
}
return dir;
}
let currentParent;
const origFindPath = Module._findPath;
Module._findPath = function (request, paths, isMain) {
if (isMain) {
return origFindPath(request, paths, isMain);
}
delete currentParent.nextTreeNode;
const maybeOrig = origFindPath(request, paths, isMain);
if (maybeOrig) {
return maybeOrig;
}
const node = currentParent.treeNode;
for (const name of (Object.keys(node.requiresNodes || {}))) {
if (request === name || request.startsWith(name + '/')) {
const newNode = node.requiresNodes[name];
currentParent.nextTreeNode = newNode;
return origFindPath(request.replace(name, cache(newNode)), paths, isMain);
}
}
};
const origResolveFilename = Module._resolveFilename;
Module._resolveFilename = function (request, parent, isMain) {
if (!isMain) {
currentParent = parent;
}
return origResolveFilename(request, parent, isMain);
};
const origLoad = Module.prototype.load;
Module.prototype.load = function (filename) {
this.treeNode = this.parent
? (this.parent.nextTreeNode || this.parent.treeNode)
: getLockTree(filename);
return origLoad.call(this, filename);
};