UNPKG

13.7 kBJavaScriptView Raw
1"use strict";
2var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 if (k2 === undefined) k2 = k;
4 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5}) : (function(o, m, k, k2) {
6 if (k2 === undefined) k2 = k;
7 o[k2] = m[k];
8}));
9var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 Object.defineProperty(o, "default", { enumerable: true, value: v });
11}) : function(o, v) {
12 o["default"] = v;
13});
14var __importStar = (this && this.__importStar) || function (mod) {
15 if (mod && mod.__esModule) return mod;
16 var result = {};
17 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 __setModuleDefault(result, mod);
19 return result;
20};
21var __importDefault = (this && this.__importDefault) || function (mod) {
22 return (mod && mod.__esModule) ? mod : { "default": mod };
23};
24Object.defineProperty(exports, "__esModule", { value: true });
25exports.rebuild = exports.Rebuilder = exports.BuildType = void 0;
26const crypto = __importStar(require("crypto"));
27const debug_1 = __importDefault(require("debug"));
28const events_1 = require("events");
29const fs = __importStar(require("fs-extra"));
30const nodeAbi = __importStar(require("node-abi"));
31const os = __importStar(require("os"));
32const path = __importStar(require("path"));
33const read_package_json_1 = require("./read-package-json");
34const cache_1 = require("./cache");
35const search_module_1 = require("./search-module");
36var BuildType;
37(function (BuildType) {
38 BuildType["Debug"] = "Debug";
39 BuildType["Release"] = "Release";
40})(BuildType = exports.BuildType || (exports.BuildType = {}));
41const d = (0, debug_1.default)('electron-rebuild');
42const defaultMode = 'sequential';
43const defaultTypes = ['prod', 'optional'];
44// Update this number if you change the caching logic to ensure no bad cache hits
45const ELECTRON_REBUILD_CACHE_ID = 1;
46class Rebuilder {
47 constructor(options) {
48 var _a;
49 this.platform = process.platform;
50 this.hashDirectory = async (dir, relativeTo = dir) => {
51 d('hashing dir', dir);
52 const dirTree = {};
53 await Promise.all((await fs.readdir(dir)).map(async (child) => {
54 d('found child', child, 'in dir', dir);
55 // Ignore output directories
56 if (dir === relativeTo && (child === 'build' || child === 'bin'))
57 return;
58 // Don't hash nested node_modules
59 if (child === 'node_modules')
60 return;
61 const childPath = path.resolve(dir, child);
62 const relative = path.relative(relativeTo, childPath);
63 if ((await fs.stat(childPath)).isDirectory()) {
64 dirTree[relative] = await this.hashDirectory(childPath, relativeTo);
65 }
66 else {
67 dirTree[relative] = crypto.createHash('SHA256').update(await fs.readFile(childPath)).digest('hex');
68 }
69 }));
70 return dirTree;
71 };
72 this.dHashTree = (tree, hash) => {
73 for (const key of Object.keys(tree).sort()) {
74 hash.update(key);
75 if (typeof tree[key] === 'string') {
76 hash.update(tree[key]);
77 }
78 else {
79 this.dHashTree(tree[key], hash);
80 }
81 }
82 };
83 this.generateCacheKey = async (opts) => {
84 const tree = await this.hashDirectory(opts.modulePath);
85 const hasher = crypto.createHash('SHA256')
86 .update(`${ELECTRON_REBUILD_CACHE_ID}`)
87 .update(path.basename(opts.modulePath))
88 .update(this.ABI)
89 .update(this.arch)
90 .update(this.debug ? 'debug' : 'not debug')
91 .update(this.headerURL)
92 .update(this.electronVersion);
93 this.dHashTree(tree, hasher);
94 const hash = hasher.digest('hex');
95 d('calculated hash of', opts.modulePath, 'to be', hash);
96 return hash;
97 };
98 this.lifecycle = options.lifecycle;
99 this.buildPath = options.buildPath;
100 this.electronVersion = options.electronVersion;
101 this.arch = options.arch || process.arch;
102 this.extraModules = options.extraModules || [];
103 this.onlyModules = options.onlyModules || null;
104 this.force = options.force || false;
105 this.headerURL = options.headerURL || 'https://www.electronjs.org/headers';
106 this.types = options.types || defaultTypes;
107 this.mode = options.mode || defaultMode;
108 this.debug = options.debug || false;
109 this.useCache = options.useCache || false;
110 this.useElectronClang = options.useElectronClang || false;
111 this.cachePath = options.cachePath || path.resolve(os.homedir(), '.electron-rebuild-cache');
112 this.prebuildTagPrefix = options.prebuildTagPrefix || 'v';
113 this.msvsVersion = process.env.GYP_MSVS_VERSION;
114 this.disablePreGypCopy = options.disablePreGypCopy || false;
115 if (this.useCache && this.force) {
116 console.warn('[WARNING]: Electron Rebuild has force enabled and cache enabled, force take precedence and the cache will not be used.');
117 this.useCache = false;
118 }
119 this.projectRootPath = options.projectRootPath;
120 if (typeof this.electronVersion === 'number') {
121 if (`${this.electronVersion}`.split('.').length === 1) {
122 this.electronVersion = `${this.electronVersion}.0.0`;
123 }
124 else {
125 this.electronVersion = `${this.electronVersion}.0`;
126 }
127 }
128 if (typeof this.electronVersion !== 'string') {
129 throw new Error(`Expected a string version for electron version, got a "${typeof this.electronVersion}"`);
130 }
131 this.ABIVersion = (_a = options.forceABI) === null || _a === void 0 ? void 0 : _a.toString();
132 this.prodDeps = this.extraModules.reduce((acc, x) => acc.add(x), new Set());
133 this.rebuilds = [];
134 this.realModulePaths = new Set();
135 this.realNodeModulesPaths = new Set();
136 }
137 get ABI() {
138 if (this.ABIVersion === undefined) {
139 this.ABIVersion = nodeAbi.getAbi(this.electronVersion, 'electron');
140 }
141 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
142 return this.ABIVersion;
143 }
144 get buildType() {
145 return this.debug ? BuildType.Debug : BuildType.Release;
146 }
147 async rebuild() {
148 if (!path.isAbsolute(this.buildPath)) {
149 throw new Error('Expected buildPath to be an absolute path');
150 }
151 d('rebuilding with args:', this.buildPath, this.electronVersion, this.arch, this.extraModules, this.force, this.headerURL, this.types, this.debug);
152 this.lifecycle.emit('start');
153 const rootPackageJson = await (0, read_package_json_1.readPackageJson)(this.buildPath);
154 const markWaiters = [];
155 const depKeys = [];
156 if (this.types.indexOf('prod') !== -1 || this.onlyModules) {
157 depKeys.push(...Object.keys(rootPackageJson.dependencies || {}));
158 }
159 if (this.types.indexOf('optional') !== -1 || this.onlyModules) {
160 depKeys.push(...Object.keys(rootPackageJson.optionalDependencies || {}));
161 }
162 if (this.types.indexOf('dev') !== -1 || this.onlyModules) {
163 depKeys.push(...Object.keys(rootPackageJson.devDependencies || {}));
164 }
165 for (const key of depKeys) {
166 this.prodDeps[key] = true;
167 const modulePaths = await (0, search_module_1.searchForModule)(this.buildPath, key, this.projectRootPath);
168 for (const modulePath of modulePaths) {
169 markWaiters.push(this.markChildrenAsProdDeps(modulePath));
170 }
171 }
172 await Promise.all(markWaiters);
173 d('identified prod deps:', this.prodDeps);
174 const nodeModulesPaths = await (0, search_module_1.searchForNodeModules)(this.buildPath, this.projectRootPath);
175 for (const nodeModulesPath of nodeModulesPaths) {
176 await this.rebuildAllModulesIn(nodeModulesPath);
177 }
178 this.rebuilds.push(() => this.rebuildModuleAt(this.buildPath));
179 if (this.mode !== 'sequential') {
180 await Promise.all(this.rebuilds.map(fn => fn()));
181 }
182 else {
183 for (const rebuildFn of this.rebuilds) {
184 await rebuildFn();
185 }
186 }
187 }
188 async rebuildModuleAt(modulePath) {
189 if (!(await fs.pathExists(path.resolve(modulePath, 'binding.gyp')))) {
190 return;
191 }
192 // eslint-disable-next-line @typescript-eslint/no-var-requires
193 const { ModuleRebuilder } = require('./module-rebuilder');
194 const moduleRebuilder = new ModuleRebuilder(this, modulePath);
195 this.lifecycle.emit('module-found', path.basename(modulePath));
196 if (!this.force && await moduleRebuilder.alreadyBuiltByRebuild()) {
197 d(`skipping: ${path.basename(modulePath)} as it is already built`);
198 this.lifecycle.emit('module-done');
199 this.lifecycle.emit('module-skip');
200 return;
201 }
202 if (await moduleRebuilder.prebuildInstallNativeModuleExists(modulePath)) {
203 d(`skipping: ${path.basename(modulePath)} as it was prebuilt`);
204 return;
205 }
206 let cacheKey;
207 if (this.useCache) {
208 cacheKey = await this.generateCacheKey({
209 modulePath,
210 });
211 const applyDiffFn = await (0, cache_1.lookupModuleState)(this.cachePath, cacheKey);
212 if (typeof applyDiffFn === 'function') {
213 await applyDiffFn(modulePath);
214 this.lifecycle.emit('module-done');
215 return;
216 }
217 }
218 if (await moduleRebuilder.findPrebuildifyModule(cacheKey)) {
219 this.lifecycle.emit('module-done');
220 return;
221 }
222 if (await moduleRebuilder.findPrebuildInstallModule(cacheKey)) {
223 this.lifecycle.emit('module-done');
224 return;
225 }
226 await moduleRebuilder.rebuildNodeGypModule(cacheKey);
227 this.lifecycle.emit('module-done');
228 }
229 async rebuildAllModulesIn(nodeModulesPath, prefix = '') {
230 // Some package managers use symbolic links when installing node modules
231 // we need to be sure we've never tested the a package before by resolving
232 // all symlinks in the path and testing against a set
233 const realNodeModulesPath = await fs.realpath(nodeModulesPath);
234 if (this.realNodeModulesPaths.has(realNodeModulesPath)) {
235 return;
236 }
237 this.realNodeModulesPaths.add(realNodeModulesPath);
238 d('scanning:', realNodeModulesPath);
239 for (const modulePath of await fs.readdir(realNodeModulesPath)) {
240 // Ignore the magical .bin directory
241 if (modulePath === '.bin')
242 continue;
243 // Ensure that we don't mark modules as needing to be rebuilt more than once
244 // by ignoring / resolving symlinks
245 const realPath = await fs.realpath(path.resolve(nodeModulesPath, modulePath));
246 if (this.realModulePaths.has(realPath)) {
247 continue;
248 }
249 this.realModulePaths.add(realPath);
250 if (this.prodDeps[`${prefix}${modulePath}`] && (!this.onlyModules || this.onlyModules.includes(modulePath))) {
251 this.rebuilds.push(() => this.rebuildModuleAt(realPath));
252 }
253 if (modulePath.startsWith('@')) {
254 await this.rebuildAllModulesIn(realPath, `${modulePath}/`);
255 }
256 if (await fs.pathExists(path.resolve(nodeModulesPath, modulePath, 'node_modules'))) {
257 await this.rebuildAllModulesIn(path.resolve(realPath, 'node_modules'));
258 }
259 }
260 }
261 async findModule(moduleName, fromDir, foundFn) {
262 const testPaths = await (0, search_module_1.searchForModule)(fromDir, moduleName, this.projectRootPath);
263 const foundFns = testPaths.map(testPath => foundFn(testPath));
264 return Promise.all(foundFns);
265 }
266 async markChildrenAsProdDeps(modulePath) {
267 if (!await fs.pathExists(modulePath)) {
268 return;
269 }
270 d('exploring', modulePath);
271 let childPackageJson;
272 try {
273 childPackageJson = await (0, read_package_json_1.readPackageJson)(modulePath, true);
274 }
275 catch (err) {
276 return;
277 }
278 const moduleWait = [];
279 const callback = this.markChildrenAsProdDeps.bind(this);
280 for (const key of Object.keys(childPackageJson.dependencies || {}).concat(Object.keys(childPackageJson.optionalDependencies || {}))) {
281 if (this.prodDeps[key]) {
282 continue;
283 }
284 this.prodDeps[key] = true;
285 moduleWait.push(this.findModule(key, modulePath, callback));
286 }
287 await Promise.all(moduleWait);
288 }
289}
290exports.Rebuilder = Rebuilder;
291function rebuild(options) {
292 // eslint-disable-next-line prefer-rest-params
293 d('rebuilding with args:', arguments);
294 const lifecycle = new events_1.EventEmitter();
295 const rebuilderOptions = { ...options, lifecycle };
296 const rebuilder = new Rebuilder(rebuilderOptions);
297 const ret = rebuilder.rebuild();
298 ret.lifecycle = lifecycle;
299 return ret;
300}
301exports.rebuild = rebuild;
302//# sourceMappingURL=rebuild.js.map
\No newline at end of file