"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const path_1 = require("path"); const buffer_1 = require("buffer"); const fs_1 = require("fs"); const child_process_1 = require("child_process"); const logger_1 = require("./logger"); const util_1 = require("./util"); const options_1 = require("./options"); const MultiStream = require("multistream"); const bundle_1 = require("./fs/bundle"); const isBsd = Boolean(~process.platform.indexOf('bsd')); const make = util_1.isWindows ? 'vcbuild.bat' : isBsd ? 'gmake' : 'make'; const configure = util_1.isWindows ? 'configure' : './configure'; class NexeError extends Error { constructor(m) { super(m); Object.setPrototypeOf(this, NexeError.prototype); } } exports.NexeError = NexeError; class NexeCompiler { constructor(options) { this.options = options; /** * Epoch of when compilation started */ this.start = Date.now(); this.log = new logger_1.Logger(this.options.loglevel); /** * Copy of process.env */ this.env = Object.assign({}, process.env); /** * In memory files that are being manipulated by the compiler */ this.files = []; /** * Standalone pieces of code run before the application entrypoint */ this.shims = []; /** * The last shim (defaults to "require('module').runMain()") */ this.startup = ''; /** * Output filename (-o myapp.exe) */ this.output = this.options.output; /** * Flag to indicate whether or notstdin was used for input */ this.stdinUsed = false; const { python } = (this.options = options); //SOMEDAY iterate over multiple targets with `--outDir` this.targets = options.targets; this.target = this.targets[0]; if (!/https?\:\/\//.test(options.remote)) { throw new NexeError(`Invalid remote URI scheme (must be http, https, or file): ${options.remote}`); } this.remoteAsset = options.remote + this.target.toString(); this.src = path_1.join(this.options.temp, this.target.version); this.configureScript = configure + (util_1.semverGt(this.target.version, '10.10.0') ? '.py' : ''); this.nodeSrcBinPath = util_1.isWindows ? path_1.join(this.src, 'Release', 'node.exe') : path_1.join(this.src, 'out', 'Release', 'node'); this.log.step('nexe ' + options_1.version, 'info'); this.bundle = new bundle_1.Bundle(options); if (util_1.isWindows) { const originalPath = process.env.PATH; delete process.env.PATH; this.env = Object.assign({}, process.env); this.env.PATH = python ? (this.env.PATH = util_1.dequote(path_1.normalize(python)) + path_1.delimiter + originalPath) : originalPath; process.env.PATH = originalPath; } else { this.env = Object.assign({}, process.env); python && (this.env.PYTHON = python); } } addResource(absoluteFileName, content) { return this.bundle.addResource(absoluteFileName, content); } readFileAsync(file) { return __awaiter(this, void 0, void 0, function* () { this.assertBuild(); let cachedFile = this.files.find((x) => path_1.normalize(x.filename) === path_1.normalize(file)); if (!cachedFile) { const absPath = path_1.join(this.src, file); cachedFile = { absPath, filename: file, contents: yield util_1.readFileAsync(absPath, 'utf-8').catch((x) => { if (x.code === 'ENOENT') return ''; throw x; }), }; this.files.push(cachedFile); } return cachedFile; }); } writeFileAsync(file, contents) { this.assertBuild(); return util_1.writeFileAsync(path_1.join(this.src, file), contents); } replaceInFileAsync(file, replace, value) { return __awaiter(this, void 0, void 0, function* () { const entry = yield this.readFileAsync(file); entry.contents = entry.contents.toString().replace(replace, value); }); } setFileContentsAsync(file, contents) { return __awaiter(this, void 0, void 0, function* () { const entry = yield this.readFileAsync(file); entry.contents = contents; }); } quit(error) { const time = Date.now() - this.start; this.log.write(`Finished in ${time / 1000}s`, error ? 'red' : 'green'); return this.log.flush(); } assertBuild() { if (!this.options.build) { throw new NexeError('This feature is only available with `--build`'); } } getNodeExecutableLocation(target) { if (this.options.asset) { return path_1.resolve(this.options.cwd, this.options.asset); } if (target) { return path_1.join(this.options.temp, target.toString()); } return this.nodeSrcBinPath; } _runBuildCommandAsync(command, args) { if (this.log.verbose) { this.compileStep.pause(); } return new Promise((resolve, reject) => { child_process_1.spawn(command, args, { cwd: this.src, env: this.env, stdio: this.log.verbose ? 'inherit' : 'ignore', }) .once('error', (e) => { if (this.log.verbose) { this.compileStep.resume(); } reject(e); }) .once('close', (code) => { if (this.log.verbose) { this.compileStep.resume(); } if (code != 0) { const error = `${command} ${args.join(' ')} exited with code: ${code}`; reject(new NexeError(error)); } resolve(); }); }); } _configureAsync() { if (util_1.isWindows && util_1.semverGt(this.target.version, '10.15.99')) { return Promise.resolve(); } return this._runBuildCommandAsync(this.env.PYTHON || 'python', [ this.configureScript, ...this.options.configure, ]); } build() { return __awaiter(this, void 0, void 0, function* () { this.compileStep.log(`Configuring node build${this.options.configure.length ? ': ' + this.options.configure : '...'}`); yield this._configureAsync(); const buildOptions = this.options.make; this.compileStep.log(`Compiling Node${buildOptions.length ? ' with arguments: ' + buildOptions : '...'}`); yield this._runBuildCommandAsync(make, buildOptions); return fs_1.createReadStream(this.getNodeExecutableLocation()); }); } _shouldCompileBinaryAsync(binary, location) { return __awaiter(this, void 0, void 0, function* () { //SOMEDAY combine make/configure/vcBuild/and modified times of included files const { snapshot, build } = this.options; if (!binary) { return true; } if (build && snapshot != null && (yield util_1.pathExistsAsync(snapshot))) { const snapshotLastModified = (yield util_1.statAsync(snapshot)).mtimeMs; const binaryLastModified = (yield util_1.statAsync(location)).mtimeMs; return snapshotLastModified > binaryLastModified; } return false; }); } compileAsync(target) { return __awaiter(this, void 0, void 0, function* () { const step = (this.compileStep = this.log.step('Compiling result')); const build = this.options.build; const location = this.getNodeExecutableLocation(build ? undefined : target); let binary = (yield util_1.pathExistsAsync(location)) ? fs_1.createReadStream(location) : null; if (yield this._shouldCompileBinaryAsync(binary, location)) { binary = yield this.build(); step.log('Node binary compiled'); } return this._assembleDeliverable(binary); }); } code() { return [this.shims.join(''), this.startup].join(';'); } _assembleDeliverable(binary) { return __awaiter(this, void 0, void 0, function* () { if (!this.options.mangle) { return binary; } const resources = this.bundle.renderIndex(); this.shims.unshift(util_1.wrap(`process.__nexe = ${JSON.stringify({ resources })};\n`)); const code = this.code(), codeSize = buffer_1.Buffer.byteLength(code), lengths = buffer_1.Buffer.from(Array(16)); lengths.writeDoubleLE(codeSize, 0); lengths.writeDoubleLE(this.bundle.size, 8); return new MultiStream([ binary, bundle_1.toStream(code), this.bundle.toStream(), bundle_1.toStream(buffer_1.Buffer.concat([buffer_1.Buffer.from(''), lengths])), ]); }); } } __decorate([ util_1.bound ], NexeCompiler.prototype, "addResource", null); __decorate([ util_1.bound ], NexeCompiler.prototype, "readFileAsync", null); __decorate([ util_1.bound ], NexeCompiler.prototype, "writeFileAsync", null); __decorate([ util_1.bound ], NexeCompiler.prototype, "replaceInFileAsync", null); __decorate([ util_1.bound ], NexeCompiler.prototype, "setFileContentsAsync", null); exports.NexeCompiler = NexeCompiler;