1 | "use strict";
|
2 | var __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 | }));
|
9 | var __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 | });
|
14 | var __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 | };
|
21 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
22 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
23 | };
|
24 | Object.defineProperty(exports, "__esModule", { value: true });
|
25 | exports.rebuild = exports.Rebuilder = exports.BuildType = void 0;
|
26 | const crypto = __importStar(require("crypto"));
|
27 | const debug_1 = __importDefault(require("debug"));
|
28 | const events_1 = require("events");
|
29 | const fs = __importStar(require("fs-extra"));
|
30 | const nodeAbi = __importStar(require("node-abi"));
|
31 | const os = __importStar(require("os"));
|
32 | const path = __importStar(require("path"));
|
33 | const read_package_json_1 = require("./read-package-json");
|
34 | const cache_1 = require("./cache");
|
35 | const search_module_1 = require("./search-module");
|
36 | var BuildType;
|
37 | (function (BuildType) {
|
38 | BuildType["Debug"] = "Debug";
|
39 | BuildType["Release"] = "Release";
|
40 | })(BuildType = exports.BuildType || (exports.BuildType = {}));
|
41 | const d = (0, debug_1.default)('electron-rebuild');
|
42 | const defaultMode = 'sequential';
|
43 | const defaultTypes = ['prod', 'optional'];
|
44 |
|
45 | const ELECTRON_REBUILD_CACHE_ID = 1;
|
46 | class 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 |
|
56 | if (dir === relativeTo && (child === 'build' || child === 'bin'))
|
57 | return;
|
58 |
|
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 |
|
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 |
|
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 |
|
231 |
|
232 |
|
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 |
|
241 | if (modulePath === '.bin')
|
242 | continue;
|
243 |
|
244 |
|
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 | }
|
290 | exports.Rebuilder = Rebuilder;
|
291 | function rebuild(options) {
|
292 |
|
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 | }
|
301 | exports.rebuild = rebuild;
|
302 |
|
\ | No newline at end of file |