Update to eslint
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"plugins": ["@typescript-eslint", "prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "warn",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "warn",
|
||||
"@typescript-eslint/no-empty-function": "warn",
|
||||
"no-useless-escape": "warn",
|
||||
"no-empty": "warn",
|
||||
"no-constant-condition": "warn",
|
||||
"prefer-const": "warn"
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"ignorePatterns": ["lib/", "node_modules/", "*.js", "webpack.config.ts"]
|
||||
}
|
||||
Generated
+1092
-236
File diff suppressed because it is too large
Load Diff
+6
-4
@@ -15,7 +15,7 @@
|
||||
"test:integration:run": "run-script-os",
|
||||
"test:integration:run:win32": "integration-tests.exe",
|
||||
"test:integration:run:default": "./integration-tests",
|
||||
"lint": "tslint \"{src,plugins,tasks}/**/*.ts\" --fix",
|
||||
"lint": "eslint \"{src,plugins,tasks}/**/*.ts\" --fix",
|
||||
"prepare": "npm run lint && npm run build && npm test",
|
||||
"prebuild": "rimraf lib",
|
||||
"build": "tsc --declaration && tsc -p tasks && webpack",
|
||||
@@ -82,9 +82,11 @@
|
||||
"mocha": "^10.2.0",
|
||||
"prettier": "^2.8.7",
|
||||
"ts-node": "^10.9.1",
|
||||
"tslint": "^6.1.1",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"tslint-plugin-prettier": "^2.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
||||
"@typescript-eslint/parser": "^5.59.0",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"typescript": "^5.0.3",
|
||||
"webpack-cli": "^5.0.1"
|
||||
}
|
||||
|
||||
+159
-140
@@ -1,8 +1,8 @@
|
||||
import { delimiter, resolve, normalize, join } from 'path'
|
||||
import { Buffer } from 'buffer'
|
||||
import { createReadStream, ReadStream } from 'fs'
|
||||
import { spawn } from 'child_process'
|
||||
import { Logger, LogStep } from './logger'
|
||||
import { delimiter, resolve, normalize, join } from "path";
|
||||
import { Buffer } from "buffer";
|
||||
import { createReadStream, ReadStream } from "fs";
|
||||
import { spawn } from "child_process";
|
||||
import { Logger, LogStep } from "./logger";
|
||||
import {
|
||||
readFileAsync,
|
||||
writeFileAsync,
|
||||
@@ -13,33 +13,33 @@ import {
|
||||
bound,
|
||||
semverGt,
|
||||
wrap,
|
||||
} from './util'
|
||||
import { NexeOptions, version } from './options'
|
||||
import { NexeTarget } from './target'
|
||||
import { PassThrough, Readable, Stream, Transform } from 'stream'
|
||||
import MultiStream = require('multistream')
|
||||
import { Bundle, toStream } from './fs/bundle'
|
||||
import { File } from 'resolve-dependencies'
|
||||
import { FactoryStream, LazyStream } from 'multistream'
|
||||
} from "./util";
|
||||
import { NexeOptions, version } from "./options";
|
||||
import { NexeTarget } from "./target";
|
||||
import { PassThrough, Readable, Stream, Transform } from "stream";
|
||||
import MultiStream = require("multistream");
|
||||
import { Bundle, toStream } from "./fs/bundle";
|
||||
import { File } from "resolve-dependencies";
|
||||
import { FactoryStream, LazyStream } from "multistream";
|
||||
|
||||
const isBsd = Boolean(~process.platform.indexOf('bsd'))
|
||||
const make = isWindows ? 'vcbuild.bat' : isBsd ? 'gmake' : 'make'
|
||||
const configure = isWindows ? 'configure' : './configure'
|
||||
const isBsd = Boolean(~process.platform.indexOf("bsd"));
|
||||
const make = isWindows ? "vcbuild.bat" : isBsd ? "gmake" : "make";
|
||||
const configure = isWindows ? "configure" : "./configure";
|
||||
|
||||
type StringReplacer = (match: string) => string
|
||||
type StringReplacer = (match: string) => string;
|
||||
|
||||
export interface NexeFile {
|
||||
filename: string
|
||||
absPath: string
|
||||
contents: string | Buffer
|
||||
filename: string;
|
||||
absPath: string;
|
||||
contents: string | Buffer;
|
||||
}
|
||||
|
||||
export { NexeOptions }
|
||||
export { NexeOptions };
|
||||
|
||||
export class NexeError extends Error {
|
||||
constructor(m: string) {
|
||||
super(m)
|
||||
Object.setPrototypeOf(this, NexeError.prototype)
|
||||
super(m);
|
||||
Object.setPrototypeOf(this, NexeError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,283 +47,302 @@ export class NexeCompiler {
|
||||
/**
|
||||
* Epoch of when compilation started
|
||||
*/
|
||||
private start = Date.now()
|
||||
private compileStep: LogStep | undefined
|
||||
public log = new Logger(this.options.loglevel)
|
||||
private start = Date.now();
|
||||
private compileStep: LogStep | undefined;
|
||||
public log = new Logger(this.options.loglevel);
|
||||
/**
|
||||
* Copy of process.env
|
||||
*/
|
||||
public env = { ...process.env }
|
||||
public env = { ...process.env };
|
||||
/**
|
||||
* Virtual FileSystem
|
||||
*/
|
||||
public bundle: Bundle
|
||||
public bundle: Bundle;
|
||||
/**
|
||||
* Root directory for the source of the current build
|
||||
*/
|
||||
public src: string
|
||||
public src: string;
|
||||
/**
|
||||
* In memory files that are being manipulated by the compiler
|
||||
*/
|
||||
public files: NexeFile[] = []
|
||||
public files: NexeFile[] = [];
|
||||
/**
|
||||
* Standalone pieces of code run before the application entrypoint
|
||||
*/
|
||||
public shims: string[] = []
|
||||
public shims: string[] = [];
|
||||
/**
|
||||
* The last shim (defaults to "require('module').runMain()")
|
||||
*/
|
||||
public startup: string = ''
|
||||
public startup = "";
|
||||
/**
|
||||
* The main entrypoint filename for your application - eg. node mainFile.js
|
||||
*/
|
||||
public entrypoint: string | undefined
|
||||
public entrypoint: string | undefined;
|
||||
/**
|
||||
* Not used
|
||||
*/
|
||||
public targets: NexeTarget[]
|
||||
public targets: NexeTarget[];
|
||||
/**
|
||||
* Current target of the compiler
|
||||
*/
|
||||
public target: NexeTarget
|
||||
public target: NexeTarget;
|
||||
/**
|
||||
* Output filename (-o myapp.exe)
|
||||
*/
|
||||
public output = this.options.output
|
||||
public output = this.options.output;
|
||||
/**
|
||||
* Flag to indicate whether or notstdin was used for input
|
||||
*/
|
||||
public stdinUsed = false
|
||||
public stdinUsed = false;
|
||||
/**
|
||||
* Path to the configure script
|
||||
*/
|
||||
public configureScript: string
|
||||
public configureScript: string;
|
||||
/**
|
||||
* The file path of node binary
|
||||
*/
|
||||
public nodeSrcBinPath: string
|
||||
public nodeSrcBinPath: string;
|
||||
/**
|
||||
* Remote asset path if available
|
||||
*/
|
||||
public remoteAsset: string
|
||||
public remoteAsset: string;
|
||||
|
||||
constructor(public options: NexeOptions) {
|
||||
const { python } = (this.options = options)
|
||||
const { python } = (this.options = options);
|
||||
//SOMEDAY iterate over multiple targets with `--outDir`
|
||||
this.targets = options.targets as NexeTarget[]
|
||||
this.target = this.targets[0]
|
||||
this.targets = options.targets as NexeTarget[];
|
||||
this.target = this.targets[0];
|
||||
if (!/https?\:\/\//.test(options.remote)) {
|
||||
throw new NexeError(`Invalid remote URI scheme (must be http or https): ${options.remote}`)
|
||||
throw new NexeError(
|
||||
`Invalid remote URI scheme (must be http or https): ${options.remote}`
|
||||
);
|
||||
}
|
||||
this.remoteAsset = options.remote + this.target.toString()
|
||||
this.src = join(this.options.temp, this.target.version)
|
||||
this.configureScript = configure + (semverGt(this.target.version, '10.10.0') ? '.py' : '')
|
||||
this.remoteAsset = options.remote + this.target.toString();
|
||||
this.src = join(this.options.temp, this.target.version);
|
||||
this.configureScript =
|
||||
configure + (semverGt(this.target.version, "10.10.0") ? ".py" : "");
|
||||
this.nodeSrcBinPath = isWindows
|
||||
? join(this.src, 'Release', 'node.exe')
|
||||
: join(this.src, 'out', 'Release', 'node')
|
||||
this.log.step('nexe ' + version, 'info')
|
||||
this.bundle = new Bundle(options)
|
||||
? join(this.src, "Release", "node.exe")
|
||||
: join(this.src, "out", "Release", "node");
|
||||
this.log.step("nexe " + version, "info");
|
||||
this.bundle = new Bundle(options);
|
||||
if (isWindows) {
|
||||
const originalPath = process.env.PATH
|
||||
delete process.env.PATH
|
||||
this.env = { ...process.env }
|
||||
const originalPath = process.env.PATH;
|
||||
delete process.env.PATH;
|
||||
this.env = { ...process.env };
|
||||
this.env.PATH = python
|
||||
? (this.env.PATH = dequote(normalize(python)) + delimiter + originalPath)
|
||||
: originalPath
|
||||
process.env.PATH = originalPath
|
||||
? (this.env.PATH =
|
||||
dequote(normalize(python)) + delimiter + originalPath)
|
||||
: originalPath;
|
||||
process.env.PATH = originalPath;
|
||||
} else {
|
||||
this.env = { ...process.env }
|
||||
python && (this.env.PYTHON = python)
|
||||
this.env = { ...process.env };
|
||||
python && (this.env.PYTHON = python);
|
||||
}
|
||||
}
|
||||
|
||||
@bound
|
||||
addResource(absoluteFileName: string, content?: Buffer | string | File) {
|
||||
return this.bundle.addResource(absoluteFileName, content)
|
||||
return this.bundle.addResource(absoluteFileName, content);
|
||||
}
|
||||
|
||||
@bound
|
||||
async readFileAsync(file: string) {
|
||||
this.assertBuild()
|
||||
let cachedFile = this.files.find((x) => normalize(x.filename) === normalize(file))
|
||||
this.assertBuild();
|
||||
let cachedFile = this.files.find(
|
||||
(x) => normalize(x.filename) === normalize(file)
|
||||
);
|
||||
if (!cachedFile) {
|
||||
const absPath = join(this.src, file)
|
||||
const absPath = join(this.src, file);
|
||||
cachedFile = {
|
||||
absPath,
|
||||
filename: file,
|
||||
contents: await readFileAsync(absPath, 'utf-8').catch((x) => {
|
||||
if (x.code === 'ENOENT') return ''
|
||||
throw x
|
||||
contents: await readFileAsync(absPath, "utf-8").catch((x) => {
|
||||
if (x.code === "ENOENT") return "";
|
||||
throw x;
|
||||
}),
|
||||
}
|
||||
this.files.push(cachedFile)
|
||||
};
|
||||
this.files.push(cachedFile);
|
||||
}
|
||||
return cachedFile
|
||||
return cachedFile;
|
||||
}
|
||||
|
||||
@bound
|
||||
writeFileAsync(file: string, contents: string | Buffer) {
|
||||
this.assertBuild()
|
||||
return writeFileAsync(join(this.src, file), contents)
|
||||
this.assertBuild();
|
||||
return writeFileAsync(join(this.src, file), contents);
|
||||
}
|
||||
|
||||
@bound
|
||||
async replaceInFileAsync(file: string, replace: string | RegExp, value: string | StringReplacer) {
|
||||
const entry = await this.readFileAsync(file)
|
||||
entry.contents = entry.contents.toString().replace(replace, value as any)
|
||||
async replaceInFileAsync(
|
||||
file: string,
|
||||
replace: string | RegExp,
|
||||
value: string | StringReplacer
|
||||
) {
|
||||
const entry = await this.readFileAsync(file);
|
||||
entry.contents = entry.contents.toString().replace(replace, value as any);
|
||||
}
|
||||
|
||||
@bound
|
||||
async setFileContentsAsync(file: string, contents: string | Buffer) {
|
||||
const entry = await this.readFileAsync(file)
|
||||
entry.contents = contents
|
||||
const entry = await this.readFileAsync(file);
|
||||
entry.contents = contents;
|
||||
}
|
||||
|
||||
quit(error?: any) {
|
||||
const time = Date.now() - this.start
|
||||
this.log.write(`Finished in ${time / 1000}s`, error ? 'red' : 'green')
|
||||
return this.log.flush()
|
||||
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`')
|
||||
throw new NexeError("This feature is only available with `--build`");
|
||||
}
|
||||
}
|
||||
|
||||
public getNodeExecutableLocation(target?: NexeTarget) {
|
||||
if (this.options.asset) {
|
||||
return resolve(this.options.cwd, this.options.asset)
|
||||
return resolve(this.options.cwd, this.options.asset);
|
||||
}
|
||||
if (target) {
|
||||
return join(this.options.temp, target.toString())
|
||||
return join(this.options.temp, target.toString());
|
||||
}
|
||||
return this.nodeSrcBinPath
|
||||
return this.nodeSrcBinPath;
|
||||
}
|
||||
|
||||
private _runBuildCommandAsync(command: string, args: string[]) {
|
||||
if (this.log.verbose) {
|
||||
this.compileStep!.pause()
|
||||
this.compileStep!.pause();
|
||||
}
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
spawn(command, args, {
|
||||
cwd: this.src,
|
||||
env: this.env,
|
||||
shell: true,
|
||||
stdio: this.log.verbose ? 'inherit' : 'ignore',
|
||||
stdio: this.log.verbose ? "inherit" : "ignore",
|
||||
})
|
||||
.once('error', (e: Error) => {
|
||||
.once("error", (e: Error) => {
|
||||
if (this.log.verbose) {
|
||||
this.compileStep!.resume()
|
||||
this.compileStep!.resume();
|
||||
}
|
||||
reject(e)
|
||||
reject(e);
|
||||
})
|
||||
.once('close', (code: number) => {
|
||||
.once("close", (code: number) => {
|
||||
if (this.log.verbose) {
|
||||
this.compileStep!.resume()
|
||||
this.compileStep!.resume();
|
||||
}
|
||||
if (code != 0) {
|
||||
const error = `${command} ${args.join(' ')} exited with code: ${code}`
|
||||
reject(new NexeError(error))
|
||||
const error = `${command} ${args.join(
|
||||
" "
|
||||
)} exited with code: ${code}`;
|
||||
reject(new NexeError(error));
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _configureAsync() {
|
||||
if (isWindows && semverGt(this.target.version, '10.15.99')) {
|
||||
return Promise.resolve()
|
||||
if (isWindows && semverGt(this.target.version, "10.15.99")) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._runBuildCommandAsync(this.env.PYTHON || 'python', [
|
||||
return this._runBuildCommandAsync(this.env.PYTHON || "python", [
|
||||
this.configureScript,
|
||||
...this.options.configure,
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
public async build(): Promise<ReadStream> {
|
||||
this.compileStep!.log(
|
||||
`Configuring node build${
|
||||
this.options.configure.length ? ': ' + this.options.configure : '...'
|
||||
this.options.configure.length ? ": " + this.options.configure : "..."
|
||||
}`
|
||||
)
|
||||
await this._configureAsync()
|
||||
const buildOptions = this.options.make
|
||||
);
|
||||
await this._configureAsync();
|
||||
const buildOptions = this.options.make;
|
||||
this.compileStep!.log(
|
||||
`Compiling Node${buildOptions.length ? ' with arguments: ' + buildOptions : '...'}`
|
||||
)
|
||||
await this._runBuildCommandAsync(make, buildOptions)
|
||||
return createReadStream(this.getNodeExecutableLocation())
|
||||
`Compiling Node${
|
||||
buildOptions.length ? " with arguments: " + buildOptions : "..."
|
||||
}`
|
||||
);
|
||||
await this._runBuildCommandAsync(make, buildOptions);
|
||||
return createReadStream(this.getNodeExecutableLocation());
|
||||
}
|
||||
|
||||
private async _shouldCompileBinaryAsync(binary: NodeJS.ReadableStream | null, location: string) {
|
||||
private async _shouldCompileBinaryAsync(
|
||||
binary: NodeJS.ReadableStream | null,
|
||||
location: string
|
||||
) {
|
||||
//SOMEDAY combine make/configure/vcBuild/and modified times of included files
|
||||
const { snapshot, build } = this.options
|
||||
const { snapshot, build } = this.options;
|
||||
|
||||
if (!binary) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
if (build && snapshot != null && (await pathExistsAsync(snapshot))) {
|
||||
const snapshotLastModified = (await statAsync(snapshot)).mtimeMs
|
||||
const binaryLastModified = (await statAsync(location)).mtimeMs
|
||||
return snapshotLastModified > binaryLastModified
|
||||
const snapshotLastModified = (await statAsync(snapshot)).mtimeMs;
|
||||
const binaryLastModified = (await statAsync(location)).mtimeMs;
|
||||
return snapshotLastModified > binaryLastModified;
|
||||
}
|
||||
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
async compileAsync(target: NexeTarget) {
|
||||
const step = (this.compileStep = this.log.step('Compiling result'))
|
||||
const build = this.options.build
|
||||
const location = this.getNodeExecutableLocation(build ? undefined : target)
|
||||
let binary = (await pathExistsAsync(location)) ? createReadStream(location) : null
|
||||
const step = (this.compileStep = this.log.step("Compiling result"));
|
||||
const build = this.options.build;
|
||||
const location = this.getNodeExecutableLocation(build ? undefined : target);
|
||||
let binary = (await pathExistsAsync(location))
|
||||
? createReadStream(location)
|
||||
: null;
|
||||
|
||||
if (await this._shouldCompileBinaryAsync(binary, location)) {
|
||||
binary = await this.build()
|
||||
step.log('Node binary compiled')
|
||||
binary = await this.build();
|
||||
step.log("Node binary compiled");
|
||||
}
|
||||
return this._assembleDeliverable(binary!)
|
||||
return this._assembleDeliverable(binary!);
|
||||
}
|
||||
|
||||
code() {
|
||||
return [this.shims.join(''), this.startup].join(';')
|
||||
return [this.shims.join(""), this.startup].join(";");
|
||||
}
|
||||
|
||||
private async _assembleDeliverable(binary: Readable) {
|
||||
if (!this.options.mangle) {
|
||||
return binary
|
||||
return binary;
|
||||
}
|
||||
const launchCode = this.code()
|
||||
const codeSize = Buffer.byteLength(launchCode)
|
||||
const sentinel = Buffer.from('<nexe~~sentinel>')
|
||||
const launchCode = this.code();
|
||||
const codeSize = Buffer.byteLength(launchCode);
|
||||
const sentinel = Buffer.from("<nexe~~sentinel>");
|
||||
|
||||
let vfsSize = 0
|
||||
let vfsSize = 0;
|
||||
const streams = [
|
||||
binary,
|
||||
toStream(launchCode),
|
||||
this.bundle.toStream().pipe(
|
||||
new Transform({
|
||||
transform: (chunk, _, cb) => {
|
||||
vfsSize || this.bundle.finalize()
|
||||
chunk && (vfsSize += chunk.length)
|
||||
cb(null, chunk)
|
||||
vfsSize || this.bundle.finalize();
|
||||
chunk && (vfsSize += chunk.length);
|
||||
cb(null, chunk);
|
||||
},
|
||||
})
|
||||
),
|
||||
]
|
||||
];
|
||||
|
||||
let done = false
|
||||
let done = false;
|
||||
return new MultiStream((cb) => {
|
||||
if (done) cb(null, null)
|
||||
else if (streams.length) cb(null, streams.shift() as Readable)
|
||||
if (done) cb(null, null);
|
||||
else if (streams.length) cb(null, streams.shift() as Readable);
|
||||
else {
|
||||
done = true
|
||||
const trailers = Buffer.alloc(16)
|
||||
trailers.writeDoubleLE(codeSize, 0)
|
||||
trailers.writeDoubleLE(vfsSize, 8)
|
||||
cb(null, toStream(Buffer.concat([sentinel, trailers])))
|
||||
done = true;
|
||||
const trailers = Buffer.alloc(16);
|
||||
trailers.writeDoubleLE(codeSize, 0);
|
||||
trailers.writeDoubleLE(vfsSize, 8);
|
||||
cb(null, toStream(Buffer.concat([sentinel, trailers])));
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+246
-187
@@ -1,4 +1,4 @@
|
||||
import { Libzip } from '@yarnpkg/libzip'
|
||||
import { Libzip } from "@yarnpkg/libzip";
|
||||
import {
|
||||
FakeFS,
|
||||
PortablePath,
|
||||
@@ -13,45 +13,50 @@ import {
|
||||
npath,
|
||||
Filename,
|
||||
CustomDir,
|
||||
} from '@yarnpkg/fslib'
|
||||
import { ZipFS, ZipOpenFS } from '@yarnpkg/libzip'
|
||||
import { resolve, toNamespacedPath } from 'path'
|
||||
import { constants } from 'fs'
|
||||
} from "@yarnpkg/fslib";
|
||||
import { ZipFS, ZipOpenFS } from "@yarnpkg/libzip";
|
||||
import { resolve, toNamespacedPath } from "path";
|
||||
import { constants } from "fs";
|
||||
|
||||
export type SnapshotZipFSOptions = {
|
||||
baseFs: FakeFS<PortablePath>
|
||||
libzip: Libzip | (() => Libzip)
|
||||
zipFs: ZipFS
|
||||
root: string
|
||||
}
|
||||
baseFs: FakeFS<PortablePath>;
|
||||
libzip: Libzip | (() => Libzip);
|
||||
zipFs: ZipFS;
|
||||
root: string;
|
||||
};
|
||||
|
||||
const uniqBy = (arr: Array<string | Dirent>, pick: (...arg: any[]) => any) => {
|
||||
const seen = new Set()
|
||||
const uniqBy = (
|
||||
arr: Array<string | Dirent<PortablePath>>,
|
||||
pick: (...arg: any[]) => any
|
||||
) => {
|
||||
const seen = new Set();
|
||||
return arr.filter((x) => {
|
||||
const key = pick(x)
|
||||
if (seen.has(key)) return false
|
||||
seen.add(key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
function uniqReaddir(arr: Array<string | Dirent>) {
|
||||
return uniqBy(arr, (s: string | Dirent) => (typeof s === 'string' ? s : s.name))
|
||||
const key = pick(x);
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
};
|
||||
function uniqReaddir(arr: Array<string | Dirent<PortablePath>>) {
|
||||
return uniqBy(arr, (s: string | Dirent<PortablePath>) =>
|
||||
typeof s === "string" ? s : s.name
|
||||
);
|
||||
}
|
||||
|
||||
export class SnapshotZipFS extends BasePortableFakeFS {
|
||||
zipFs: ZipFS
|
||||
baseFs: FakeFS<PortablePath>
|
||||
root: string
|
||||
magic: number
|
||||
zipFs: ZipFS;
|
||||
baseFs: FakeFS<PortablePath>;
|
||||
root: string;
|
||||
magic: number;
|
||||
constructor(opts: SnapshotZipFSOptions) {
|
||||
super()
|
||||
this.zipFs = opts.zipFs
|
||||
this.baseFs = opts.baseFs
|
||||
this.root = opts.root
|
||||
this.magic = 0x2a << 24
|
||||
super();
|
||||
this.zipFs = opts.zipFs;
|
||||
this.baseFs = opts.baseFs;
|
||||
this.root = opts.root;
|
||||
this.magic = 0x2a << 24;
|
||||
}
|
||||
private readonly fdMap: Map<number, [ZipFS, number]> = new Map()
|
||||
private nextFd = 3
|
||||
private readonly fdMap: Map<number, [ZipFS, number]> = new Map();
|
||||
private nextFd = 3;
|
||||
|
||||
async makeCallPromise<T>(
|
||||
p: FSPath<PortablePath>,
|
||||
@@ -59,38 +64,41 @@ export class SnapshotZipFS extends BasePortableFakeFS {
|
||||
accept: (zipFS: ZipFS, zipInfo: { subPath: PortablePath }) => Promise<T>,
|
||||
{ requireSubpath = true }: { requireSubpath?: boolean } = {}
|
||||
): Promise<T> {
|
||||
if (typeof p !== 'string') return await discard()
|
||||
if (typeof p !== "string") return await discard();
|
||||
|
||||
const normalizedP = this.resolve(p)
|
||||
const normalizedP = this.resolve(p);
|
||||
|
||||
const zipInfo = this.findZip(normalizedP)
|
||||
if (!zipInfo) return await discard()
|
||||
const zipInfo = this.findZip(normalizedP);
|
||||
if (!zipInfo) return await discard();
|
||||
|
||||
if (requireSubpath && zipInfo.subPath === '/') return await discard()
|
||||
if (requireSubpath && zipInfo.subPath === "/") return await discard();
|
||||
|
||||
return await accept(this.zipFs, zipInfo)
|
||||
return await accept(this.zipFs, zipInfo);
|
||||
}
|
||||
|
||||
makeCallSync<T>(
|
||||
p: FSPath<PortablePath>,
|
||||
discard: () => T,
|
||||
accept: (zipFS: ZipFS, zipInfo: { subPath: PortablePath; archivePath: string }) => T,
|
||||
accept: (
|
||||
zipFS: ZipFS,
|
||||
zipInfo: { subPath: PortablePath; archivePath: string }
|
||||
) => T,
|
||||
{ requireSubpath = true }: { requireSubpath?: boolean } = {}
|
||||
): T {
|
||||
if (typeof p !== 'string') return discard()
|
||||
if (typeof p !== "string") return discard();
|
||||
|
||||
const normalizedP = this.resolve(p)
|
||||
const normalizedP = this.resolve(p);
|
||||
|
||||
const zipInfo = this.findZip(normalizedP)
|
||||
if (!zipInfo) return discard()
|
||||
const zipInfo = this.findZip(normalizedP);
|
||||
if (!zipInfo) return discard();
|
||||
|
||||
if (requireSubpath && zipInfo.subPath === '/') return discard()
|
||||
if (requireSubpath && zipInfo.subPath === "/") return discard();
|
||||
|
||||
return accept(this.zipFs, { archivePath: '', ...zipInfo })
|
||||
return accept(this.zipFs, { archivePath: "", ...zipInfo });
|
||||
}
|
||||
|
||||
async realpathPromise(p: PortablePath) {
|
||||
return await this.realpathSync(p)
|
||||
return await this.realpathSync(p);
|
||||
}
|
||||
|
||||
realpathSync(p: PortablePath) {
|
||||
@@ -99,26 +107,29 @@ export class SnapshotZipFS extends BasePortableFakeFS {
|
||||
() => this.baseFs.realpathSync(p),
|
||||
(zipFs, { subPath }) => {
|
||||
if (zipFs.lstatSync(subPath).isSymbolicLink()) {
|
||||
return zipFs.realpathSync(subPath)
|
||||
return zipFs.realpathSync(subPath);
|
||||
} else {
|
||||
// return the original path in case it wasn't under /snapshot, e.g. if it was for a node module - otherwise the node module parent path is the wrong one (and other things resolve relative to that)
|
||||
return p
|
||||
return p;
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
findZip(p: PortablePath) {
|
||||
p = this.resolve(p)
|
||||
const snapshotPP = npath.toPortablePath('/snapshot')
|
||||
p = this.resolve(p);
|
||||
const snapshotPP = npath.toPortablePath("/snapshot");
|
||||
const pathsToTry = Array.from(
|
||||
new Set([
|
||||
p,
|
||||
resolve('/snapshot', p),
|
||||
resolve("/snapshot", p),
|
||||
ppath.resolve(
|
||||
snapshotPP,
|
||||
npath.toPortablePath(
|
||||
npath.relative(npath.fromPortablePath(this.root), npath.fromPortablePath(p))
|
||||
npath.relative(
|
||||
npath.fromPortablePath(this.root),
|
||||
npath.fromPortablePath(p)
|
||||
)
|
||||
)
|
||||
),
|
||||
ppath.resolve(
|
||||
@@ -133,7 +144,10 @@ export class SnapshotZipFS extends BasePortableFakeFS {
|
||||
ppath.resolve(
|
||||
snapshotPP,
|
||||
npath.toPortablePath(
|
||||
npath.relative(npath.fromPortablePath(process.cwd()), npath.fromPortablePath(p))
|
||||
npath.relative(
|
||||
npath.fromPortablePath(process.cwd()),
|
||||
npath.fromPortablePath(p)
|
||||
)
|
||||
)
|
||||
),
|
||||
ppath.resolve(
|
||||
@@ -146,18 +160,18 @@ export class SnapshotZipFS extends BasePortableFakeFS {
|
||||
)
|
||||
),
|
||||
])
|
||||
)
|
||||
);
|
||||
for (const path of pathsToTry) {
|
||||
const portablePath = npath.toPortablePath(path)
|
||||
const portablePath = npath.toPortablePath(path);
|
||||
if (this.zipFs.existsSync(portablePath)) {
|
||||
return {
|
||||
subPath: portablePath,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async copyFilePromise(sourceP: PortablePath, destP: PortablePath, flags: number = 0) {
|
||||
async copyFilePromise(sourceP: PortablePath, destP: PortablePath, flags = 0) {
|
||||
const fallback = async (
|
||||
sourceFs: FakeFS<PortablePath>,
|
||||
sourceP: PortablePath,
|
||||
@@ -166,40 +180,49 @@ export class SnapshotZipFS extends BasePortableFakeFS {
|
||||
) => {
|
||||
if ((flags & constants.COPYFILE_FICLONE_FORCE) !== 0)
|
||||
throw Object.assign(
|
||||
new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`),
|
||||
new Error(
|
||||
`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`
|
||||
),
|
||||
{ code: `EXDEV` }
|
||||
)
|
||||
if (flags & constants.COPYFILE_EXCL && (await this.existsPromise(sourceP)))
|
||||
);
|
||||
if (
|
||||
flags & constants.COPYFILE_EXCL &&
|
||||
(await this.existsPromise(sourceP))
|
||||
)
|
||||
throw Object.assign(
|
||||
new Error(`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`),
|
||||
new Error(
|
||||
`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`
|
||||
),
|
||||
{ code: `EEXIST` }
|
||||
)
|
||||
);
|
||||
|
||||
let content
|
||||
let content;
|
||||
try {
|
||||
content = await sourceFs.readFilePromise(sourceP)
|
||||
content = await sourceFs.readFilePromise(sourceP);
|
||||
} catch (error) {
|
||||
throw Object.assign(
|
||||
new Error(`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`),
|
||||
new Error(
|
||||
`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`
|
||||
),
|
||||
{ code: `EINVAL` }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
await destFs.writeFilePromise(destP, content)
|
||||
}
|
||||
await destFs.writeFilePromise(destP, content);
|
||||
};
|
||||
|
||||
return await this.makeCallPromise(
|
||||
sourceP,
|
||||
async () => {
|
||||
return await this.baseFs.copyFilePromise(sourceP, destP, flags)
|
||||
return await this.baseFs.copyFilePromise(sourceP, destP, flags);
|
||||
},
|
||||
async (zipFsS, { subPath: subPathS }) => {
|
||||
return await fallback(zipFsS, subPathS, this.baseFs, destP)
|
||||
return await fallback(zipFsS, subPathS, this.baseFs, destP);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
copyFileSync(sourceP: PortablePath, destP: PortablePath, flags: number = 0) {
|
||||
copyFileSync(sourceP: PortablePath, destP: PortablePath, flags = 0) {
|
||||
const fallback = (
|
||||
sourceFs: FakeFS<PortablePath>,
|
||||
sourceP: PortablePath,
|
||||
@@ -208,203 +231,239 @@ export class SnapshotZipFS extends BasePortableFakeFS {
|
||||
) => {
|
||||
if ((flags & constants.COPYFILE_FICLONE_FORCE) !== 0)
|
||||
throw Object.assign(
|
||||
new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`),
|
||||
new Error(
|
||||
`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`
|
||||
),
|
||||
{ code: `EXDEV` }
|
||||
)
|
||||
);
|
||||
if (flags & constants.COPYFILE_EXCL && this.existsSync(sourceP))
|
||||
throw Object.assign(
|
||||
new Error(`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`),
|
||||
new Error(
|
||||
`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`
|
||||
),
|
||||
{ code: `EEXIST` }
|
||||
)
|
||||
);
|
||||
|
||||
let content
|
||||
let content;
|
||||
try {
|
||||
content = sourceFs.readFileSync(sourceP)
|
||||
content = sourceFs.readFileSync(sourceP);
|
||||
} catch (error) {
|
||||
throw Object.assign(
|
||||
new Error(`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`),
|
||||
new Error(
|
||||
`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`
|
||||
),
|
||||
{ code: `EINVAL` }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
destFs.writeFileSync(destP, content)
|
||||
}
|
||||
destFs.writeFileSync(destP, content);
|
||||
};
|
||||
|
||||
return this.makeCallSync(
|
||||
sourceP,
|
||||
() => {
|
||||
return this.baseFs.copyFileSync(sourceP, destP, flags)
|
||||
return this.baseFs.copyFileSync(sourceP, destP, flags);
|
||||
},
|
||||
(zipFsS, { subPath: subPathS }) => {
|
||||
return fallback(zipFsS, subPathS, this.baseFs, destP)
|
||||
return fallback(zipFsS, subPathS, this.baseFs, destP);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
async readdirPromise(p: PortablePath): Promise<Array<Filename>>
|
||||
async readdirPromise(p: PortablePath): Promise<Array<Filename>>;
|
||||
async readdirPromise(
|
||||
p: PortablePath,
|
||||
opts: { withFileTypes: false } | null
|
||||
): Promise<Array<Filename>>
|
||||
async readdirPromise(p: PortablePath, opts: { withFileTypes: true }): Promise<Array<Dirent>>
|
||||
): Promise<Array<Filename>>;
|
||||
async readdirPromise(
|
||||
p: PortablePath,
|
||||
opts: { withFileTypes: true }
|
||||
): Promise<Array<Dirent<PortablePath>>>;
|
||||
async readdirPromise(
|
||||
p: PortablePath,
|
||||
opts: { withFileTypes: boolean }
|
||||
): Promise<Array<Filename> | Array<Dirent>>
|
||||
): Promise<Array<Filename> | Array<Dirent<PortablePath>>>;
|
||||
async readdirPromise(
|
||||
p: PortablePath,
|
||||
opts?: { withFileTypes?: boolean } | null
|
||||
): Promise<Array<string | Dirent>> {
|
||||
): Promise<Array<string | Dirent<PortablePath>>> {
|
||||
const fallback = async () => {
|
||||
return await this.baseFs.readdirPromise(p, opts as any)
|
||||
}
|
||||
return await this.baseFs.readdirPromise(p, opts as any);
|
||||
};
|
||||
return await this.makeCallPromise(
|
||||
p,
|
||||
fallback,
|
||||
async (zipFs, { subPath }) => {
|
||||
const fallbackPaths: Array<string | Dirent> = await fallback().catch(() => [])
|
||||
const fallbackPaths: Array<string | Dirent<PortablePath>> =
|
||||
await fallback().catch(() => []);
|
||||
return Promise.resolve(
|
||||
uniqReaddir(fallbackPaths.concat(await zipFs.readdirPromise(subPath, opts as any)))
|
||||
)
|
||||
uniqReaddir(
|
||||
fallbackPaths.concat(
|
||||
await zipFs.readdirPromise(subPath, opts as any)
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
{
|
||||
requireSubpath: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
readdirSync(p: PortablePath): Array<Filename>
|
||||
readdirSync(p: PortablePath, opts: { withFileTypes: false } | null): Array<Filename>
|
||||
readdirSync(p: PortablePath, opts: { withFileTypes: true }): Array<Dirent>
|
||||
readdirSync(p: PortablePath, opts: { withFileTypes: boolean }): Array<Filename> | Array<Dirent>
|
||||
readdirSync(p: PortablePath, opts?: { withFileTypes?: boolean } | null): Array<string | Dirent> {
|
||||
readdirSync(p: PortablePath): Array<Filename>;
|
||||
readdirSync(
|
||||
p: PortablePath,
|
||||
opts: { withFileTypes: false } | null
|
||||
): Array<Filename>;
|
||||
readdirSync(
|
||||
p: PortablePath,
|
||||
opts: { withFileTypes: true }
|
||||
): Array<Dirent<PortablePath>>;
|
||||
readdirSync(
|
||||
p: PortablePath,
|
||||
opts: { withFileTypes: boolean }
|
||||
): Array<Filename> | Array<Dirent<PortablePath>>;
|
||||
readdirSync(
|
||||
p: PortablePath,
|
||||
opts?: { withFileTypes?: boolean } | null
|
||||
): Array<string | Dirent<PortablePath>> {
|
||||
const fallback = () => {
|
||||
return this.baseFs.readdirSync(p, opts as any)
|
||||
}
|
||||
return this.baseFs.readdirSync(p, opts as any);
|
||||
};
|
||||
return this.makeCallSync(
|
||||
p,
|
||||
fallback,
|
||||
(zipFs, { subPath }) => {
|
||||
let fallbackPaths: Array<string | Dirent> = []
|
||||
let fallbackPaths: Array<string | Dirent<PortablePath>> = [];
|
||||
try {
|
||||
fallbackPaths = fallback()
|
||||
fallbackPaths = fallback();
|
||||
} catch (e) {}
|
||||
return fallbackPaths.concat(uniqReaddir(zipFs.readdirSync(subPath, opts as any)))
|
||||
return fallbackPaths.concat(
|
||||
uniqReaddir(zipFs.readdirSync(subPath, opts as any))
|
||||
);
|
||||
},
|
||||
{
|
||||
requireSubpath: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async mkdirPromise(p: PortablePath, opts?: MkdirOptions) {
|
||||
return await this.baseFs.mkdirPromise(p, opts)
|
||||
return await this.baseFs.mkdirPromise(p, opts);
|
||||
}
|
||||
|
||||
mkdirSync(p: PortablePath, opts?: MkdirOptions) {
|
||||
return this.baseFs.mkdirSync(p, opts)
|
||||
return this.baseFs.mkdirSync(p, opts);
|
||||
}
|
||||
|
||||
async rmdirPromise(p: PortablePath, opts?: RmdirOptions) {
|
||||
return await this.baseFs.rmdirPromise(p, opts)
|
||||
return await this.baseFs.rmdirPromise(p, opts);
|
||||
}
|
||||
|
||||
rmdirSync(p: PortablePath, opts?: RmdirOptions) {
|
||||
return this.baseFs.rmdirSync(p, opts)
|
||||
return this.baseFs.rmdirSync(p, opts);
|
||||
}
|
||||
|
||||
async rmPromise(p: PortablePath, opts?: any) {
|
||||
return await this.baseFs.rmPromise(p, opts);
|
||||
}
|
||||
|
||||
rmSync(p: PortablePath, opts?: any) {
|
||||
return this.baseFs.rmSync(p, opts);
|
||||
}
|
||||
|
||||
async opendirPromise(p: PortablePath, opts?: OpendirOptions) {
|
||||
return this.opendirSync(p, opts)
|
||||
return this.opendirSync(p, opts);
|
||||
}
|
||||
|
||||
opendirSync(p: PortablePath, opts?: OpendirOptions) {
|
||||
const zipInfo = this.findZip(p)
|
||||
let zipFsDir: Dir<PortablePath> | null = null
|
||||
const zipInfo = this.findZip(p);
|
||||
let zipFsDir: Dir<PortablePath> | null = null;
|
||||
if (zipInfo) {
|
||||
zipFsDir = this.zipFs.opendirSync(zipInfo.subPath)
|
||||
zipFsDir = this.zipFs.opendirSync(zipInfo.subPath);
|
||||
}
|
||||
let realFsDir: Dir<PortablePath> | null = null
|
||||
let realFsDir: Dir<PortablePath> | null = null;
|
||||
try {
|
||||
realFsDir = this.baseFs.opendirSync(p)
|
||||
realFsDir = this.baseFs.opendirSync(p);
|
||||
} catch (e) {
|
||||
if (!zipFsDir) throw e
|
||||
if (!zipFsDir) throw e;
|
||||
}
|
||||
const seen = new Set()
|
||||
const seen = new Set();
|
||||
const nextDirent = () => {
|
||||
const entry = realFsDir?.readSync() || zipFsDir?.readSync()
|
||||
const entry = realFsDir?.readSync() || zipFsDir?.readSync();
|
||||
if (entry && !seen.has(entry.name)) {
|
||||
seen.add(entry.name)
|
||||
return entry
|
||||
seen.add(entry.name);
|
||||
return entry;
|
||||
}
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
zipFsDir?.closeSync()
|
||||
realFsDir?.closeSync()
|
||||
}
|
||||
zipFsDir?.closeSync();
|
||||
realFsDir?.closeSync();
|
||||
};
|
||||
|
||||
return new CustomDir(p, nextDirent, { onClose })
|
||||
return new CustomDir(p, nextDirent, { onClose });
|
||||
}
|
||||
|
||||
accessPromise = ZipOpenFS.prototype.accessPromise
|
||||
accessSync = ZipOpenFS.prototype.accessSync
|
||||
appendFilePromise = ZipOpenFS.prototype.appendFilePromise
|
||||
appendFileSync = ZipOpenFS.prototype.appendFileSync
|
||||
chmodPromise = ZipOpenFS.prototype.chmodPromise
|
||||
chmodSync = ZipOpenFS.prototype.chmodSync
|
||||
fchmodPromise = ZipOpenFS.prototype.fchmodPromise
|
||||
fchmodSync = ZipOpenFS.prototype.fchmodSync
|
||||
chownPromise = ZipOpenFS.prototype.chownPromise
|
||||
chownSync = ZipOpenFS.prototype.chownSync
|
||||
fchownPromise = ZipOpenFS.prototype.fchownPromise
|
||||
fchownSync = ZipOpenFS.prototype.fchownSync
|
||||
closePromise = ZipOpenFS.prototype.closePromise
|
||||
closeSync = ZipOpenFS.prototype.closeSync
|
||||
createReadStream = ZipOpenFS.prototype.createReadStream
|
||||
createWriteStream = ZipOpenFS.prototype.createWriteStream
|
||||
existsPromise = ZipOpenFS.prototype.existsPromise
|
||||
existsSync = ZipOpenFS.prototype.existsSync
|
||||
fstatPromise = ZipOpenFS.prototype.fstatPromise
|
||||
fstatSync = ZipOpenFS.prototype.fstatSync
|
||||
getExtractHint = ZipOpenFS.prototype.getExtractHint
|
||||
getRealPath = ZipOpenFS.prototype.getRealPath
|
||||
linkPromise = ZipOpenFS.prototype.linkPromise
|
||||
linkSync = ZipOpenFS.prototype.linkSync
|
||||
lstatPromise = ZipOpenFS.prototype.lstatPromise
|
||||
lstatSync = ZipOpenFS.prototype.lstatSync
|
||||
openPromise = ZipOpenFS.prototype.openPromise
|
||||
openSync = ZipOpenFS.prototype.openSync
|
||||
readFilePromise = ZipOpenFS.prototype.readFilePromise
|
||||
readFileSync = ZipOpenFS.prototype.readFileSync
|
||||
readlinkPromise = ZipOpenFS.prototype.readlinkPromise
|
||||
readlinkSync = ZipOpenFS.prototype.readlinkSync
|
||||
readPromise = ZipOpenFS.prototype.readPromise
|
||||
readSync = ZipOpenFS.prototype.readSync
|
||||
renamePromise = ZipOpenFS.prototype.renamePromise
|
||||
renameSync = ZipOpenFS.prototype.renameSync
|
||||
resolve = ZipOpenFS.prototype.resolve
|
||||
statPromise = ZipOpenFS.prototype.statPromise
|
||||
statSync = ZipOpenFS.prototype.statSync
|
||||
symlinkPromise = ZipOpenFS.prototype.symlinkPromise
|
||||
symlinkSync = ZipOpenFS.prototype.symlinkSync
|
||||
truncatePromise = ZipOpenFS.prototype.truncatePromise
|
||||
truncateSync = ZipOpenFS.prototype.truncateSync
|
||||
ftruncatePromise = ZipOpenFS.prototype.ftruncatePromise
|
||||
ftruncateSync = ZipOpenFS.prototype.ftruncateSync
|
||||
unlinkPromise = ZipOpenFS.prototype.unlinkPromise
|
||||
unlinkSync = ZipOpenFS.prototype.unlinkSync
|
||||
unwatchFile = ZipOpenFS.prototype.unwatchFile
|
||||
utimesPromise = ZipOpenFS.prototype.utimesPromise
|
||||
utimesSync = ZipOpenFS.prototype.utimesSync
|
||||
lutimesPromise = ZipOpenFS.prototype.utimesPromise
|
||||
lutimesSync = ZipOpenFS.prototype.utimesSync
|
||||
watch = ZipOpenFS.prototype.watch
|
||||
watchFile = ZipOpenFS.prototype.watchFile
|
||||
writeFilePromise = ZipOpenFS.prototype.writeFilePromise
|
||||
writeFileSync = ZipOpenFS.prototype.writeFileSync
|
||||
writePromise = ZipOpenFS.prototype.writePromise
|
||||
writeSync = ZipOpenFS.prototype.writeSync
|
||||
accessPromise = ZipOpenFS.prototype.accessPromise;
|
||||
accessSync = ZipOpenFS.prototype.accessSync;
|
||||
appendFilePromise = ZipOpenFS.prototype.appendFilePromise;
|
||||
appendFileSync = ZipOpenFS.prototype.appendFileSync;
|
||||
chmodPromise = ZipOpenFS.prototype.chmodPromise;
|
||||
chmodSync = ZipOpenFS.prototype.chmodSync;
|
||||
fchmodPromise = ZipOpenFS.prototype.fchmodPromise;
|
||||
fchmodSync = ZipOpenFS.prototype.fchmodSync;
|
||||
chownPromise = ZipOpenFS.prototype.chownPromise;
|
||||
chownSync = ZipOpenFS.prototype.chownSync;
|
||||
fchownPromise = ZipOpenFS.prototype.fchownPromise;
|
||||
fchownSync = ZipOpenFS.prototype.fchownSync;
|
||||
closePromise = ZipOpenFS.prototype.closePromise;
|
||||
closeSync = ZipOpenFS.prototype.closeSync;
|
||||
createReadStream = ZipOpenFS.prototype.createReadStream;
|
||||
createWriteStream = ZipOpenFS.prototype.createWriteStream;
|
||||
existsPromise = ZipOpenFS.prototype.existsPromise;
|
||||
existsSync = ZipOpenFS.prototype.existsSync;
|
||||
fstatPromise = ZipOpenFS.prototype.fstatPromise;
|
||||
fstatSync = ZipOpenFS.prototype.fstatSync;
|
||||
getExtractHint = ZipOpenFS.prototype.getExtractHint;
|
||||
getRealPath = ZipOpenFS.prototype.getRealPath;
|
||||
linkPromise = ZipOpenFS.prototype.linkPromise;
|
||||
linkSync = ZipOpenFS.prototype.linkSync;
|
||||
lstatPromise = ZipOpenFS.prototype.lstatPromise;
|
||||
lstatSync = ZipOpenFS.prototype.lstatSync;
|
||||
openPromise = ZipOpenFS.prototype.openPromise;
|
||||
openSync = ZipOpenFS.prototype.openSync;
|
||||
readFilePromise = ZipOpenFS.prototype.readFilePromise;
|
||||
readFileSync = ZipOpenFS.prototype.readFileSync;
|
||||
readlinkPromise = ZipOpenFS.prototype.readlinkPromise;
|
||||
readlinkSync = ZipOpenFS.prototype.readlinkSync;
|
||||
readPromise = ZipOpenFS.prototype.readPromise;
|
||||
readSync = ZipOpenFS.prototype.readSync;
|
||||
renamePromise = ZipOpenFS.prototype.renamePromise;
|
||||
renameSync = ZipOpenFS.prototype.renameSync;
|
||||
resolve = ZipOpenFS.prototype.resolve;
|
||||
statPromise = ZipOpenFS.prototype.statPromise;
|
||||
statSync = ZipOpenFS.prototype.statSync;
|
||||
symlinkPromise = ZipOpenFS.prototype.symlinkPromise;
|
||||
symlinkSync = ZipOpenFS.prototype.symlinkSync;
|
||||
truncatePromise = ZipOpenFS.prototype.truncatePromise;
|
||||
truncateSync = ZipOpenFS.prototype.truncateSync;
|
||||
ftruncatePromise = ZipOpenFS.prototype.ftruncatePromise;
|
||||
ftruncateSync = ZipOpenFS.prototype.ftruncateSync;
|
||||
unlinkPromise = ZipOpenFS.prototype.unlinkPromise;
|
||||
unlinkSync = ZipOpenFS.prototype.unlinkSync;
|
||||
unwatchFile = ZipOpenFS.prototype.unwatchFile;
|
||||
utimesPromise = ZipOpenFS.prototype.utimesPromise;
|
||||
utimesSync = ZipOpenFS.prototype.utimesSync;
|
||||
lutimesPromise = ZipOpenFS.prototype.utimesPromise;
|
||||
lutimesSync = ZipOpenFS.prototype.utimesSync;
|
||||
watch = ZipOpenFS.prototype.watch;
|
||||
watchFile = ZipOpenFS.prototype.watchFile;
|
||||
writeFilePromise = ZipOpenFS.prototype.writeFilePromise;
|
||||
writeFileSync = ZipOpenFS.prototype.writeFileSync;
|
||||
writePromise = ZipOpenFS.prototype.writePromise;
|
||||
writeSync = ZipOpenFS.prototype.writeSync;
|
||||
|
||||
// @ts-ignore
|
||||
remapFd = ZipOpenFS.prototype.remapFd
|
||||
remapFd = ZipOpenFS.prototype.remapFd;
|
||||
}
|
||||
|
||||
+25
-22
@@ -1,51 +1,54 @@
|
||||
import { relative } from 'path'
|
||||
import { Readable } from 'stream'
|
||||
import { File } from 'resolve-dependencies'
|
||||
import archiver from 'archiver'
|
||||
import { relative } from "path";
|
||||
import { Readable } from "stream";
|
||||
import { File } from "resolve-dependencies";
|
||||
import archiver from "archiver";
|
||||
|
||||
function makeRelativeToZip(cwd: string, path: string) {
|
||||
return '/snapshot/' + relative(cwd, path)
|
||||
return "/snapshot/" + relative(cwd, path);
|
||||
}
|
||||
|
||||
export function toStream(content: Buffer | string) {
|
||||
const readable = new Readable({
|
||||
read() {
|
||||
this.push(content)
|
||||
this.push(null)
|
||||
this.push(content);
|
||||
this.push(null);
|
||||
},
|
||||
})
|
||||
return readable
|
||||
});
|
||||
return readable;
|
||||
}
|
||||
export class Bundle {
|
||||
cwd: string
|
||||
files = new Set<string>()
|
||||
zip: any
|
||||
cwd: string;
|
||||
files = new Set<string>();
|
||||
zip: any;
|
||||
constructor({ cwd }: { cwd: string } = { cwd: process.cwd() }) {
|
||||
this.cwd = cwd
|
||||
this.zip = archiver('zip')
|
||||
this.cwd = cwd;
|
||||
this.zip = archiver("zip");
|
||||
}
|
||||
|
||||
get list() {
|
||||
return Array.from(this.files)
|
||||
return Array.from(this.files);
|
||||
}
|
||||
|
||||
public addResource(absoluteFileName: string, content?: File | Buffer | string) {
|
||||
const destPath = makeRelativeToZip(this.cwd, absoluteFileName)
|
||||
public addResource(
|
||||
absoluteFileName: string,
|
||||
content?: File | Buffer | string
|
||||
) {
|
||||
const destPath = makeRelativeToZip(this.cwd, absoluteFileName);
|
||||
if (!this.files.has(destPath)) {
|
||||
if (content == null) {
|
||||
this.zip.file(absoluteFileName, { name: destPath })
|
||||
this.zip.file(absoluteFileName, { name: destPath });
|
||||
} else {
|
||||
this.zip.append(content, { name: destPath })
|
||||
this.zip.append(content, { name: destPath });
|
||||
}
|
||||
this.files.add(destPath)
|
||||
this.files.add(destPath);
|
||||
}
|
||||
}
|
||||
|
||||
public finalize() {
|
||||
return this.zip.finalize()
|
||||
return this.zip.finalize();
|
||||
}
|
||||
|
||||
public toStream(): Readable {
|
||||
return this.zip
|
||||
return this.zip;
|
||||
}
|
||||
}
|
||||
|
||||
+85
-66
@@ -1,117 +1,136 @@
|
||||
import { ZipFS, getLibzipSync } from '@yarnpkg/libzip'
|
||||
import { patchFs, npath, PosixFS, NodeFS } from '@yarnpkg/fslib'
|
||||
import { SnapshotZipFS } from './SnapshotZipFS'
|
||||
import * as assert from 'assert'
|
||||
import * as constants from 'constants'
|
||||
import { dirname } from 'path'
|
||||
import { ZipFS, getLibzipSync } from "@yarnpkg/libzip";
|
||||
import { patchFs, npath, PosixFS, NodeFS } from "@yarnpkg/fslib";
|
||||
import { SnapshotZipFS } from "./SnapshotZipFS";
|
||||
import * as assert from "assert";
|
||||
import * as constants from "constants";
|
||||
import { dirname } from "path";
|
||||
|
||||
export interface NexeHeader {
|
||||
blobPath: string
|
||||
blobPath: string;
|
||||
layout: {
|
||||
resourceStart: number
|
||||
resourceSize: number
|
||||
contentSize: number
|
||||
contentStart: number
|
||||
}
|
||||
resourceStart: number;
|
||||
resourceSize: number;
|
||||
contentSize: number;
|
||||
contentStart: number;
|
||||
};
|
||||
}
|
||||
|
||||
let originalFsMethods: any = null
|
||||
let lazyRestoreFs = () => {}
|
||||
const patches = (process as any).nexe.patches || {}
|
||||
const originalPatches = { ...patches }
|
||||
delete (process as any).nexe
|
||||
let originalFsMethods: any = null;
|
||||
let lazyRestoreFs = () => {};
|
||||
const patches = (process as any).nexe.patches || {};
|
||||
const originalPatches = { ...patches };
|
||||
delete (process as any).nexe;
|
||||
|
||||
function shimFs(binary: NexeHeader, fs: typeof import('fs') = require('fs')) {
|
||||
function shimFs(binary: NexeHeader, fs: typeof import("fs") = require("fs")) {
|
||||
if (originalFsMethods !== null) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
originalFsMethods = Object.assign({}, fs)
|
||||
originalFsMethods = Object.assign({}, fs);
|
||||
|
||||
const realFs: typeof fs = { ...fs }
|
||||
const nodeFs = new NodeFS(realFs)
|
||||
const realFs: typeof fs = { ...fs };
|
||||
const nodeFs = new NodeFS(realFs);
|
||||
|
||||
const blob = Buffer.allocUnsafe(binary.layout.resourceSize)
|
||||
const blobFd = realFs.openSync(binary.blobPath, 'r')
|
||||
const blob = Buffer.allocUnsafe(binary.layout.resourceSize);
|
||||
const blobFd = realFs.openSync(binary.blobPath, "r");
|
||||
const bytesRead = realFs.readSync(
|
||||
blobFd,
|
||||
blob,
|
||||
0,
|
||||
binary.layout.resourceSize,
|
||||
binary.layout.resourceStart
|
||||
)
|
||||
assert.equal(bytesRead, binary.layout.resourceSize)
|
||||
);
|
||||
assert.equal(bytesRead, binary.layout.resourceSize);
|
||||
|
||||
const zipFs = new ZipFS(blob, { readOnly: true })
|
||||
const zipFs = new ZipFS(blob, { readOnly: true });
|
||||
const snapshotZipFS = new SnapshotZipFS({
|
||||
libzip: getLibzipSync(),
|
||||
zipFs,
|
||||
baseFs: nodeFs,
|
||||
root: dirname(process.argv[0]),
|
||||
})
|
||||
const posixSnapshotZipFs = new PosixFS(snapshotZipFS)
|
||||
patchFs(fs, posixSnapshotZipFs)
|
||||
});
|
||||
const posixSnapshotZipFs = new PosixFS(snapshotZipFS);
|
||||
patchFs(fs, posixSnapshotZipFs);
|
||||
|
||||
let log = (_: string) => true
|
||||
if ((process.env.DEBUG || '').toLowerCase().includes('nexe:require')) {
|
||||
let log = (_: string) => true;
|
||||
if ((process.env.DEBUG || "").toLowerCase().includes("nexe:require")) {
|
||||
process.stderr.write(
|
||||
// @ts-ignore
|
||||
`[nexe] - FILES ${JSON.stringify(Array.from(zipFs.entries.keys()), null, 4)}\n`
|
||||
)
|
||||
`[nexe] - FILES ${JSON.stringify(
|
||||
// @ts-ignore - accessing private property for debugging
|
||||
Array.from(zipFs.entries.keys()),
|
||||
null,
|
||||
4
|
||||
)}\n`
|
||||
);
|
||||
process.stderr.write(
|
||||
// @ts-ignore
|
||||
`[nexe] - DIRECTORIES ${JSON.stringify(Array.from(zipFs.listings.keys()), null, 4)}\n`
|
||||
)
|
||||
`[nexe] - DIRECTORIES ${JSON.stringify(
|
||||
// @ts-ignore - accessing private property for debugging
|
||||
Array.from(zipFs.listings.keys()),
|
||||
null,
|
||||
4
|
||||
)}\n`
|
||||
);
|
||||
log = (text: string) => {
|
||||
return process.stderr.write(`[nexe] - ${text}\n`)
|
||||
}
|
||||
return process.stderr.write(`[nexe] - ${text}\n`);
|
||||
};
|
||||
}
|
||||
function internalModuleReadFile(this: any, original: any, ...args: any[]) {
|
||||
log(`internalModuleReadFile ${args[0]}`)
|
||||
log(`internalModuleReadFile ${args[0]}`);
|
||||
try {
|
||||
return posixSnapshotZipFs.readFileSync(args[0], 'utf-8')
|
||||
return posixSnapshotZipFs.readFileSync(args[0], "utf-8");
|
||||
} catch (e) {
|
||||
return ''
|
||||
return "";
|
||||
}
|
||||
}
|
||||
if (patches.internalModuleReadFile) {
|
||||
patches.internalModuleReadFile = internalModuleReadFile
|
||||
patches.internalModuleReadFile = internalModuleReadFile;
|
||||
}
|
||||
let returningArray: boolean
|
||||
patches.internalModuleReadJSON = function (this: any, original: any, ...args: any[]) {
|
||||
if (returningArray == null) returningArray = Array.isArray(original.call(this, ''))
|
||||
const res = internalModuleReadFile.call(this, original, ...args)
|
||||
let returningArray: boolean;
|
||||
patches.internalModuleReadJSON = function (
|
||||
this: any,
|
||||
original: any,
|
||||
...args: any[]
|
||||
) {
|
||||
if (returningArray == null)
|
||||
returningArray = Array.isArray(original.call(this, ""));
|
||||
const res = internalModuleReadFile.call(this, original, ...args);
|
||||
return returningArray && !Array.isArray(res)
|
||||
? res === ''
|
||||
? res === ""
|
||||
? []
|
||||
: [res, /"(main|name|type|exports|imports)"/.test(res)]
|
||||
: res
|
||||
}
|
||||
patches.internalModuleStat = function (this: any, original: any, ...args: any[]) {
|
||||
let statPath = args[0]
|
||||
: res;
|
||||
};
|
||||
patches.internalModuleStat = function (
|
||||
this: any,
|
||||
original: any,
|
||||
...args: any[]
|
||||
) {
|
||||
let statPath = args[0];
|
||||
//in node 22, the path arg moved to arg[1]
|
||||
if (typeof args[0] !== 'string') statPath = args[1]
|
||||
let result = 0
|
||||
if (typeof args[0] !== "string") statPath = args[1];
|
||||
let result = 0;
|
||||
try {
|
||||
const stat = posixSnapshotZipFs.statSync(statPath)
|
||||
if (stat.isDirectory()) result = 1
|
||||
else result = 0
|
||||
const stat = posixSnapshotZipFs.statSync(statPath);
|
||||
if (stat.isDirectory()) result = 1;
|
||||
else result = 0;
|
||||
} catch (e) {
|
||||
result = -constants.ENOENT
|
||||
result = -constants.ENOENT;
|
||||
}
|
||||
log(`internalModuleStat ${result} ${statPath}`)
|
||||
return result
|
||||
}
|
||||
log(`internalModuleStat ${result} ${statPath}`);
|
||||
return result;
|
||||
};
|
||||
|
||||
lazyRestoreFs = () => {
|
||||
Object.assign(fs, originalFsMethods)
|
||||
Object.assign(patches, originalPatches)
|
||||
lazyRestoreFs = () => {}
|
||||
}
|
||||
Object.assign(fs, originalFsMethods);
|
||||
Object.assign(patches, originalPatches);
|
||||
lazyRestoreFs = () => {};
|
||||
};
|
||||
}
|
||||
|
||||
function restoreFs() {
|
||||
lazyRestoreFs()
|
||||
lazyRestoreFs();
|
||||
}
|
||||
|
||||
export { shimFs, restoreFs }
|
||||
export { shimFs, restoreFs };
|
||||
|
||||
+38
-38
@@ -1,65 +1,65 @@
|
||||
import colors from 'chalk'
|
||||
import ora from 'ora'
|
||||
import colors from "chalk";
|
||||
import ora from "ora";
|
||||
|
||||
const frameLength = 120
|
||||
const frameLength = 120;
|
||||
|
||||
export interface LogStep {
|
||||
modify(text: string, color?: string): void
|
||||
log(text: string, color?: string): void
|
||||
pause(): void
|
||||
resume(): void
|
||||
modify(text: string, color?: string): void;
|
||||
log(text: string, color?: string): void;
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
}
|
||||
|
||||
export class Logger {
|
||||
public verbose: boolean
|
||||
private silent: boolean
|
||||
private ora: any
|
||||
private modify: Function
|
||||
public write: (text: string, color?: string) => void
|
||||
public verbose: boolean;
|
||||
private silent: boolean;
|
||||
private ora: any;
|
||||
private modify: Function;
|
||||
public write: (text: string, color?: string) => void;
|
||||
|
||||
constructor(level: 'verbose' | 'silent' | 'info') {
|
||||
this.verbose = level === 'verbose'
|
||||
this.silent = level === 'silent'
|
||||
constructor(level: "verbose" | "silent" | "info") {
|
||||
this.verbose = level === "verbose";
|
||||
this.silent = level === "silent";
|
||||
if (!this.silent) {
|
||||
this.ora = ora({
|
||||
text: 'Starting...',
|
||||
color: 'blue',
|
||||
spinner: 'dots',
|
||||
})
|
||||
this.ora.stop()
|
||||
text: "Starting...",
|
||||
color: "blue",
|
||||
spinner: "dots",
|
||||
});
|
||||
this.ora.stop();
|
||||
}
|
||||
const noop = () => {}
|
||||
this.modify = this.silent ? noop : this._modify.bind(this)
|
||||
this.write = this.silent ? noop : this._write.bind(this)
|
||||
const noop = () => {};
|
||||
this.modify = this.silent ? noop : this._modify.bind(this);
|
||||
this.write = this.silent ? noop : this._write.bind(this);
|
||||
}
|
||||
|
||||
flush() {
|
||||
!this.silent && this.ora.succeed()
|
||||
return new Promise((resolve) => setTimeout(resolve, frameLength))
|
||||
!this.silent && this.ora.succeed();
|
||||
return new Promise((resolve) => setTimeout(resolve, frameLength));
|
||||
}
|
||||
|
||||
_write(update: string, color = 'green') {
|
||||
this.ora.succeed().text = (colors as any)[color](update)
|
||||
this.ora.start()
|
||||
_write(update: string, color = "green") {
|
||||
this.ora.succeed().text = (colors as any)[color](update);
|
||||
this.ora.start();
|
||||
}
|
||||
|
||||
_modify(update: string, color = this.ora.color) {
|
||||
this.ora.text = update
|
||||
this.ora.color = color
|
||||
this.ora.text = update;
|
||||
this.ora.color = color;
|
||||
}
|
||||
|
||||
step(text: string, method: string = 'succeed'): LogStep {
|
||||
step(text: string, method = "succeed"): LogStep {
|
||||
if (this.silent) {
|
||||
return { modify() {}, log() {}, pause() {}, resume() {} }
|
||||
return { modify() {}, log() {}, pause() {}, resume() {} };
|
||||
}
|
||||
if (!this.ora.id) {
|
||||
this.ora.start().text = text
|
||||
if (method !== 'succeed') {
|
||||
this.ora[method]()
|
||||
this.ora.start().text = text;
|
||||
if (method !== "succeed") {
|
||||
this.ora[method]();
|
||||
}
|
||||
} else {
|
||||
this.ora[method]().text = text
|
||||
this.ora.start()
|
||||
this.ora[method]().text = text;
|
||||
this.ora.start();
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -67,6 +67,6 @@ export class Logger {
|
||||
log: this.verbose ? this.write : this.modify,
|
||||
pause: () => this.ora.stopAndPersist(),
|
||||
resume: () => this.ora.start(),
|
||||
} as LogStep
|
||||
} as LogStep;
|
||||
}
|
||||
}
|
||||
|
||||
+26
-24
@@ -1,15 +1,15 @@
|
||||
import { EOL } from 'os'
|
||||
import { compose } from 'app-builder'
|
||||
import { NexeCompiler, NexeError } from './compiler'
|
||||
import { normalizeOptions, NexeOptions, NexePatch } from './options'
|
||||
import resource from './steps/resource'
|
||||
import clean from './steps/clean'
|
||||
import cli from './steps/cli'
|
||||
import bundle from './steps/bundle'
|
||||
import download from './steps/download'
|
||||
import shim from './steps/shim'
|
||||
import artifacts from './steps/artifacts'
|
||||
import patches from './patches'
|
||||
import { EOL } from "os";
|
||||
import { compose } from "app-builder";
|
||||
import { NexeCompiler, NexeError } from "./compiler";
|
||||
import { normalizeOptions, NexeOptions, NexePatch } from "./options";
|
||||
import resource from "./steps/resource";
|
||||
import clean from "./steps/clean";
|
||||
import cli from "./steps/cli";
|
||||
import bundle from "./steps/bundle";
|
||||
import download from "./steps/download";
|
||||
import shim from "./steps/shim";
|
||||
import artifacts from "./steps/artifacts";
|
||||
import patches from "./patches";
|
||||
|
||||
async function compile(
|
||||
compilerOptions?: Partial<NexeOptions>,
|
||||
@@ -17,11 +17,11 @@ async function compile(
|
||||
) {
|
||||
let error: Error | null = null,
|
||||
options: NexeOptions | null = null,
|
||||
compiler: NexeCompiler | null = null
|
||||
compiler: NexeCompiler | null = null;
|
||||
|
||||
try {
|
||||
options = normalizeOptions(compilerOptions)
|
||||
compiler = new NexeCompiler(options)
|
||||
options = normalizeOptions(compilerOptions);
|
||||
compiler = new NexeCompiler(options);
|
||||
await compose(
|
||||
clean,
|
||||
resource,
|
||||
@@ -29,21 +29,23 @@ async function compile(
|
||||
bundle,
|
||||
shim,
|
||||
download,
|
||||
options.build ? [artifacts, ...patches, ...(options.patches as NexePatch[])] : [],
|
||||
options.build
|
||||
? [artifacts, ...patches, ...(options.patches as NexePatch[])]
|
||||
: [],
|
||||
options.plugins as NexePatch[]
|
||||
)(compiler)
|
||||
)(compiler);
|
||||
} catch (e: any) {
|
||||
error = e
|
||||
error = e;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
compiler && compiler.quit(error)
|
||||
if (callback) return callback(error)
|
||||
return Promise.reject(error)
|
||||
compiler && compiler.quit(error);
|
||||
if (callback) return callback(error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (callback) callback(null)
|
||||
if (callback) callback(null);
|
||||
}
|
||||
|
||||
export { compile, NexeCompiler }
|
||||
export { argv, version, NexeOptions, help } from './options'
|
||||
export { compile, NexeCompiler };
|
||||
export { argv, version, NexeOptions, help } from "./options";
|
||||
|
||||
+165
-156
@@ -1,61 +1,62 @@
|
||||
import parseArgv from 'minimist'
|
||||
import { NexeCompiler, NexeError } from './compiler'
|
||||
import { isWindows, STDIN_FLAG } from './util'
|
||||
import { basename, extname, join, isAbsolute, resolve } from 'path'
|
||||
import { getTarget, NexeTarget } from './target'
|
||||
import { EOL, homedir } from 'os'
|
||||
import chalk from 'chalk'
|
||||
import { resolveSync } from 'resolve-dependencies'
|
||||
const caw = require('caw')
|
||||
const c = process.platform === 'win32' ? chalk.constructor({ enabled: false }) : chalk
|
||||
import parseArgv from "minimist";
|
||||
import { NexeCompiler, NexeError } from "./compiler";
|
||||
import { isWindows, STDIN_FLAG } from "./util";
|
||||
import { basename, extname, join, isAbsolute, resolve } from "path";
|
||||
import { getTarget, NexeTarget } from "./target";
|
||||
import { EOL, homedir } from "os";
|
||||
import chalk from "chalk";
|
||||
import { resolveSync } from "resolve-dependencies";
|
||||
const caw = require("caw");
|
||||
const c =
|
||||
process.platform === "win32" ? chalk.constructor({ enabled: false }) : chalk;
|
||||
|
||||
export const version = '{{ version }}'
|
||||
export const version = "{{ version }}";
|
||||
|
||||
export interface NexePatch {
|
||||
(compiler: NexeCompiler, next: () => Promise<void>): Promise<void>
|
||||
(compiler: NexeCompiler, next: () => Promise<void>): Promise<void>;
|
||||
}
|
||||
|
||||
export interface NexeOptions {
|
||||
build: boolean
|
||||
input: string
|
||||
output: string
|
||||
targets: (string | NexeTarget)[]
|
||||
name: string
|
||||
remote: string
|
||||
asset: string
|
||||
cwd: string
|
||||
fs: boolean | string[]
|
||||
flags: string[]
|
||||
configure: string[]
|
||||
vcBuild: string[]
|
||||
make: string[]
|
||||
snapshot?: string
|
||||
resources: string[]
|
||||
temp: string
|
||||
rc: { [key: string]: string }
|
||||
enableNodeCli: boolean
|
||||
bundle: boolean | string
|
||||
patches: (string | NexePatch)[]
|
||||
plugins: (string | NexePatch)[]
|
||||
native: any
|
||||
mangle: boolean
|
||||
ghToken: string
|
||||
sourceUrl?: string
|
||||
enableStdIn?: boolean
|
||||
python?: string
|
||||
loglevel: 'info' | 'silent' | 'verbose'
|
||||
silent?: boolean
|
||||
fakeArgv?: boolean
|
||||
verbose?: boolean
|
||||
info?: boolean
|
||||
ico?: string
|
||||
debugBundle?: boolean
|
||||
warmup?: string
|
||||
clean?: boolean
|
||||
build: boolean;
|
||||
input: string;
|
||||
output: string;
|
||||
targets: (string | NexeTarget)[];
|
||||
name: string;
|
||||
remote: string;
|
||||
asset: string;
|
||||
cwd: string;
|
||||
fs: boolean | string[];
|
||||
flags: string[];
|
||||
configure: string[];
|
||||
vcBuild: string[];
|
||||
make: string[];
|
||||
snapshot?: string;
|
||||
resources: string[];
|
||||
temp: string;
|
||||
rc: { [key: string]: string };
|
||||
enableNodeCli: boolean;
|
||||
bundle: boolean | string;
|
||||
patches: (string | NexePatch)[];
|
||||
plugins: (string | NexePatch)[];
|
||||
native: any;
|
||||
mangle: boolean;
|
||||
ghToken: string;
|
||||
sourceUrl?: string;
|
||||
enableStdIn?: boolean;
|
||||
python?: string;
|
||||
loglevel: "info" | "silent" | "verbose";
|
||||
silent?: boolean;
|
||||
fakeArgv?: boolean;
|
||||
verbose?: boolean;
|
||||
info?: boolean;
|
||||
ico?: string;
|
||||
debugBundle?: boolean;
|
||||
warmup?: string;
|
||||
clean?: boolean;
|
||||
/**
|
||||
* Api Only
|
||||
*/
|
||||
downloadOptions: any
|
||||
downloadOptions: any;
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
@@ -66,37 +67,37 @@ const defaults = {
|
||||
mangle: true,
|
||||
make: [],
|
||||
targets: [],
|
||||
vcBuild: isWindows ? ['nosign', 'release'] : [],
|
||||
vcBuild: isWindows ? ["nosign", "release"] : [],
|
||||
enableNodeCli: false,
|
||||
build: false,
|
||||
bundle: true,
|
||||
patches: [],
|
||||
plugins: [],
|
||||
remote: 'https://github.com/nexe/nexe/releases/download/v3.3.3/',
|
||||
}
|
||||
remote: "https://github.com/nexe/nexe/releases/download/v3.3.3/",
|
||||
};
|
||||
const alias = {
|
||||
i: 'input',
|
||||
o: 'output',
|
||||
v: 'version',
|
||||
a: 'asset',
|
||||
t: 'target',
|
||||
b: 'build',
|
||||
n: 'name',
|
||||
r: 'resource',
|
||||
p: 'python',
|
||||
f: 'flag',
|
||||
c: 'configure',
|
||||
m: 'make',
|
||||
h: 'help',
|
||||
l: 'loglevel',
|
||||
'fake-argv': 'fakeArgv',
|
||||
'gh-token': 'ghToken',
|
||||
}
|
||||
const argv = parseArgv(process.argv, { alias, default: { ...defaults } })
|
||||
i: "input",
|
||||
o: "output",
|
||||
v: "version",
|
||||
a: "asset",
|
||||
t: "target",
|
||||
b: "build",
|
||||
n: "name",
|
||||
r: "resource",
|
||||
p: "python",
|
||||
f: "flag",
|
||||
c: "configure",
|
||||
m: "make",
|
||||
h: "help",
|
||||
l: "loglevel",
|
||||
"fake-argv": "fakeArgv",
|
||||
"gh-token": "ghToken",
|
||||
};
|
||||
const argv = parseArgv(process.argv, { alias, default: { ...defaults } });
|
||||
let help = `
|
||||
${c.bold('nexe <entry-file> [options]')}
|
||||
${c.bold("nexe <entry-file> [options]")}
|
||||
|
||||
${c.underline.bold('Options:')}
|
||||
${c.underline.bold("Options:")}
|
||||
|
||||
-i --input -- application entry point
|
||||
-o --output -- path to output file
|
||||
@@ -106,7 +107,7 @@ ${c.bold('nexe <entry-file> [options]')}
|
||||
--remote -- alternate location (URL) to download pre-built base (nexe) binaries from
|
||||
--plugin -- extend nexe runtime behavior
|
||||
|
||||
${c.underline.bold('Building from source:')}
|
||||
${c.underline.bold("Building from source:")}
|
||||
|
||||
-b --build -- build from source
|
||||
-p --python -- python3 (as python) executable path
|
||||
@@ -121,7 +122,7 @@ ${c.bold('nexe <entry-file> [options]')}
|
||||
--sourceUrl -- pass an alternate source (node.tar.gz) url
|
||||
--enableNodeCli -- enable node cli enforcement (blocks app cli)
|
||||
|
||||
${c.underline.bold('Other options:')}
|
||||
${c.underline.bold("Other options:")}
|
||||
|
||||
--bundle -- custom bundling module with 'createBundle' export
|
||||
--temp -- temp file storage default '~/.nexe'
|
||||
@@ -131,11 +132,11 @@ ${c.bold('nexe <entry-file> [options]')}
|
||||
--silent -- disable logging
|
||||
--verbose -- set logging to verbose
|
||||
|
||||
-* variable key name * option can be used more than once`.trim()
|
||||
help = EOL + help + EOL
|
||||
-* variable key name * option can be used more than once`.trim();
|
||||
help = EOL + help + EOL;
|
||||
|
||||
function flatten(...args: any[]): string[] {
|
||||
return ([] as string[]).concat(...args).filter((x) => x)
|
||||
return ([] as string[]).concat(...args).filter((x) => x);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,48 +149,48 @@ function extractCliMap(match: RegExp, options: any) {
|
||||
return Object.keys(options)
|
||||
.filter((x) => match.test(x))
|
||||
.reduce((map: any, option: any) => {
|
||||
const key = option.split('-')[1]
|
||||
map[key] = options[option]
|
||||
delete options[option]
|
||||
return map
|
||||
}, {} as any)
|
||||
const key = option.split("-")[1];
|
||||
map[key] = options[option];
|
||||
delete options[option];
|
||||
return map;
|
||||
}, {} as any);
|
||||
}
|
||||
|
||||
function extractLogLevel(options: NexeOptions) {
|
||||
if (options.loglevel) return options.loglevel
|
||||
if (options.silent) return 'silent'
|
||||
if (options.verbose) return 'verbose'
|
||||
return 'info'
|
||||
if (options.loglevel) return options.loglevel;
|
||||
if (options.silent) return "silent";
|
||||
if (options.verbose) return "verbose";
|
||||
return "info";
|
||||
}
|
||||
|
||||
function isName(name: string) {
|
||||
return name && name !== 'index' && name !== STDIN_FLAG
|
||||
return name && name !== "index" && name !== STDIN_FLAG;
|
||||
}
|
||||
|
||||
function extractName(options: NexeOptions) {
|
||||
let name = options.name
|
||||
let name = options.name;
|
||||
//try and use the input filename as the output filename if its not index
|
||||
if (!isName(name) && typeof options.input === 'string') {
|
||||
name = basename(options.input).replace(extname(options.input), '')
|
||||
if (!isName(name) && typeof options.input === "string") {
|
||||
name = basename(options.input).replace(extname(options.input), "");
|
||||
}
|
||||
//try and use the directory as the filename
|
||||
if (!isName(name) && basename(options.cwd)) {
|
||||
name = basename(options.cwd)
|
||||
name = basename(options.cwd);
|
||||
}
|
||||
|
||||
return name.replace(/\.exe$/, '')
|
||||
return name.replace(/\.exe$/, "");
|
||||
}
|
||||
|
||||
function padRelative(input: string) {
|
||||
let prefix = ''
|
||||
if (!input.startsWith('.')) {
|
||||
prefix = './'
|
||||
let prefix = "";
|
||||
if (!input.startsWith(".")) {
|
||||
prefix = "./";
|
||||
}
|
||||
return prefix + input
|
||||
return prefix + input;
|
||||
}
|
||||
|
||||
function isEntryFile(filename?: string): filename is string {
|
||||
return Boolean(filename && !isAbsolute(filename))
|
||||
return Boolean(filename && !isAbsolute(filename));
|
||||
}
|
||||
|
||||
export function resolveEntry(
|
||||
@@ -198,109 +199,117 @@ export function resolveEntry(
|
||||
maybeEntry: string | undefined,
|
||||
bundle: boolean | string
|
||||
) {
|
||||
let result = null
|
||||
if (input === '-' || maybeEntry === '-') {
|
||||
return STDIN_FLAG
|
||||
let result = null;
|
||||
if (input === "-" || maybeEntry === "-") {
|
||||
return STDIN_FLAG;
|
||||
}
|
||||
if (input && isAbsolute(input)) {
|
||||
return input
|
||||
return input;
|
||||
}
|
||||
if (input) {
|
||||
const inputPath = padRelative(input)
|
||||
result = resolveSync(cwd, inputPath)
|
||||
const inputPath = padRelative(input);
|
||||
result = resolveSync(cwd, inputPath);
|
||||
}
|
||||
if (isEntryFile(maybeEntry) && (!result || !result.absPath)) {
|
||||
const inputPath = padRelative(maybeEntry)
|
||||
result = resolveSync(cwd, inputPath)
|
||||
const inputPath = padRelative(maybeEntry);
|
||||
result = resolveSync(cwd, inputPath);
|
||||
}
|
||||
if (!process.stdin.isTTY && (!result || !result.absPath) && bundle === defaults.bundle) {
|
||||
return STDIN_FLAG
|
||||
if (
|
||||
!process.stdin.isTTY &&
|
||||
(!result || !result.absPath) &&
|
||||
bundle === defaults.bundle
|
||||
) {
|
||||
return STDIN_FLAG;
|
||||
}
|
||||
if (!result || !result.absPath) {
|
||||
result = resolveSync(cwd, '.')
|
||||
result = resolveSync(cwd, ".");
|
||||
}
|
||||
if (!result.absPath) {
|
||||
throw new NexeError(`Entry file "${input || ''}" not found!`)
|
||||
throw new NexeError(`Entry file "${input || ""}" not found!`);
|
||||
}
|
||||
return result.absPath
|
||||
return result.absPath;
|
||||
}
|
||||
|
||||
function isCli(options?: Partial<NexeOptions>) {
|
||||
return argv === options
|
||||
return argv === options;
|
||||
}
|
||||
|
||||
function normalizeOptions(input?: Partial<NexeOptions>): NexeOptions {
|
||||
const options = Object.assign({}, defaults, input) as NexeOptions
|
||||
const opts = options as any
|
||||
const cwd = (options.cwd = resolve(options.cwd))
|
||||
const options = Object.assign({}, defaults, input) as NexeOptions;
|
||||
const opts = options as any;
|
||||
const cwd = (options.cwd = resolve(options.cwd));
|
||||
options.temp = options.temp
|
||||
? resolve(cwd, options.temp)
|
||||
: process.env.NEXE_TEMP || join(homedir(), '.nexe')
|
||||
const maybeEntry = isCli(input) ? argv._[argv._.length - 1] : undefined
|
||||
options.input = resolveEntry(options.input, cwd, maybeEntry, options.bundle)
|
||||
options.enableStdIn = isCli(input) && options.input === STDIN_FLAG
|
||||
options.name = extractName(options)
|
||||
options.loglevel = extractLogLevel(options)
|
||||
options.flags = flatten(opts.flag, options.flags)
|
||||
options.targets = flatten(opts.target, options.targets).map(getTarget)
|
||||
: process.env.NEXE_TEMP || join(homedir(), ".nexe");
|
||||
const maybeEntry = isCli(input) ? argv._[argv._.length - 1] : undefined;
|
||||
options.input = resolveEntry(options.input, cwd, maybeEntry, options.bundle);
|
||||
options.enableStdIn = isCli(input) && options.input === STDIN_FLAG;
|
||||
options.name = extractName(options);
|
||||
options.loglevel = extractLogLevel(options);
|
||||
options.flags = flatten(opts.flag, options.flags);
|
||||
options.targets = flatten(opts.target, options.targets).map(getTarget);
|
||||
if (!options.targets.length) {
|
||||
options.targets.push(getTarget())
|
||||
options.targets.push(getTarget());
|
||||
}
|
||||
options.ghToken = options.ghToken || process.env.GITHUB_TOKEN || ''
|
||||
options.make = flatten(isWindows ? options.vcBuild : options.make)
|
||||
options.configure = flatten(options.configure)
|
||||
options.resources = flatten(opts.resource, options.resources)
|
||||
options.ghToken = options.ghToken || process.env.GITHUB_TOKEN || "";
|
||||
options.make = flatten(isWindows ? options.vcBuild : options.make);
|
||||
options.configure = flatten(options.configure);
|
||||
options.resources = flatten(opts.resource, options.resources);
|
||||
|
||||
if (!options.remote.endsWith('/')) {
|
||||
options.remote += '/'
|
||||
if (!options.remote.endsWith("/")) {
|
||||
options.remote += "/";
|
||||
}
|
||||
|
||||
options.downloadOptions = options.downloadOptions || {}
|
||||
options.downloadOptions.headers = options.downloadOptions.headers || {}
|
||||
options.downloadOptions.headers['User-Agent'] = 'nexe (https://www.npmjs.com/package/nexe)'
|
||||
options.downloadOptions = options.downloadOptions || {};
|
||||
options.downloadOptions.headers = options.downloadOptions.headers || {};
|
||||
options.downloadOptions.headers["User-Agent"] =
|
||||
"nexe (https://www.npmjs.com/package/nexe)";
|
||||
options.downloadOptions.agent = process.env.HTTPS_PROXY
|
||||
? caw(process.env.HTTPS_PROXY, { protocol: 'https' })
|
||||
: options.downloadOptions.agent || require('https').globalAgent
|
||||
options.downloadOptions.rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED
|
||||
? caw(process.env.HTTPS_PROXY, { protocol: "https" })
|
||||
: options.downloadOptions.agent || require("https").globalAgent;
|
||||
options.downloadOptions.rejectUnauthorized = process.env
|
||||
.NODE_TLS_REJECT_UNAUTHORIZED
|
||||
? false
|
||||
: true
|
||||
: true;
|
||||
|
||||
options.rc = options.rc || extractCliMap(/^rc-.*/, options)
|
||||
options.rc = options.rc || extractCliMap(/^rc-.*/, options);
|
||||
options.output =
|
||||
(options.targets[0] as NexeTarget).platform === 'windows'
|
||||
? `${(options.output || options.name).replace(/\.exe$/, '')}.exe`
|
||||
: `${options.output || options.name}`
|
||||
options.output = resolve(cwd, options.output)
|
||||
(options.targets[0] as NexeTarget).platform === "windows"
|
||||
? `${(options.output || options.name).replace(/\.exe$/, "")}.exe`
|
||||
: `${options.output || options.name}`;
|
||||
options.output = resolve(cwd, options.output);
|
||||
|
||||
const requireDefault = (x: string) => {
|
||||
if (typeof x === 'string') {
|
||||
return require(x).default
|
||||
if (typeof x === "string") {
|
||||
return require(x).default;
|
||||
}
|
||||
return x
|
||||
}
|
||||
return x;
|
||||
};
|
||||
|
||||
options.mangle = 'mangle' in opts ? opts.mangle : true
|
||||
options.plugins = flatten(opts.plugin, options.plugins).map(requireDefault)
|
||||
options.patches = flatten(opts.patch, options.patches).map(requireDefault)
|
||||
options.mangle = "mangle" in opts ? opts.mangle : true;
|
||||
options.plugins = flatten(opts.plugin, options.plugins).map(requireDefault);
|
||||
options.patches = flatten(opts.patch, options.patches).map(requireDefault);
|
||||
|
||||
if ((!options.mangle && !options.bundle) || options.patches.length) {
|
||||
options.build = true
|
||||
options.build = true;
|
||||
}
|
||||
|
||||
if (options.build) {
|
||||
const { arch } = options.targets[0] as NexeTarget
|
||||
const { arch } = options.targets[0] as NexeTarget;
|
||||
if (isWindows) {
|
||||
options.make = Array.from(new Set(options.make.concat(arch)))
|
||||
options.make = Array.from(new Set(options.make.concat(arch)));
|
||||
} else {
|
||||
options.configure = Array.from(new Set(options.configure.concat([`--dest-cpu=${arch}`])))
|
||||
options.configure = Array.from(
|
||||
new Set(options.configure.concat([`--dest-cpu=${arch}`]))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(alias)
|
||||
.filter((k) => k !== 'rc')
|
||||
.forEach((x) => delete opts[x])
|
||||
.filter((k) => k !== "rc")
|
||||
.forEach((x) => delete opts[x]);
|
||||
|
||||
return options
|
||||
return options;
|
||||
}
|
||||
|
||||
export { argv, normalizeOptions, help }
|
||||
export { argv, normalizeOptions, help };
|
||||
|
||||
+33
-29
@@ -1,56 +1,57 @@
|
||||
const fs = require('fs'),
|
||||
fd = fs.openSync(process.execPath, 'r'),
|
||||
const fs = require("fs"),
|
||||
fd = fs.openSync(process.execPath, "r"),
|
||||
stat = fs.statSync(fd),
|
||||
tailSize = Math.min(stat.size, 16000),
|
||||
tailWindow = Buffer.alloc(tailSize),
|
||||
match = '<nexe' + '~~sentinel>',
|
||||
match = "<nexe" + "~~sentinel>",
|
||||
matchLength = match.length,
|
||||
lastBuffer = Buffer.alloc(matchLength + 32)
|
||||
lastBuffer = Buffer.alloc(matchLength + 32);
|
||||
|
||||
let offset = stat.size,
|
||||
footerPosition = -1,
|
||||
footerPositionOffset = 0,
|
||||
footer: Buffer
|
||||
footer: Buffer;
|
||||
|
||||
while (true) {
|
||||
const bytesRead = fs.readSync(fd, tailWindow, 0, tailSize, offset - tailSize)
|
||||
if (bytesRead === 0) break
|
||||
const bytesRead = fs.readSync(fd, tailWindow, 0, tailSize, offset - tailSize);
|
||||
if (bytesRead === 0) break;
|
||||
|
||||
const combinedBuffers = Buffer.concat([tailWindow, lastBuffer])
|
||||
footerPosition = combinedBuffers.indexOf(match)
|
||||
const combinedBuffers = Buffer.concat([tailWindow, lastBuffer]);
|
||||
footerPosition = combinedBuffers.indexOf(match);
|
||||
|
||||
if (footerPosition > -1) {
|
||||
footer = combinedBuffers.slice(footerPosition, footerPosition + 32)
|
||||
break
|
||||
footer = combinedBuffers.slice(footerPosition, footerPosition + 32);
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset < 0) break
|
||||
if (offset < 0) break;
|
||||
|
||||
tailWindow.copy(lastBuffer)
|
||||
offset = offset - bytesRead
|
||||
tailWindow.copy(lastBuffer);
|
||||
offset = offset - bytesRead;
|
||||
}
|
||||
|
||||
if (footerPosition == -1) {
|
||||
throw 'Invalid Nexe binary'
|
||||
throw "Invalid Nexe binary";
|
||||
}
|
||||
|
||||
const contentSize = footer!.readDoubleLE(16),
|
||||
resourceSize = footer!.readDoubleLE(24),
|
||||
contentStart = offset - tailSize + footerPosition - resourceSize - contentSize,
|
||||
resourceStart = contentStart + contentSize
|
||||
contentStart =
|
||||
offset - tailSize + footerPosition - resourceSize - contentSize,
|
||||
resourceStart = contentStart + contentSize;
|
||||
|
||||
Object.defineProperty(
|
||||
process,
|
||||
'__nexe',
|
||||
"__nexe",
|
||||
(function () {
|
||||
let nexeHeader: any = null
|
||||
let nexeHeader: any = null;
|
||||
return {
|
||||
get: function () {
|
||||
return nexeHeader
|
||||
return nexeHeader;
|
||||
},
|
||||
set: function (value: any) {
|
||||
if (nexeHeader) {
|
||||
throw new Error('This property is readonly')
|
||||
throw new Error("This property is readonly");
|
||||
}
|
||||
nexeHeader = Object.assign({}, value, {
|
||||
blobPath: process.execPath,
|
||||
@@ -61,19 +62,22 @@ Object.defineProperty(
|
||||
resourceSize,
|
||||
resourceStart,
|
||||
},
|
||||
})
|
||||
Object.freeze(nexeHeader)
|
||||
});
|
||||
Object.freeze(nexeHeader);
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
}
|
||||
};
|
||||
})()
|
||||
)
|
||||
);
|
||||
|
||||
const contentBuffer = Buffer.alloc(contentSize),
|
||||
Module = require('module')
|
||||
Module = require("module");
|
||||
|
||||
fs.readSync(fd, contentBuffer, 0, contentSize, contentStart)
|
||||
fs.closeSync(fd)
|
||||
fs.readSync(fd, contentBuffer, 0, contentSize, contentStart);
|
||||
fs.closeSync(fd);
|
||||
|
||||
new Module(process.execPath, null)._compile(contentBuffer.slice(1).toString(), process.execPath)
|
||||
new Module(process.execPath, null)._compile(
|
||||
contentBuffer.slice(1).toString(),
|
||||
process.execPath
|
||||
);
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { NexeCompiler } from "../compiler";
|
||||
|
||||
export default async function buildFixes(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
if (!compiler.target.version.startsWith('8.2')) {
|
||||
return next()
|
||||
export default async function buildFixes(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
if (!compiler.target.version.startsWith("8.2")) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const file = await compiler.readFileAsync('./tools/msvs/find_python.cmd')
|
||||
const file = await compiler.readFileAsync("./tools/msvs/find_python.cmd");
|
||||
|
||||
await compiler.replaceInFileAsync(
|
||||
'./tools/msvs/find_python.cmd',
|
||||
'%p%python.exe -V 2>&1',
|
||||
"./tools/msvs/find_python.cmd",
|
||||
"%p%python.exe -V 2>&1",
|
||||
'"%p%python.exe" -V 2>&1'
|
||||
)
|
||||
);
|
||||
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -1,50 +1,61 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { semverGt } from '../util'
|
||||
import { NexeCompiler } from "../compiler";
|
||||
import { semverGt } from "../util";
|
||||
|
||||
export default async function disableNodeCli(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
export default async function disableNodeCli(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
if (compiler.options.enableNodeCli) {
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
if (semverGt(compiler.target.version, '18.99')) {
|
||||
if (semverGt(compiler.target.version, "18.99")) {
|
||||
await compiler.replaceInFileAsync(
|
||||
'src/node.cc',
|
||||
"src/node.cc",
|
||||
/(?<!static ExitCode )ProcessGlobalArgsInternal\(argv[^;]*;/gm,
|
||||
'ExitCode::kNoFailure;/*$&*/'
|
||||
)
|
||||
} else if (semverGt(compiler.target.version, '11.6.0')) {
|
||||
"ExitCode::kNoFailure;/*$&*/"
|
||||
);
|
||||
} else if (semverGt(compiler.target.version, "11.6.0")) {
|
||||
await compiler.replaceInFileAsync(
|
||||
'src/node.cc',
|
||||
"src/node.cc",
|
||||
/(?<!int )ProcessGlobalArgs\(argv[^;]*;/gm,
|
||||
'0;/*$&*/'
|
||||
)
|
||||
} else if (semverGt(compiler.target.version, '10.9')) {
|
||||
await compiler.replaceInFileAsync('src/node.cc', /(?<!void )ProcessArgv\(argv/g, '//$&')
|
||||
} else if (semverGt(compiler.target.version, '9.999')) {
|
||||
"0;/*$&*/"
|
||||
);
|
||||
} else if (semverGt(compiler.target.version, "10.9")) {
|
||||
await compiler.replaceInFileAsync(
|
||||
'src/node.cc',
|
||||
'int i = 1; i < v8_argc; i++',
|
||||
'int i = v8_argc; i < v8_argc; i++'
|
||||
)
|
||||
let matches = 0
|
||||
await compiler.replaceInFileAsync('src/node.cc', /v8_argc > 1/g, (match) => {
|
||||
if (matches++) {
|
||||
return 'false'
|
||||
"src/node.cc",
|
||||
/(?<!void )ProcessArgv\(argv/g,
|
||||
"//$&"
|
||||
);
|
||||
} else if (semverGt(compiler.target.version, "9.999")) {
|
||||
await compiler.replaceInFileAsync(
|
||||
"src/node.cc",
|
||||
"int i = 1; i < v8_argc; i++",
|
||||
"int i = v8_argc; i < v8_argc; i++"
|
||||
);
|
||||
let matches = 0;
|
||||
await compiler.replaceInFileAsync(
|
||||
"src/node.cc",
|
||||
/v8_argc > 1/g,
|
||||
(match) => {
|
||||
if (matches++) {
|
||||
return "false";
|
||||
}
|
||||
return match;
|
||||
}
|
||||
return match
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const nodeccMarker = 'argv[index][0] =='
|
||||
const nodeccMarker = "argv[index][0] ==";
|
||||
|
||||
await compiler.replaceInFileAsync(
|
||||
'src/node.cc',
|
||||
"src/node.cc",
|
||||
`${nodeccMarker} '-'`,
|
||||
// allow NODE_OPTIONS, introduced in 8.0
|
||||
semverGt(compiler.target.version, '7.99')
|
||||
semverGt(compiler.target.version, "7.99")
|
||||
? `(${nodeccMarker} (is_env ? '-' : ']'))`
|
||||
: `(${nodeccMarker} ']')`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
+11
-8
@@ -1,16 +1,19 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { NexeCompiler } from "../compiler";
|
||||
|
||||
export default async function flags(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
const nodeflags = compiler.options.flags
|
||||
export default async function flags(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
const nodeflags = compiler.options.flags;
|
||||
if (!nodeflags.length) {
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
await compiler.replaceInFileAsync(
|
||||
'node.gyp',
|
||||
"node.gyp",
|
||||
"'node_v8_options%': ''",
|
||||
`'node_v8_options%': '${nodeflags.join(' ')}'`
|
||||
)
|
||||
`'node_v8_options%': '${nodeflags.join(" ")}'`
|
||||
);
|
||||
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,21 +1,21 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { NexeCompiler } from "../compiler";
|
||||
|
||||
export default async function nodeGyp(
|
||||
{ files, replaceInFileAsync }: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
await next()
|
||||
await next();
|
||||
|
||||
const nodeGypMarker = "'lib/fs.js',"
|
||||
const nodeGypMarker = "'lib/fs.js',";
|
||||
await replaceInFileAsync(
|
||||
'node.gyp',
|
||||
"node.gyp",
|
||||
nodeGypMarker,
|
||||
`
|
||||
${nodeGypMarker}
|
||||
${files
|
||||
.filter((x) => x.filename.startsWith('lib'))
|
||||
.filter((x) => x.filename.startsWith("lib"))
|
||||
.map((x) => `'${x.filename}'`)
|
||||
.toString()},
|
||||
`.trim()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
+14
-8
@@ -1,12 +1,18 @@
|
||||
import { normalize } from 'path'
|
||||
import { readFileAsync } from '../util'
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { normalize } from "path";
|
||||
import { readFileAsync } from "../util";
|
||||
import { NexeCompiler } from "../compiler";
|
||||
|
||||
export default async function ico(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
const iconFile = compiler.options.ico
|
||||
export default async function ico(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
const iconFile = compiler.options.ico;
|
||||
if (!iconFile) {
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
await compiler.setFileContentsAsync('src/res/node.ico', await readFileAsync(normalize(iconFile)))
|
||||
return next()
|
||||
await compiler.setFileContentsAsync(
|
||||
"src/res/node.ico",
|
||||
await readFileAsync(normalize(iconFile))
|
||||
);
|
||||
return next();
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,12 +1,12 @@
|
||||
import gyp from './gyp'
|
||||
import bootNexe from './third-party-main'
|
||||
import buildFixes from './build-fixes'
|
||||
import cli from './disable-node-cli'
|
||||
import flags from './flags'
|
||||
import ico from './ico'
|
||||
import rc from './node-rc'
|
||||
import snapshot from './snapshot'
|
||||
import gyp from "./gyp";
|
||||
import bootNexe from "./third-party-main";
|
||||
import buildFixes from "./build-fixes";
|
||||
import cli from "./disable-node-cli";
|
||||
import flags from "./flags";
|
||||
import ico from "./ico";
|
||||
import rc from "./node-rc";
|
||||
import snapshot from "./snapshot";
|
||||
|
||||
const patches = [gyp, bootNexe, buildFixes, cli, flags, ico, rc, snapshot]
|
||||
const patches = [gyp, bootNexe, buildFixes, cli, flags, ico, rc, snapshot];
|
||||
|
||||
export default patches
|
||||
export default patches;
|
||||
|
||||
+17
-14
@@ -1,28 +1,31 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { NexeCompiler } from "../compiler";
|
||||
|
||||
export default async function nodeRc(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
const options = compiler.options.rc
|
||||
export default async function nodeRc(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
const options = compiler.options.rc;
|
||||
if (!options) {
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
const file = await compiler.readFileAsync('src/res/node.rc')
|
||||
const file = await compiler.readFileAsync("src/res/node.rc");
|
||||
|
||||
Object.keys(options).forEach((key) => {
|
||||
let value = options[key]
|
||||
const isVar = /^[A-Z_]+$/.test(value)
|
||||
value = isVar ? value : `"${value}"`
|
||||
let value = options[key];
|
||||
const isVar = /^[A-Z_]+$/.test(value);
|
||||
value = isVar ? value : `"${value}"`;
|
||||
file.contents = file.contents
|
||||
.toString()
|
||||
.replace(new RegExp(`VALUE "${key}",.*`), `VALUE "${key}", ${value}`)
|
||||
})
|
||||
;['PRODUCTVERSION', 'FILEVERSION'].forEach((x) => {
|
||||
.replace(new RegExp(`VALUE "${key}",.*`), `VALUE "${key}", ${value}`);
|
||||
});
|
||||
["PRODUCTVERSION", "FILEVERSION"].forEach((x) => {
|
||||
if (options[x]) {
|
||||
file.contents = file.contents
|
||||
.toString()
|
||||
.replace(new RegExp(x + ' .*$', 'm'), `${x} ${options[x]}`)
|
||||
.replace(new RegExp(x + " .*$", "m"), `${x} ${options[x]}`);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
+15
-10
@@ -1,19 +1,24 @@
|
||||
import { resolve } from 'path'
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { semverGt } from '../util'
|
||||
import { resolve } from "path";
|
||||
import { NexeCompiler } from "../compiler";
|
||||
import { semverGt } from "../util";
|
||||
|
||||
export default async function (compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
const { snapshot, warmup, cwd } = compiler.options
|
||||
export default async function (
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
const { snapshot, warmup, cwd } = compiler.options;
|
||||
|
||||
if (!snapshot) {
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
const variablePrefix = semverGt(compiler.target.version, '11.0.0') ? 'v8_' : ''
|
||||
const variablePrefix = semverGt(compiler.target.version, "11.0.0")
|
||||
? "v8_"
|
||||
: "";
|
||||
|
||||
await compiler.replaceInFileAsync(
|
||||
compiler.configureScript,
|
||||
'def configure_v8(o):',
|
||||
"def configure_v8(o):",
|
||||
`def configure_v8(o):\n o['variables']['${variablePrefix}embed_script'] = r'${resolve(
|
||||
cwd,
|
||||
snapshot
|
||||
@@ -21,7 +26,7 @@ export default async function (compiler: NexeCompiler, next: () => Promise<void>
|
||||
cwd,
|
||||
warmup || snapshot
|
||||
)}'`
|
||||
)
|
||||
);
|
||||
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -1,40 +1,43 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { parse } from 'meriyah'
|
||||
import { wrap, semverGt } from '../util'
|
||||
import { NexeCompiler } from "../compiler";
|
||||
import { parse } from "meriyah";
|
||||
import { wrap, semverGt } from "../util";
|
||||
|
||||
function walkSome(node: any, visit: Function) {
|
||||
if (!node || typeof node.type !== 'string' || node._visited) {
|
||||
return false
|
||||
if (!node || typeof node.type !== "string" || node._visited) {
|
||||
return false;
|
||||
}
|
||||
visit(node)
|
||||
node._visited = true
|
||||
for (let childNode in node) {
|
||||
const child = node[childNode]
|
||||
visit(node);
|
||||
node._visited = true;
|
||||
for (const childNode in node) {
|
||||
const child = node[childNode];
|
||||
if (Array.isArray(child)) {
|
||||
for (let i = 0; i < child.length; i++) {
|
||||
if (walkSome(child[i], visit)) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (walkSome(child, visit)) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
export default async function main(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
let bootFile = 'lib/internal/bootstrap_node.js'
|
||||
const { version } = compiler.target
|
||||
export default async function main(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
let bootFile = "lib/internal/bootstrap_node.js";
|
||||
const { version } = compiler.target;
|
||||
|
||||
if (version.startsWith('4.')) {
|
||||
bootFile = 'src/node.js'
|
||||
} else if (semverGt(version, '18.11.99')) {
|
||||
bootFile = 'lib/internal/process/pre_execution.js'
|
||||
} else if (semverGt(version, '11.99')) {
|
||||
bootFile = 'lib/internal/bootstrap/pre_execution.js'
|
||||
} else if (semverGt(version, '9.10.1')) {
|
||||
bootFile = 'lib/internal/bootstrap/node.js'
|
||||
if (version.startsWith("4.")) {
|
||||
bootFile = "src/node.js";
|
||||
} else if (semverGt(version, "18.11.99")) {
|
||||
bootFile = "lib/internal/process/pre_execution.js";
|
||||
} else if (semverGt(version, "11.99")) {
|
||||
bootFile = "lib/internal/bootstrap/pre_execution.js";
|
||||
} else if (semverGt(version, "9.10.1")) {
|
||||
bootFile = "lib/internal/bootstrap/node.js";
|
||||
}
|
||||
|
||||
const file = await compiler.readFileAsync(bootFile),
|
||||
@@ -42,77 +45,79 @@ export default async function main(compiler: NexeCompiler, next: () => Promise<v
|
||||
next: true,
|
||||
globalReturn: true,
|
||||
loc: true,
|
||||
specDeviation: true,
|
||||
}),
|
||||
location = { start: { line: 0 } }
|
||||
location = { start: { line: 0 } };
|
||||
|
||||
walkSome(ast, (node: any) => {
|
||||
if (!location.start.line && node.type === 'BlockStatement') {
|
||||
if (!location.start.line && node.type === "BlockStatement") {
|
||||
//Find the first block statement and mark the location
|
||||
Object.assign(location, node.loc)
|
||||
return true
|
||||
Object.assign(location, node.loc);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const fileLines = file.contents.toString().split('\n')
|
||||
if (semverGt(version, '18.16.99')) {
|
||||
const fileLines = file.contents.toString().split("\n");
|
||||
if (semverGt(version, "18.16.99")) {
|
||||
await compiler.replaceInFileAsync(
|
||||
'lib/internal/modules/cjs/loader.js',
|
||||
"lib/internal/modules/cjs/loader.js",
|
||||
"'use strict';",
|
||||
"'use strict';\n" + '{{ file("lib/fs/bootstrap.js") }}' + '\n'
|
||||
)
|
||||
fileLines.splice(location.start.line, 0, 'expandArgv1 = false;')
|
||||
"'use strict';\n" + '{{ file("lib/fs/bootstrap.js") }}' + "\n"
|
||||
);
|
||||
fileLines.splice(location.start.line, 0, "expandArgv1 = false;");
|
||||
} else {
|
||||
fileLines.splice(
|
||||
location.start.line,
|
||||
0,
|
||||
'{{ file("lib/fs/bootstrap.js") }}' +
|
||||
'\n' +
|
||||
(semverGt(version, '11.99') ? 'expandArgv1 = false;\n' : '')
|
||||
)
|
||||
"\n" +
|
||||
(semverGt(version, "11.99") ? "expandArgv1 = false;\n" : "")
|
||||
);
|
||||
}
|
||||
file.contents = fileLines.join('\n')
|
||||
file.contents = fileLines.join("\n");
|
||||
|
||||
if (semverGt(version, '11.99')) {
|
||||
if (semverGt(version, '12.17.99')) {
|
||||
if (semverGt(version, "11.99")) {
|
||||
if (semverGt(version, "12.17.99")) {
|
||||
await compiler.replaceInFileAsync(
|
||||
bootFile,
|
||||
'initializeFrozenIntrinsics();',
|
||||
'initializeFrozenIntrinsics();\n' + wrap('{{ file("lib/patches/boot-nexe.js") }}')
|
||||
)
|
||||
"initializeFrozenIntrinsics();",
|
||||
"initializeFrozenIntrinsics();\n" +
|
||||
wrap('{{ file("lib/patches/boot-nexe.js") }}')
|
||||
);
|
||||
} else {
|
||||
await compiler.replaceInFileAsync(
|
||||
bootFile,
|
||||
'initializePolicy();',
|
||||
'initializePolicy();\n' + wrap('{{ file("lib/patches/boot-nexe.js") }}')
|
||||
)
|
||||
"initializePolicy();",
|
||||
"initializePolicy();\n" + wrap('{{ file("lib/patches/boot-nexe.js") }}')
|
||||
);
|
||||
}
|
||||
await compiler.replaceInFileAsync(
|
||||
bootFile,
|
||||
'assert(!CJSLoader.hasLoadedAnyUserCJSModule)',
|
||||
'/*assert(!CJSLoader.hasLoadedAnyUserCJSModule)*/'
|
||||
)
|
||||
const { contents: nodeccContents } = await compiler.readFileAsync('src/node.cc')
|
||||
if (nodeccContents.includes('if (env->worker_context() != nullptr) {')) {
|
||||
"assert(!CJSLoader.hasLoadedAnyUserCJSModule)",
|
||||
"/*assert(!CJSLoader.hasLoadedAnyUserCJSModule)*/"
|
||||
);
|
||||
const { contents: nodeccContents } = await compiler.readFileAsync(
|
||||
"src/node.cc"
|
||||
);
|
||||
if (nodeccContents.includes("if (env->worker_context() != nullptr) {")) {
|
||||
await compiler.replaceInFileAsync(
|
||||
'src/node.cc',
|
||||
'if (env->worker_context() != nullptr) {',
|
||||
'if (env->worker_context() == nullptr) {\n' +
|
||||
"src/node.cc",
|
||||
"if (env->worker_context() != nullptr) {",
|
||||
"if (env->worker_context() == nullptr) {\n" +
|
||||
' return StartExecution(env, "internal/main/run_main_module"); } else {\n'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
await compiler.replaceInFileAsync(
|
||||
'src/node.cc',
|
||||
'MaybeLocal<Value> StartMainThreadExecution(Environment* env) {',
|
||||
'MaybeLocal<Value> StartMainThreadExecution(Environment* env) {\n' +
|
||||
"src/node.cc",
|
||||
"MaybeLocal<Value> StartMainThreadExecution(Environment* env) {",
|
||||
"MaybeLocal<Value> StartMainThreadExecution(Environment* env) {\n" +
|
||||
' return StartExecution(env, "internal/main/run_main_module");\n'
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await compiler.setFileContentsAsync(
|
||||
'lib/_third_party_main.js',
|
||||
"lib/_third_party_main.js",
|
||||
'{{ file("lib/patches/boot-nexe.js") }}'
|
||||
)
|
||||
);
|
||||
}
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
+43
-30
@@ -1,23 +1,31 @@
|
||||
import axios from 'axios';
|
||||
import { platforms, architectures, NexeTarget, getTarget, targetsEqual } from './target'
|
||||
export { NexeTarget }
|
||||
import axios from "axios";
|
||||
import {
|
||||
platforms,
|
||||
architectures,
|
||||
NexeTarget,
|
||||
getTarget,
|
||||
targetsEqual,
|
||||
} from "./target";
|
||||
export { NexeTarget };
|
||||
|
||||
const versionsToSkip = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 17, 19, 21, 22]
|
||||
const versionsToSkip = [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 17, 19, 21, 22,
|
||||
];
|
||||
export interface GitAsset {
|
||||
name: string
|
||||
url: string
|
||||
browser_download_url: string
|
||||
name: string;
|
||||
url: string;
|
||||
browser_download_url: string;
|
||||
}
|
||||
|
||||
export interface GitRelease {
|
||||
tag_name: string
|
||||
assets_url: string
|
||||
upload_url: string
|
||||
assets: GitAsset[]
|
||||
tag_name: string;
|
||||
assets_url: string;
|
||||
upload_url: string;
|
||||
assets: GitAsset[];
|
||||
}
|
||||
|
||||
interface NodeRelease {
|
||||
version: string
|
||||
version: string;
|
||||
}
|
||||
|
||||
async function getJson<T>(url: string, options?: any): Promise<T> {
|
||||
@@ -26,38 +34,43 @@ async function getJson<T>(url: string, options?: any): Promise<T> {
|
||||
}
|
||||
|
||||
function isBuildableVersion(version: string) {
|
||||
if (version === '12.11.0') {
|
||||
return false
|
||||
if (version === "12.11.0") {
|
||||
return false;
|
||||
}
|
||||
return !versionsToSkip.includes(Number(version.split('.')[0]))
|
||||
return !versionsToSkip.includes(Number(version.split(".")[0]));
|
||||
}
|
||||
|
||||
export function getLatestGitRelease(options?: any): Promise<GitRelease> {
|
||||
return getJson<GitRelease>('https://api.github.com/repos/nexe/nexe/releases/latest', options)
|
||||
return getJson<GitRelease>(
|
||||
"https://api.github.com/repos/nexe/nexe/releases/latest",
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
export async function getUnBuiltReleases(options?: any) {
|
||||
const nodeReleases = await getJson<NodeRelease[]>(
|
||||
'https://nodejs.org/download/release/index.json'
|
||||
)
|
||||
const existingVersions = (await getLatestGitRelease(options)).assets.map((x) => getTarget(x.name))
|
||||
"https://nodejs.org/download/release/index.json"
|
||||
);
|
||||
const existingVersions = (await getLatestGitRelease(options)).assets.map(
|
||||
(x) => getTarget(x.name)
|
||||
);
|
||||
|
||||
const versionMap: { [key: string]: true } = {}
|
||||
const versionMap: { [key: string]: true } = {};
|
||||
return nodeReleases
|
||||
.reduce((versions: NexeTarget[], { version }) => {
|
||||
version = version.replace('v', '').trim()
|
||||
version = version.replace("v", "").trim();
|
||||
if (!isBuildableVersion(version) || versionMap[version]) {
|
||||
return versions
|
||||
return versions;
|
||||
}
|
||||
versionMap[version] = true
|
||||
versionMap[version] = true;
|
||||
platforms.forEach((platform) => {
|
||||
architectures.forEach((arch) => {
|
||||
if (arch === 'x86' && platform === 'mac') return
|
||||
if (arch.includes('arm')) return
|
||||
versions.push(getTarget({ platform, arch, version }))
|
||||
})
|
||||
})
|
||||
return versions
|
||||
if (arch === "x86" && platform === "mac") return;
|
||||
if (arch.includes("arm")) return;
|
||||
versions.push(getTarget({ platform, arch, version }));
|
||||
});
|
||||
});
|
||||
return versions;
|
||||
}, [])
|
||||
.filter((x) => !existingVersions.some((t) => targetsEqual(t, x)))
|
||||
.filter((x) => !existingVersions.some((t) => targetsEqual(t, x)));
|
||||
}
|
||||
|
||||
+41
-33
@@ -1,33 +1,35 @@
|
||||
import { join, dirname } from 'path'
|
||||
import * as fs from 'fs'
|
||||
import { promisify } from 'util'
|
||||
import { readFileAsync, writeFileAsync, isDirectoryAsync } from '../util'
|
||||
import mkdirpAsync = require('mkdirp')
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { join, dirname } from "path";
|
||||
import * as fs from "fs";
|
||||
import { promisify } from "util";
|
||||
import { readFileAsync, writeFileAsync, isDirectoryAsync } from "../util";
|
||||
import mkdirpAsync = require("mkdirp");
|
||||
import { NexeCompiler } from "../compiler";
|
||||
|
||||
const unlinkAsync = promisify(fs.unlink),
|
||||
readdirAsync = promisify(fs.readdir)
|
||||
readdirAsync = promisify(fs.readdir);
|
||||
|
||||
function readDirAsync(dir: string): Promise<string[]> {
|
||||
return readdirAsync(dir).then((paths) => {
|
||||
return Promise.all(
|
||||
paths.map((file: string) => {
|
||||
const path = join(dir, file)
|
||||
return isDirectoryAsync(path).then((x) => (x ? readDirAsync(path) : (path as any)))
|
||||
const path = join(dir, file);
|
||||
return isDirectoryAsync(path).then((x) =>
|
||||
x ? readDirAsync(path) : (path as any)
|
||||
);
|
||||
})
|
||||
).then((result) => {
|
||||
return [].concat(...(result as any))
|
||||
})
|
||||
})
|
||||
return [].concat(...(result as any));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function maybeReadFileContentsAsync(file: string) {
|
||||
return readFileAsync(file, 'utf-8').catch((e) => {
|
||||
if (e.code === 'ENOENT') {
|
||||
return ''
|
||||
return readFileAsync(file, "utf-8").catch((e) => {
|
||||
if (e.code === "ENOENT") {
|
||||
return "";
|
||||
}
|
||||
throw e
|
||||
})
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,30 +41,36 @@ function maybeReadFileContentsAsync(file: string) {
|
||||
* - Original versions of sources to be patched are written to the temporary directory
|
||||
* - Finally, The patched files are written into source.
|
||||
*/
|
||||
export default async function artifacts(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
const { src } = compiler
|
||||
const temp = join(src, 'nexe')
|
||||
await mkdirpAsync(temp)
|
||||
const tmpFiles = await readDirAsync(temp)
|
||||
export default async function artifacts(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
const { src } = compiler;
|
||||
const temp = join(src, "nexe");
|
||||
await mkdirpAsync(temp);
|
||||
const tmpFiles = await readDirAsync(temp);
|
||||
|
||||
await Promise.all(
|
||||
tmpFiles.map(async (path) => {
|
||||
return compiler.writeFileAsync(path.replace(temp, ''), await readFileAsync(path, 'utf-8'))
|
||||
return compiler.writeFileAsync(
|
||||
path.replace(temp, ""),
|
||||
await readFileAsync(path, "utf-8")
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
await next()
|
||||
await next();
|
||||
|
||||
await Promise.all(tmpFiles.map((x) => unlinkAsync(x)))
|
||||
await Promise.all(tmpFiles.map((x) => unlinkAsync(x)));
|
||||
return Promise.all(
|
||||
compiler.files.map(async (file) => {
|
||||
const sourceFile = join(src, file.filename)
|
||||
const tempFile = join(temp, file.filename)
|
||||
const fileContents = await maybeReadFileContentsAsync(sourceFile)
|
||||
const sourceFile = join(src, file.filename);
|
||||
const tempFile = join(temp, file.filename);
|
||||
const fileContents = await maybeReadFileContentsAsync(sourceFile);
|
||||
|
||||
await mkdirpAsync(dirname(tempFile))
|
||||
await writeFileAsync(tempFile, fileContents)
|
||||
await compiler.writeFileAsync(file.filename, file.contents)
|
||||
await mkdirpAsync(dirname(tempFile));
|
||||
await writeFileAsync(tempFile, fileContents);
|
||||
await compiler.writeFileAsync(file.filename, file.contents);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
+51
-44
@@ -1,88 +1,95 @@
|
||||
import { NexeCompiler, NexeError } from '../compiler'
|
||||
import { resolve, relative } from 'path'
|
||||
import resolveFiles, { resolveSync } from 'resolve-dependencies'
|
||||
import { dequote, STDIN_FLAG, semverGt, each } from '../util'
|
||||
import { Readable } from 'stream'
|
||||
import { NexeCompiler, NexeError } from "../compiler";
|
||||
import { resolve, relative } from "path";
|
||||
import resolveFiles, { resolveSync } from "resolve-dependencies";
|
||||
import { dequote, STDIN_FLAG, semverGt, each } from "../util";
|
||||
import { Readable } from "stream";
|
||||
|
||||
function getStdIn(stdin: Readable): Promise<string> {
|
||||
let out = ''
|
||||
let out = "";
|
||||
return new Promise((resolve) => {
|
||||
stdin
|
||||
.setEncoding('utf8')
|
||||
.on('readable', () => {
|
||||
let current
|
||||
.setEncoding("utf8")
|
||||
.on("readable", () => {
|
||||
let current;
|
||||
while ((current = stdin.read())) {
|
||||
out += current
|
||||
out += current;
|
||||
}
|
||||
})
|
||||
.on('end', () => resolve(out.trim()))
|
||||
.on("end", () => resolve(out.trim()));
|
||||
setTimeout(() => {
|
||||
if (!out.trim()) {
|
||||
resolve(out.trim())
|
||||
resolve(out.trim());
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
export default async function bundle(compiler: NexeCompiler, next: any) {
|
||||
const { bundle: doBundle, cwd, input: inputPath } = compiler.options
|
||||
let input = inputPath
|
||||
compiler.entrypoint = './' + relative(cwd, input)
|
||||
const { bundle: doBundle, cwd, input: inputPath } = compiler.options;
|
||||
let input = inputPath;
|
||||
compiler.entrypoint = "./" + relative(cwd, input);
|
||||
|
||||
if (semverGt(compiler.target.version, '11.99')) {
|
||||
compiler.startup = ''
|
||||
if (semverGt(compiler.target.version, "11.99")) {
|
||||
compiler.startup = "";
|
||||
} else {
|
||||
compiler.startup = ';require("module").runMain();'
|
||||
compiler.startup = ';require("module").runMain();';
|
||||
}
|
||||
|
||||
if (!doBundle) {
|
||||
await compiler.addResource(resolve(cwd, input))
|
||||
return next()
|
||||
await compiler.addResource(resolve(cwd, input));
|
||||
return next();
|
||||
}
|
||||
|
||||
let code = ''
|
||||
if (typeof doBundle === 'string') {
|
||||
code = await require(doBundle).createBundle(compiler.options)
|
||||
let code = "";
|
||||
if (typeof doBundle === "string") {
|
||||
code = await require(doBundle).createBundle(compiler.options);
|
||||
}
|
||||
|
||||
if (input === STDIN_FLAG && (code = code || dequote(await getStdIn(process.stdin)))) {
|
||||
compiler.stdinUsed = true
|
||||
compiler.entrypoint = './__nexe_stdin.js'
|
||||
await compiler.addResource(resolve(cwd, compiler.entrypoint), code)
|
||||
return next()
|
||||
if (
|
||||
input === STDIN_FLAG &&
|
||||
(code = code || dequote(await getStdIn(process.stdin)))
|
||||
) {
|
||||
compiler.stdinUsed = true;
|
||||
compiler.entrypoint = "./__nexe_stdin.js";
|
||||
await compiler.addResource(resolve(cwd, compiler.entrypoint), code);
|
||||
return next();
|
||||
}
|
||||
|
||||
if (input === STDIN_FLAG) {
|
||||
const maybeInput = resolveSync(cwd, '.')
|
||||
const maybeInput = resolveSync(cwd, ".");
|
||||
if (!maybeInput || !maybeInput.absPath) {
|
||||
throw new NexeError('No valid input detected')
|
||||
throw new NexeError("No valid input detected");
|
||||
}
|
||||
input = maybeInput.absPath
|
||||
compiler.entrypoint = './' + relative(cwd, input)
|
||||
input = maybeInput.absPath;
|
||||
compiler.entrypoint = "./" + relative(cwd, input);
|
||||
}
|
||||
|
||||
const step = compiler.log.step('Resolving dependencies...')
|
||||
const step = compiler.log.step("Resolving dependencies...");
|
||||
|
||||
const { files, warnings } = await resolveFiles(
|
||||
input,
|
||||
...Object.keys(compiler.bundle.list).filter((x) => x.endsWith('.js') || x.endsWith('.mjs')),
|
||||
{ cwd, expand: 'variable', loadContent: false }
|
||||
)
|
||||
...Object.keys(compiler.bundle.list).filter(
|
||||
(x) => x.endsWith(".js") || x.endsWith(".mjs")
|
||||
),
|
||||
{ cwd, expand: "variable", loadContent: false }
|
||||
);
|
||||
|
||||
if (
|
||||
warnings.filter((x) => x.startsWith('Error parsing file') && !x.includes('node_modules')).length
|
||||
warnings.filter(
|
||||
(x) => x.startsWith("Error parsing file") && !x.includes("node_modules")
|
||||
).length
|
||||
) {
|
||||
throw new NexeError('Parsing Error:\n' + warnings.join('\n'))
|
||||
throw new NexeError("Parsing Error:\n" + warnings.join("\n"));
|
||||
}
|
||||
|
||||
//TODO: warnings.forEach((x) => console.log(x))
|
||||
|
||||
await Promise.all(
|
||||
Object.entries(files).map(([key]) => {
|
||||
step.log(`Including dependency: ${key}`)
|
||||
return compiler.addResource(key)
|
||||
step.log(`Including dependency: ${key}`);
|
||||
return compiler.addResource(key);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
+18
-13
@@ -1,19 +1,24 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { rimrafAsync } from '../util'
|
||||
import { NexeTarget } from '../target'
|
||||
import { NexeCompiler } from "../compiler";
|
||||
import { rimrafAsync } from "../util";
|
||||
import { NexeTarget } from "../target";
|
||||
|
||||
export default async function clean(compiler: NexeCompiler, next: () => Promise<any>) {
|
||||
const { options } = compiler
|
||||
export default async function clean(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<any>
|
||||
) {
|
||||
const { options } = compiler;
|
||||
if (options.clean) {
|
||||
let path = compiler.src
|
||||
let path = compiler.src;
|
||||
if (!options.build) {
|
||||
path = compiler.getNodeExecutableLocation(compiler.options.targets[0] as NexeTarget)
|
||||
path = compiler.getNodeExecutableLocation(
|
||||
compiler.options.targets[0] as NexeTarget
|
||||
);
|
||||
}
|
||||
const step = compiler.log.step('Cleaning up nexe build artifacts...')
|
||||
step.log(`Deleting contents at: ${path}`)
|
||||
await rimrafAsync(path)
|
||||
step.log(`Deleted contents at: ${path}`)
|
||||
return compiler.quit()
|
||||
const step = compiler.log.step("Cleaning up nexe build artifacts...");
|
||||
step.log(`Deleting contents at: ${path}`);
|
||||
await rimrafAsync(path);
|
||||
step.log(`Deleted contents at: ${path}`);
|
||||
return compiler.quit();
|
||||
}
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
+40
-30
@@ -1,11 +1,17 @@
|
||||
import { platform } from 'os'
|
||||
import { dirname, normalize, relative, resolve } from 'path'
|
||||
import { createWriteStream, chmodSync, statSync, readFileSync, writeFileSync } from 'fs'
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { NexeTarget } from '../target'
|
||||
import { STDIN_FLAG } from '../util'
|
||||
import { patchMachOExecutable } from './mach-o'
|
||||
import mkdirp = require('mkdirp')
|
||||
import { platform } from "os";
|
||||
import { dirname, normalize, relative, resolve } from "path";
|
||||
import {
|
||||
createWriteStream,
|
||||
chmodSync,
|
||||
statSync,
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
} from "fs";
|
||||
import { NexeCompiler } from "../compiler";
|
||||
import { NexeTarget } from "../target";
|
||||
import { STDIN_FLAG } from "../util";
|
||||
import { patchMachOExecutable } from "./mach-o";
|
||||
import mkdirp = require("mkdirp");
|
||||
|
||||
/**
|
||||
* The "cli" step detects the appropriate input. If no input options are passed,
|
||||
@@ -17,50 +23,54 @@ import mkdirp = require('mkdirp')
|
||||
* @param {*} compiler
|
||||
* @param {*} next
|
||||
*/
|
||||
export default async function cli(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
await next()
|
||||
export default async function cli(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
await next();
|
||||
const { log } = compiler,
|
||||
target = compiler.options.targets.shift() as NexeTarget,
|
||||
deliverable = await compiler.compileAsync(target),
|
||||
output = normalize(compiler.output!)
|
||||
output = normalize(compiler.output!);
|
||||
|
||||
mkdirp.sync(dirname(output))
|
||||
mkdirp.sync(dirname(output));
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
const step = log.step('Writing result to file')
|
||||
const step = log.step("Writing result to file");
|
||||
deliverable
|
||||
.pipe(createWriteStream(output))
|
||||
.on('error', rej)
|
||||
.once('close', (e: Error) => {
|
||||
if (e) {
|
||||
rej(e)
|
||||
} else if (compiler.output) {
|
||||
.on("error", rej)
|
||||
.once("close", () => {
|
||||
if (compiler.output) {
|
||||
const output = compiler.output,
|
||||
mode = statSync(output).mode | 0o111,
|
||||
inputFileLogOutput = relative(
|
||||
process.cwd(),
|
||||
resolve(compiler.options.cwd, compiler.entrypoint || compiler.options.input)
|
||||
resolve(
|
||||
compiler.options.cwd,
|
||||
compiler.entrypoint || compiler.options.input
|
||||
)
|
||||
),
|
||||
outputFileLogOutput = relative(process.cwd(), output)
|
||||
outputFileLogOutput = relative(process.cwd(), output);
|
||||
|
||||
if (platform() === 'darwin') {
|
||||
step.log('Preparing binary for macOS signing.')
|
||||
writeFileSync(output, patchMachOExecutable(readFileSync(output)))
|
||||
if (platform() === "darwin") {
|
||||
step.log("Preparing binary for macOS signing.");
|
||||
writeFileSync(output, patchMachOExecutable(readFileSync(output)));
|
||||
}
|
||||
|
||||
chmodSync(output, mode.toString(8).slice(-3))
|
||||
chmodSync(output, mode.toString(8).slice(-3));
|
||||
step.log(
|
||||
`Entry: '${
|
||||
compiler.stdinUsed
|
||||
? compiler.options.mangle
|
||||
? STDIN_FLAG
|
||||
: '[none]'
|
||||
: "[none]"
|
||||
: inputFileLogOutput
|
||||
}' written to: ${outputFileLogOutput}`
|
||||
)
|
||||
compiler.quit()
|
||||
res(output)
|
||||
);
|
||||
compiler.quit();
|
||||
res(output);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
+87
-60
@@ -1,75 +1,86 @@
|
||||
import axios, { AxiosResponse } from 'axios'
|
||||
import { pathExistsAsync } from '../util'
|
||||
import { LogStep } from '../logger'
|
||||
import { IncomingMessage } from 'http'
|
||||
import { NexeCompiler, NexeError } from '../compiler'
|
||||
import { dirname } from 'path'
|
||||
import { createWriteStream } from 'fs'
|
||||
import { pipeline } from 'stream/promises'
|
||||
import { createBrotliDecompress, createGunzip, createInflate } from 'zlib'
|
||||
import tar from 'tar'
|
||||
import fs from 'fs/promises'
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import { pathExistsAsync } from "../util";
|
||||
import { LogStep } from "../logger";
|
||||
import { IncomingMessage } from "http";
|
||||
import { NexeCompiler, NexeError } from "../compiler";
|
||||
import { dirname } from "path";
|
||||
import { createWriteStream } from "fs";
|
||||
import { pipeline } from "stream/promises";
|
||||
import { createBrotliDecompress, createGunzip, createInflate } from "zlib";
|
||||
import tar from "tar";
|
||||
import fs from "fs/promises";
|
||||
|
||||
async function downloadWithProgress(url: string, dest: string, options: any = {}, step?: LogStep): Promise<void> {
|
||||
async function downloadWithProgress(
|
||||
url: string,
|
||||
dest: string,
|
||||
options: any = {},
|
||||
step?: LogStep
|
||||
): Promise<void> {
|
||||
const response = await axios({
|
||||
url,
|
||||
method: 'GET',
|
||||
responseType: 'stream',
|
||||
...options
|
||||
})
|
||||
method: "GET",
|
||||
responseType: "stream",
|
||||
...options,
|
||||
});
|
||||
|
||||
const total = parseInt(response.headers['content-length'] || '0', 10)
|
||||
let current = 0
|
||||
const total = parseInt(response.headers["content-length"] || "0", 10);
|
||||
let current = 0;
|
||||
|
||||
// Create write stream
|
||||
const writer = createWriteStream(dest)
|
||||
const writer = createWriteStream(dest);
|
||||
|
||||
// Handle progress
|
||||
response.data.on('data', (chunk: Buffer) => {
|
||||
current += chunk.length
|
||||
response.data.on("data", (chunk: Buffer) => {
|
||||
current += chunk.length;
|
||||
if (step && total > 0) {
|
||||
step.modify(`Downloading...${((current / total) * 100).toFixed()}%`)
|
||||
step.modify(`Downloading...${((current / total) * 100).toFixed()}%`);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Pipe the response to file
|
||||
await pipeline(response.data, writer)
|
||||
await pipeline(response.data, writer);
|
||||
|
||||
if (step) {
|
||||
step.log(`Download completed: ${dest}`)
|
||||
step.log(`Download completed: ${dest}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchNodeSourceAsync(dest: string, url: string, step: LogStep, options = {}) {
|
||||
const setText = (p: number) => step.modify(`Downloading Node: ${p.toFixed()}%...`)
|
||||
async function fetchNodeSourceAsync(
|
||||
dest: string,
|
||||
url: string,
|
||||
step: LogStep,
|
||||
options = {}
|
||||
) {
|
||||
const setText = (p: number) =>
|
||||
step.modify(`Downloading Node: ${p.toFixed()}%...`);
|
||||
|
||||
// Download the file first
|
||||
const tempFile = dest + '.tar.gz'
|
||||
const tempFile = dest + ".tar.gz";
|
||||
const response = await axios({
|
||||
url,
|
||||
method: 'GET',
|
||||
responseType: 'stream',
|
||||
...options
|
||||
})
|
||||
method: "GET",
|
||||
responseType: "stream",
|
||||
...options,
|
||||
});
|
||||
|
||||
const total = parseInt(response.headers['content-length'] || '0', 10)
|
||||
let current = 0
|
||||
const total = parseInt(response.headers["content-length"] || "0", 10);
|
||||
let current = 0;
|
||||
|
||||
// Create write stream for the temp file
|
||||
const writer = createWriteStream(tempFile)
|
||||
const writer = createWriteStream(tempFile);
|
||||
|
||||
// Track progress
|
||||
response.data.on('data', (chunk: Buffer) => {
|
||||
current += chunk.length
|
||||
response.data.on("data", (chunk: Buffer) => {
|
||||
current += chunk.length;
|
||||
if (total > 0) {
|
||||
setText((current / total) * 100)
|
||||
setText((current / total) * 100);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Wait for download to complete
|
||||
await pipeline(response.data, writer)
|
||||
await pipeline(response.data, writer);
|
||||
|
||||
step.log('Extracting Node...')
|
||||
step.log("Extracting Node...");
|
||||
|
||||
// Extract the tar.gz file
|
||||
await pipeline(
|
||||
@@ -77,27 +88,34 @@ async function fetchNodeSourceAsync(dest: string, url: string, step: LogStep, op
|
||||
createGunzip(),
|
||||
tar.extract({
|
||||
cwd: dest,
|
||||
strip: 1
|
||||
strip: 1,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Clean up temp file
|
||||
await fs.unlink(tempFile)
|
||||
await fs.unlink(tempFile);
|
||||
|
||||
step.log(`Node source extracted to: ${dest}`)
|
||||
step.log(`Node source extracted to: ${dest}`);
|
||||
}
|
||||
|
||||
async function fetchPrebuiltBinary(compiler: NexeCompiler, step: any) {
|
||||
const { target, remoteAsset } = compiler,
|
||||
filename = compiler.getNodeExecutableLocation(target)
|
||||
filename = compiler.getNodeExecutableLocation(target);
|
||||
|
||||
try {
|
||||
await downloadWithProgress(remoteAsset, filename, compiler.options.downloadOptions, step)
|
||||
await downloadWithProgress(
|
||||
remoteAsset,
|
||||
filename,
|
||||
compiler.options.downloadOptions,
|
||||
step
|
||||
);
|
||||
} catch (e: any) {
|
||||
if (e.response?.status === 404) {
|
||||
throw new NexeError(`${remoteAsset} is not available, create it using the --build flag`)
|
||||
throw new NexeError(
|
||||
`${remoteAsset} is not available, create it using the --build flag`
|
||||
);
|
||||
} else {
|
||||
throw new NexeError('Error downloading prebuilt binary: ' + e.message)
|
||||
throw new NexeError("Error downloading prebuilt binary: " + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,32 +125,41 @@ async function fetchPrebuiltBinary(compiler: NexeCompiler, step: any) {
|
||||
* @param {*} compiler
|
||||
* @param {*} next
|
||||
*/
|
||||
export default async function downloadNode(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
export default async function downloadNode(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
const { src, log, target } = compiler,
|
||||
{ version } = target,
|
||||
{ sourceUrl, downloadOptions, build } = compiler.options,
|
||||
url = sourceUrl || `https://nodejs.org/dist/v${version}/node-v${version}.tar.gz`,
|
||||
url =
|
||||
sourceUrl ||
|
||||
`https://nodejs.org/dist/v${version}/node-v${version}.tar.gz`,
|
||||
step = log.step(
|
||||
`Downloading ${build ? '' : 'pre-built'} Node.js ${build ? `source from: ${url}` : ''}`
|
||||
`Downloading ${build ? "" : "pre-built"} Node.js ${
|
||||
build ? `source from: ${url}` : ""
|
||||
}`
|
||||
),
|
||||
exeLocation = compiler.getNodeExecutableLocation(build ? undefined : target),
|
||||
downloadExists = await pathExistsAsync(build ? src : exeLocation)
|
||||
exeLocation = compiler.getNodeExecutableLocation(
|
||||
build ? undefined : target
|
||||
),
|
||||
downloadExists = await pathExistsAsync(build ? src : exeLocation);
|
||||
|
||||
if (downloadExists) {
|
||||
step.log('Already downloaded...')
|
||||
return next()
|
||||
step.log("Already downloaded...");
|
||||
return next();
|
||||
}
|
||||
|
||||
if (build) {
|
||||
await fetchNodeSourceAsync(src, url, step, downloadOptions)
|
||||
await fetchNodeSourceAsync(src, url, step, downloadOptions);
|
||||
} else {
|
||||
await fetchPrebuiltBinary(compiler, step)
|
||||
await fetchPrebuiltBinary(compiler, step);
|
||||
}
|
||||
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
// Helper function to create a readable stream
|
||||
function createReadStream(path: string) {
|
||||
return require('fs').createReadStream(path)
|
||||
return require("fs").createReadStream(path);
|
||||
}
|
||||
|
||||
+24
-24
@@ -27,7 +27,7 @@
|
||||
function parseCStr(buf: Buffer) {
|
||||
for (let i = 0; i < buf.length; i += 1) {
|
||||
if (buf[i] === 0) {
|
||||
return buf.slice(0, i).toString()
|
||||
return buf.slice(0, i).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,50 +35,50 @@ function parseCStr(buf: Buffer) {
|
||||
function patchCommand(type: number, buf: Buffer, file: Buffer) {
|
||||
// segment_64
|
||||
if (type === 0x19) {
|
||||
const name = parseCStr(buf.slice(0, 16))
|
||||
const name = parseCStr(buf.slice(0, 16));
|
||||
|
||||
if (name === '__LINKEDIT') {
|
||||
const fileoff = buf.readBigUInt64LE(32)
|
||||
const vmsizePatched = BigInt(file.length) - fileoff
|
||||
const filesizePatched = vmsizePatched
|
||||
if (name === "__LINKEDIT") {
|
||||
const fileoff = buf.readBigUInt64LE(32);
|
||||
const vmsizePatched = BigInt(file.length) - fileoff;
|
||||
const filesizePatched = vmsizePatched;
|
||||
|
||||
buf.writeBigUInt64LE(vmsizePatched, 24)
|
||||
buf.writeBigUInt64LE(filesizePatched, 40)
|
||||
buf.writeBigUInt64LE(vmsizePatched, 24);
|
||||
buf.writeBigUInt64LE(filesizePatched, 40);
|
||||
}
|
||||
}
|
||||
|
||||
// symtab
|
||||
if (type === 0x2) {
|
||||
const stroff = buf.readUInt32LE(8)
|
||||
const strsizePatched = file.length - stroff
|
||||
const stroff = buf.readUInt32LE(8);
|
||||
const strsizePatched = file.length - stroff;
|
||||
|
||||
buf.writeUInt32LE(strsizePatched, 12)
|
||||
buf.writeUInt32LE(strsizePatched, 12);
|
||||
}
|
||||
}
|
||||
|
||||
function patchMachOExecutable(file: Buffer) {
|
||||
const align = 8
|
||||
const hsize = 32
|
||||
const align = 8;
|
||||
const hsize = 32;
|
||||
|
||||
const ncmds = file.readUInt32LE(16)
|
||||
const buf = file.slice(hsize)
|
||||
const ncmds = file.readUInt32LE(16);
|
||||
const buf = file.slice(hsize);
|
||||
|
||||
for (let offset = 0, i = 0; i < ncmds; i += 1) {
|
||||
const type = buf.readUInt32LE(offset)
|
||||
const type = buf.readUInt32LE(offset);
|
||||
|
||||
offset += 4
|
||||
const size = buf.readUInt32LE(offset) - 8
|
||||
offset += 4;
|
||||
const size = buf.readUInt32LE(offset) - 8;
|
||||
|
||||
offset += 4
|
||||
patchCommand(type, buf.slice(offset, offset + size), file)
|
||||
offset += 4;
|
||||
patchCommand(type, buf.slice(offset, offset + size), file);
|
||||
|
||||
offset += size
|
||||
offset += size;
|
||||
if (offset & align) {
|
||||
offset += align - (offset & align)
|
||||
offset += align - (offset & align);
|
||||
}
|
||||
}
|
||||
|
||||
return file
|
||||
return file;
|
||||
}
|
||||
|
||||
export { patchMachOExecutable }
|
||||
export { patchMachOExecutable };
|
||||
|
||||
+25
-17
@@ -1,25 +1,33 @@
|
||||
import { each } from '../util'
|
||||
import globs from 'globby'
|
||||
import { resolve } from 'path'
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { each } from "../util";
|
||||
import globs from "globby";
|
||||
import { resolve } from "path";
|
||||
import { NexeCompiler } from "../compiler";
|
||||
|
||||
export default async function resource(compiler: NexeCompiler, next: () => Promise<any>) {
|
||||
const { cwd, resources } = compiler.options
|
||||
export default async function resource(
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<any>
|
||||
) {
|
||||
const { cwd, resources } = compiler.options;
|
||||
if (!resources.length) {
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
const step = compiler.log.step('Bundling Resources...')
|
||||
let count = 0
|
||||
const step = compiler.log.step("Bundling Resources...");
|
||||
let count = 0;
|
||||
|
||||
// workaround for https://github.com/sindresorhus/globby/issues/127
|
||||
// and https://github.com/mrmlnc/fast-glob#pattern-syntax
|
||||
const resourcesWithForwardSlashes = resources.map((r) => r.replace(/\\/g, '/'))
|
||||
const resourcesWithForwardSlashes = resources.map((r) =>
|
||||
r.replace(/\\/g, "/")
|
||||
);
|
||||
|
||||
await each(globs(resourcesWithForwardSlashes, { cwd, onlyFiles: true }), async (file) => {
|
||||
count++
|
||||
step.log(`Including file: ${file}`)
|
||||
await compiler.addResource(resolve(cwd, file))
|
||||
})
|
||||
step.log(`Included ${count} file(s)`)
|
||||
return next()
|
||||
await each(
|
||||
globs(resourcesWithForwardSlashes, { cwd, onlyFiles: true }),
|
||||
async (file) => {
|
||||
count++;
|
||||
step.log(`Including file: ${file}`);
|
||||
await compiler.addResource(resolve(cwd, file));
|
||||
}
|
||||
);
|
||||
step.log(`Included ${count} file(s)`);
|
||||
return next();
|
||||
}
|
||||
|
||||
+19
-16
@@ -1,24 +1,27 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { wrap } from '../util'
|
||||
import { NexeCompiler } from "../compiler";
|
||||
import { wrap } from "../util";
|
||||
|
||||
export default async function (compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
await next()
|
||||
export default async function (
|
||||
compiler: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
await next();
|
||||
compiler.shims.push(
|
||||
wrap(
|
||||
[
|
||||
'process.__nexe = {};',
|
||||
'const fsPatcher = (function() {',
|
||||
'const module = {exports: {}};',
|
||||
'const exports = module.exports;',
|
||||
"process.__nexe = {};",
|
||||
"const fsPatcher = (function() {",
|
||||
"const module = {exports: {}};",
|
||||
"const exports = module.exports;",
|
||||
'{{file("lib/fs/patch.bundle.js")}}',
|
||||
'return module.exports;',
|
||||
'})()',
|
||||
'fsPatcher.shimFs(process.__nexe);',
|
||||
compiler.options.fs ? '' : 'restoreFs();',
|
||||
].join('\n')
|
||||
"return module.exports;",
|
||||
"})()",
|
||||
"fsPatcher.shimFs(process.__nexe);",
|
||||
compiler.options.fs ? "" : "restoreFs();",
|
||||
].join("\n")
|
||||
//TODO support only restoring specific methods
|
||||
)
|
||||
)
|
||||
);
|
||||
compiler.shims.push(
|
||||
wrap(`
|
||||
if (process.argv[1] && process.env.NODE_UNIQUE_ID) {
|
||||
@@ -27,7 +30,7 @@ export default async function (compiler: NexeCompiler, next: () => Promise<void>
|
||||
delete process.env.NODE_UNIQUE_ID
|
||||
}
|
||||
`)
|
||||
)
|
||||
);
|
||||
|
||||
compiler.shims.push(
|
||||
wrap(`
|
||||
@@ -39,5 +42,5 @@ export default async function (compiler: NexeCompiler, next: () => Promise<void>
|
||||
process.argv.splice(1,0, entry)
|
||||
}
|
||||
`)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
+61
-50
@@ -1,96 +1,107 @@
|
||||
export type NodePlatform = 'windows' | 'mac' | 'alpine' | 'linux'
|
||||
export type NodeArch = 'x86' | 'x64' | 'arm' | 'arm64'
|
||||
export type NodePlatform = "windows" | "mac" | "alpine" | "linux";
|
||||
export type NodeArch = "x86" | "x64" | "arm" | "arm64";
|
||||
|
||||
const platforms: NodePlatform[] = ['windows', 'mac', 'alpine', 'linux'],
|
||||
architectures: NodeArch[] = ['x86', 'x64', 'arm', 'arm64']
|
||||
const platforms: NodePlatform[] = ["windows", "mac", "alpine", "linux"],
|
||||
architectures: NodeArch[] = ["x86", "x64", "arm", "arm64"];
|
||||
|
||||
export { platforms, architectures }
|
||||
export { platforms, architectures };
|
||||
|
||||
export interface NexeTarget {
|
||||
version: string
|
||||
platform: NodePlatform | string
|
||||
arch: NodeArch | string
|
||||
version: string;
|
||||
platform: NodePlatform | string;
|
||||
arch: NodeArch | string;
|
||||
}
|
||||
|
||||
const prettyPlatform: { [key: string]: NodePlatform } = {
|
||||
win32: 'windows',
|
||||
windows: 'windows',
|
||||
win: 'windows',
|
||||
darwin: 'mac',
|
||||
macos: 'mac',
|
||||
mac: 'mac',
|
||||
linux: 'linux',
|
||||
static: 'alpine',
|
||||
alpine: 'alpine',
|
||||
}
|
||||
win32: "windows",
|
||||
windows: "windows",
|
||||
win: "windows",
|
||||
darwin: "mac",
|
||||
macos: "mac",
|
||||
mac: "mac",
|
||||
linux: "linux",
|
||||
static: "alpine",
|
||||
alpine: "alpine",
|
||||
};
|
||||
|
||||
const prettyArch: { [key: string]: NodeArch } = {
|
||||
x86: 'x86',
|
||||
arm6: 'arm',
|
||||
arm64: 'arm64',
|
||||
arm6l: 'arm',
|
||||
arm: 'arm',
|
||||
arm7: 'arm',
|
||||
arm7l: 'arm',
|
||||
amd64: 'x64',
|
||||
ia32: 'x86',
|
||||
x32: 'x86',
|
||||
x64: 'x64',
|
||||
}
|
||||
x86: "x86",
|
||||
arm6: "arm",
|
||||
arm64: "arm64",
|
||||
arm6l: "arm",
|
||||
arm: "arm",
|
||||
arm7: "arm",
|
||||
arm7l: "arm",
|
||||
amd64: "x64",
|
||||
ia32: "x86",
|
||||
x32: "x86",
|
||||
x64: "x64",
|
||||
};
|
||||
|
||||
function isVersion(x: string) {
|
||||
if (!x) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return /^[\d]+$/.test(x.replace(/v|\.|\s+/g, ''))
|
||||
return /^[\d]+$/.test(x.replace(/v|\.|\s+/g, ""));
|
||||
}
|
||||
|
||||
function isPlatform(x: string): x is NodePlatform {
|
||||
return x in prettyPlatform
|
||||
return x in prettyPlatform;
|
||||
}
|
||||
|
||||
function isArch(x: string): x is NodeArch {
|
||||
return x in prettyArch
|
||||
return x in prettyArch;
|
||||
}
|
||||
|
||||
class Target implements NexeTarget {
|
||||
constructor(public arch: NodeArch, public platform: NodePlatform, public version: string) {}
|
||||
constructor(
|
||||
public arch: NodeArch,
|
||||
public platform: NodePlatform,
|
||||
public version: string
|
||||
) {}
|
||||
toJSON() {
|
||||
return this.toString()
|
||||
return this.toString();
|
||||
}
|
||||
toString() {
|
||||
return `${this.platform}-${this.arch}-${this.version}`
|
||||
return `${this.platform}-${this.arch}-${this.version}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function targetsEqual(a: NexeTarget, b: NexeTarget) {
|
||||
return a.arch === b.arch && a.platform === b.platform && a.version === b.version
|
||||
return (
|
||||
a.arch === b.arch && a.platform === b.platform && a.version === b.version
|
||||
);
|
||||
}
|
||||
|
||||
export function getTarget(target: string | Partial<NexeTarget> = ''): NexeTarget {
|
||||
const currentArch = process.arch
|
||||
let arch = currentArch in prettyArch ? prettyArch[process.arch] : (process.arch as NodeArch),
|
||||
export function getTarget(
|
||||
target: string | Partial<NexeTarget> = ""
|
||||
): NexeTarget {
|
||||
const currentArch = process.arch;
|
||||
let arch =
|
||||
currentArch in prettyArch
|
||||
? prettyArch[process.arch]
|
||||
: (process.arch as NodeArch),
|
||||
platform = prettyPlatform[process.platform],
|
||||
version = process.version.slice(1)
|
||||
version = process.version.slice(1);
|
||||
|
||||
if (typeof target !== 'string') {
|
||||
target = `${target.platform}-${target.arch}-${target.version}`
|
||||
if (typeof target !== "string") {
|
||||
target = `${target.platform}-${target.arch}-${target.version}`;
|
||||
}
|
||||
|
||||
target
|
||||
.toLowerCase()
|
||||
.split('-')
|
||||
.split("-")
|
||||
.forEach((x) => {
|
||||
if (isVersion(x)) {
|
||||
version = x.replace(/v/g, '')
|
||||
version = x.replace(/v/g, "");
|
||||
}
|
||||
if (isPlatform(x)) {
|
||||
platform = prettyPlatform[x]
|
||||
platform = prettyPlatform[x];
|
||||
}
|
||||
if (isArch(x)) {
|
||||
arch = prettyArch[x]
|
||||
arch = prettyArch[x];
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return new Target(arch, platform, version)
|
||||
return new Target(arch, platform, version);
|
||||
}
|
||||
|
||||
Vendored
+13
-13
@@ -1,23 +1,23 @@
|
||||
declare module 'got' {
|
||||
declare module "got" {
|
||||
interface GotFn {
|
||||
(url: string, options?: any): Promise<{ body: string }>
|
||||
stream(url: string, optoins?: any): any
|
||||
(url: string, options?: any): Promise<{ body: string }>;
|
||||
stream(url: string, optoins?: any): any;
|
||||
}
|
||||
const got: GotFn
|
||||
export = got
|
||||
const got: GotFn;
|
||||
export = got;
|
||||
}
|
||||
declare module 'download' {
|
||||
import { Duplex } from 'stream'
|
||||
declare module "download" {
|
||||
import { Duplex } from "stream";
|
||||
interface DownloadOptions {
|
||||
extract?: boolean
|
||||
strip?: number
|
||||
filename?: string
|
||||
proxy?: string
|
||||
extract?: boolean;
|
||||
strip?: number;
|
||||
filename?: string;
|
||||
proxy?: string;
|
||||
}
|
||||
function download(
|
||||
url: string,
|
||||
destination?: string | DownloadOptions,
|
||||
options?: DownloadOptions
|
||||
): PromiseLike<Buffer> & Duplex
|
||||
export = download
|
||||
): PromiseLike<Buffer> & Duplex;
|
||||
export = download;
|
||||
}
|
||||
|
||||
+40
-40
@@ -1,100 +1,100 @@
|
||||
import { readFile, writeFile, stat } from 'fs'
|
||||
import { execFile } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
import rimraf = require('rimraf')
|
||||
import { readFile, writeFile, stat } from "fs";
|
||||
import { execFile } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import rimraf = require("rimraf");
|
||||
|
||||
const rimrafAsync = promisify(rimraf)
|
||||
export const STDIN_FLAG = '[stdin]'
|
||||
const rimrafAsync = promisify(rimraf);
|
||||
export const STDIN_FLAG = "[stdin]";
|
||||
|
||||
export async function each<T>(
|
||||
list: T[] | Promise<T[]>,
|
||||
action: (item: T, index: number, list: T[]) => Promise<any>
|
||||
) {
|
||||
const l = await list
|
||||
return Promise.all(l.map(action))
|
||||
const l = await list;
|
||||
return Promise.all(l.map(action));
|
||||
}
|
||||
|
||||
export function wrap(code: string) {
|
||||
return '!(function () {' + code + '})();'
|
||||
return "!(function () {" + code + "})();";
|
||||
}
|
||||
|
||||
function falseOnEnoent(e: any) {
|
||||
if (e.code === 'ENOENT') {
|
||||
return false
|
||||
if (e.code === "ENOENT") {
|
||||
return false;
|
||||
}
|
||||
throw e
|
||||
throw e;
|
||||
}
|
||||
|
||||
function padRight(str: string, l: number) {
|
||||
return (str + ' '.repeat(l)).slice(0, l)
|
||||
return (str + " ".repeat(l)).slice(0, l);
|
||||
}
|
||||
|
||||
const bound: MethodDecorator = function bound<T>(
|
||||
target: Object,
|
||||
propertyKey: string | Symbol,
|
||||
propertyKey: string | symbol,
|
||||
descriptor: TypedPropertyDescriptor<T>
|
||||
) {
|
||||
const configurable = true
|
||||
const configurable = true;
|
||||
return {
|
||||
configurable,
|
||||
get(this: T) {
|
||||
const value = (descriptor.value as any).bind(this)
|
||||
const value = (descriptor.value as any).bind(this);
|
||||
Object.defineProperty(this, propertyKey as string, {
|
||||
configurable,
|
||||
value,
|
||||
writable: true,
|
||||
})
|
||||
return value
|
||||
});
|
||||
return value;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function dequote(input: string) {
|
||||
input = input.trim()
|
||||
const singleQuote = input.startsWith("'") && input.endsWith("'")
|
||||
const doubleQuote = input.startsWith('"') && input.endsWith('"')
|
||||
input = input.trim();
|
||||
const singleQuote = input.startsWith("'") && input.endsWith("'");
|
||||
const doubleQuote = input.startsWith('"') && input.endsWith('"');
|
||||
if (singleQuote || doubleQuote) {
|
||||
return input.slice(1).slice(0, -1)
|
||||
return input.slice(1).slice(0, -1);
|
||||
}
|
||||
return input
|
||||
return input;
|
||||
}
|
||||
|
||||
export interface ReadFileAsync {
|
||||
(path: string): Promise<Buffer>
|
||||
(path: string, encoding: string): Promise<string>
|
||||
(path: string): Promise<Buffer>;
|
||||
(path: string, encoding: string): Promise<string>;
|
||||
}
|
||||
|
||||
const readFileAsync = promisify(readFile)
|
||||
const writeFileAsync = promisify(writeFile)
|
||||
const statAsync = promisify(stat)
|
||||
const execFileAsync = promisify(execFile)
|
||||
const isWindows = process.platform === 'win32'
|
||||
const readFileAsync = promisify(readFile);
|
||||
const writeFileAsync = promisify(writeFile);
|
||||
const statAsync = promisify(stat);
|
||||
const execFileAsync = promisify(execFile);
|
||||
const isWindows = process.platform === "win32";
|
||||
|
||||
function pathExistsAsync(path: string) {
|
||||
return statAsync(path)
|
||||
.then((x) => true)
|
||||
.catch(falseOnEnoent)
|
||||
.catch(falseOnEnoent);
|
||||
}
|
||||
|
||||
function isDirectoryAsync(path: string) {
|
||||
return statAsync(path)
|
||||
.then((x) => x.isDirectory())
|
||||
.catch(falseOnEnoent)
|
||||
.catch(falseOnEnoent);
|
||||
}
|
||||
/**
|
||||
* @param version See if this version is greather than the second one
|
||||
* @param operand Version to compare against
|
||||
*/
|
||||
function semverGt(version: string, operand: string) {
|
||||
const [cMajor, cMinor, cPatch] = version.split('.').map(Number)
|
||||
let [major, minor, patch] = operand.split('.').map(Number)
|
||||
if (!minor) minor = 0
|
||||
if (!patch) patch = 0
|
||||
const [cMajor, cMinor, cPatch] = version.split(".").map(Number);
|
||||
let [major, minor, patch] = operand.split(".").map(Number);
|
||||
if (!minor) minor = 0;
|
||||
if (!patch) patch = 0;
|
||||
return (
|
||||
cMajor > major ||
|
||||
(cMajor === major && cMinor > minor) ||
|
||||
(cMajor === major && cMinor === minor && cPatch > patch)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -110,4 +110,4 @@ export {
|
||||
pathExistsAsync,
|
||||
isDirectoryAsync,
|
||||
writeFileAsync,
|
||||
}
|
||||
};
|
||||
|
||||
+87
-72
@@ -1,125 +1,140 @@
|
||||
import * as nexe from '../lib/nexe'
|
||||
import { getUnBuiltReleases, getLatestGitRelease } from '../lib/releases'
|
||||
import { runDockerBuild } from './docker'
|
||||
import { getTarget, targetsEqual, NexeTarget } from '../lib/target'
|
||||
import { pathExistsAsync, readFileAsync, execFileAsync, semverGt } from '../lib/util'
|
||||
import axios from 'axios'
|
||||
import FormData from 'form-data'
|
||||
import { cpus } from 'os'
|
||||
import * as nexe from "../lib/nexe";
|
||||
import { getUnBuiltReleases, getLatestGitRelease } from "../lib/releases";
|
||||
import { runDockerBuild } from "./docker";
|
||||
import { getTarget, targetsEqual, NexeTarget } from "../lib/target";
|
||||
import {
|
||||
pathExistsAsync,
|
||||
readFileAsync,
|
||||
execFileAsync,
|
||||
semverGt,
|
||||
} from "../lib/util";
|
||||
import axios from "axios";
|
||||
import FormData from "form-data";
|
||||
import { cpus } from "os";
|
||||
|
||||
const env = process.env,
|
||||
isPullRequest = env.BUILD_REASON === 'PullRequest',
|
||||
isWindows = process.platform === 'win32',
|
||||
isLinux = process.platform === 'linux',
|
||||
buildHost = env.AGENT_JOBNAME || (isWindows && 'windows_2017_2015') || '',
|
||||
isMac = process.platform === 'darwin',
|
||||
isPullRequest = env.BUILD_REASON === "PullRequest",
|
||||
isWindows = process.platform === "win32",
|
||||
isLinux = process.platform === "linux",
|
||||
buildHost = env.AGENT_JOBNAME || (isWindows && "windows_2017_2015") || "",
|
||||
isMac = process.platform === "darwin",
|
||||
headers = {
|
||||
Authorization: 'token ' + env.GITHUB_TOKEN,
|
||||
'User-Agent': 'nexe (https://www.npmjs.com/package/nexe)',
|
||||
}
|
||||
Authorization: "token " + env.GITHUB_TOKEN,
|
||||
"User-Agent": "nexe (https://www.npmjs.com/package/nexe)",
|
||||
};
|
||||
|
||||
if (require.main === module) {
|
||||
if (!isPullRequest) {
|
||||
build().catch((x) => {
|
||||
console.error(x)
|
||||
process.exit(1)
|
||||
})
|
||||
console.error(x);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function build() {
|
||||
const releases = await getUnBuiltReleases({ headers })
|
||||
const releases = await getUnBuiltReleases({ headers });
|
||||
if (!releases.length) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const windowsBuild = releases.find((x) => x.platform === 'windows'),
|
||||
macBuild = releases.find((x) => x.platform === 'mac'),
|
||||
linux = releases.find((x) => x.platform === 'linux'),
|
||||
alpine = releases.find((x) => x.platform === 'alpine')
|
||||
const windowsBuild = releases.find((x) => x.platform === "windows"),
|
||||
macBuild = releases.find((x) => x.platform === "mac"),
|
||||
linux = releases.find((x) => x.platform === "linux"),
|
||||
alpine = releases.find((x) => x.platform === "alpine");
|
||||
|
||||
let target: NexeTarget | undefined
|
||||
let target: NexeTarget | undefined;
|
||||
|
||||
if (env.NEXE_VERSION) target = getTarget(env.NEXE_VERSION)
|
||||
else if (isWindows) target = windowsBuild
|
||||
else if (isMac) target = macBuild
|
||||
else if (isLinux) target = linux
|
||||
if (buildHost.includes('alpine')) target = alpine
|
||||
if (env.NEXE_VERSION) target = getTarget(env.NEXE_VERSION);
|
||||
else if (isWindows) target = windowsBuild;
|
||||
else if (isMac) target = macBuild;
|
||||
else if (isLinux) target = linux;
|
||||
if (buildHost.includes("alpine")) target = alpine;
|
||||
|
||||
if (!target) {
|
||||
return console.log('Nothing to build...')
|
||||
return console.log("Nothing to build...");
|
||||
}
|
||||
|
||||
if (isWindows && buildHost.includes('2017') && !semverGt(target.version, '9.99.99')) {
|
||||
return console.log(`Not building ${target} on this host...`)
|
||||
if (
|
||||
isWindows &&
|
||||
buildHost.includes("2017") &&
|
||||
!semverGt(target.version, "9.99.99")
|
||||
) {
|
||||
return console.log(`Not building ${target} on this host...`);
|
||||
}
|
||||
if (isWindows && buildHost.includes('2015') && semverGt(target.version, '9.99.99')) {
|
||||
return console.log(`Not building ${target} on this host...`)
|
||||
if (
|
||||
isWindows &&
|
||||
buildHost.includes("2015") &&
|
||||
semverGt(target.version, "9.99.99")
|
||||
) {
|
||||
return console.log(`Not building ${target} on this host...`);
|
||||
}
|
||||
|
||||
const output = isWindows ? './out.exe' : './out',
|
||||
const output = isWindows ? "./out.exe" : "./out",
|
||||
options = {
|
||||
mangle: false,
|
||||
build: true,
|
||||
verbose: Boolean(env.NEXE_VERBOSE!),
|
||||
target,
|
||||
make: ['-j' + cpus().length],
|
||||
make: ["-j" + cpus().length],
|
||||
output,
|
||||
}
|
||||
console.log('Building: ' + target + ' on ' + buildHost)
|
||||
const stop = keepalive()
|
||||
};
|
||||
console.log("Building: " + target + " on " + buildHost);
|
||||
const stop = keepalive();
|
||||
|
||||
if (
|
||||
[/*'arm7l', 'arm6l', 'arm64', */ 'alpine'].includes(target.platform) &&
|
||||
buildHost.includes('alpine')
|
||||
[/*'arm7l', 'arm6l', 'arm64', */ "alpine"].includes(target.platform) &&
|
||||
buildHost.includes("alpine")
|
||||
) {
|
||||
await runDockerBuild(target)
|
||||
await runDockerBuild(target);
|
||||
} else {
|
||||
await nexe.compile(options)
|
||||
await nexe.compile(options);
|
||||
}
|
||||
stop()
|
||||
stop();
|
||||
if (await pathExistsAsync(output)) {
|
||||
await assertNexeBinary(output)
|
||||
await assertNexeBinary(output);
|
||||
const gitRelease = await getLatestGitRelease({ headers }),
|
||||
unbuiltReleases = await getUnBuiltReleases({ headers })
|
||||
unbuiltReleases = await getUnBuiltReleases({ headers });
|
||||
if (!unbuiltReleases.some((x) => targetsEqual(x, target!))) {
|
||||
console.log(`${target} already uploaded.`)
|
||||
process.exit(0)
|
||||
return
|
||||
console.log(`${target} already uploaded.`);
|
||||
process.exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create form data for file upload
|
||||
const formData = new FormData();
|
||||
formData.append('file', await readFileAsync(output), {
|
||||
formData.append("file", await readFileAsync(output), {
|
||||
filename: target.toString(),
|
||||
contentType: 'application/octet-stream'
|
||||
contentType: "application/octet-stream",
|
||||
});
|
||||
|
||||
const uploadUrl = gitRelease.upload_url.split('{')[0];
|
||||
await axios.post(uploadUrl, formData, {
|
||||
params: { name: target.toString() },
|
||||
headers: {
|
||||
...headers,
|
||||
...formData.getHeaders(),
|
||||
},
|
||||
}).catch((reason) => {
|
||||
console.log(reason && reason.response && reason.response.data)
|
||||
throw reason
|
||||
})
|
||||
console.log(target + ' uploaded.')
|
||||
process.exit(0)
|
||||
const uploadUrl = gitRelease.upload_url.split("{")[0];
|
||||
await axios
|
||||
.post(uploadUrl, formData, {
|
||||
params: { name: target.toString() },
|
||||
headers: {
|
||||
...headers,
|
||||
...formData.getHeaders(),
|
||||
},
|
||||
})
|
||||
.catch((reason) => {
|
||||
console.log(reason && reason.response && reason.response.data);
|
||||
throw reason;
|
||||
});
|
||||
console.log(target + " uploaded.");
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
function keepalive() {
|
||||
const keepalive = setInterval(() => console.log('Building...'), 300 * 1000)
|
||||
return () => clearInterval(keepalive)
|
||||
const keepalive = setInterval(() => console.log("Building..."), 300 * 1000);
|
||||
return () => clearInterval(keepalive);
|
||||
}
|
||||
|
||||
function assertNexeBinary(file: string) {
|
||||
return execFileAsync(file).catch((e) => {
|
||||
if (e && e.stack && e.stack.includes('Invalid Nexe binary')) {
|
||||
return
|
||||
if (e && e.stack && e.stack.includes("Invalid Nexe binary")) {
|
||||
return;
|
||||
}
|
||||
throw e
|
||||
})
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
+29
-27
@@ -1,12 +1,12 @@
|
||||
import { NexeTarget, architectures } from '../lib/target'
|
||||
import { writeFileAsync, readFileAsync } from '../lib/util'
|
||||
import axios from 'axios'
|
||||
import execa = require('execa')
|
||||
import { appendFileSync } from 'fs'
|
||||
import { NexeTarget, architectures } from "../lib/target";
|
||||
import { writeFileAsync, readFileAsync } from "../lib/util";
|
||||
import axios from "axios";
|
||||
import execa = require("execa");
|
||||
import { appendFileSync } from "fs";
|
||||
|
||||
function alpine(target: NexeTarget) {
|
||||
return `
|
||||
FROM ${target.arch === 'x64' ? '' : 'i386/'}alpine:3.12
|
||||
FROM ${target.arch === "x64" ? "" : "i386/"}alpine:3.12
|
||||
RUN apk add --no-cache curl make gcc g++ binutils-gold python linux-headers paxctl libgcc libstdc++ git vim tar gzip wget
|
||||
ENV NODE_VERSION=${target.version}
|
||||
ENV NEXE_VERSION=latest
|
||||
@@ -23,7 +23,7 @@ RUN rm /nexe_temp/\${NODE_VERSION}/out/Release/node && \
|
||||
npm install -g nexe@\${NEXE_VERSION}
|
||||
RUN echo "console.log('hello world')" >> index.js && \
|
||||
nexe --build --no-mangle --temp /nexe_temp -c="--fully-static" -o out
|
||||
`.trim()
|
||||
`.trim();
|
||||
}
|
||||
|
||||
function arm(target: NexeTarget) {
|
||||
@@ -34,30 +34,32 @@ WORKDIR /
|
||||
|
||||
RUN yarn global add nexe@\${NEXE_VERSION} && \
|
||||
nexe --build --no-mangle -o out -t ${target.version}
|
||||
`.trim()
|
||||
`.trim();
|
||||
}
|
||||
|
||||
export async function runDockerBuild(target: NexeTarget) {
|
||||
//todo switch on alpine and arm
|
||||
const dockerfile = alpine(target)
|
||||
await writeFileAsync('Dockerfile', dockerfile)
|
||||
const outFilename = 'nexe-docker-build-log.txt'
|
||||
await writeFileAsync(outFilename, '')
|
||||
let output: any = []
|
||||
const dockerfile = alpine(target);
|
||||
await writeFileAsync("Dockerfile", dockerfile);
|
||||
const outFilename = "nexe-docker-build-log.txt";
|
||||
await writeFileAsync(outFilename, "");
|
||||
const output: any = [];
|
||||
|
||||
try {
|
||||
output.push(await execa(`docker build -t nexe-docker .`, { shell: true }))
|
||||
output.push(await execa(`docker run -d --name nexe nexe-docker sh`, { shell: true }))
|
||||
output.push(await execa(`docker cp nexe:/out out`, { shell: true }))
|
||||
output.push(await execa(`docker rm nexe`, { shell: true }))
|
||||
output.push(await execa(`docker build -t nexe-docker .`, { shell: true }));
|
||||
output.push(
|
||||
await execa(`docker run -d --name nexe nexe-docker sh`, { shell: true })
|
||||
);
|
||||
output.push(await execa(`docker cp nexe:/out out`, { shell: true }));
|
||||
output.push(await execa(`docker rm nexe`, { shell: true }));
|
||||
} catch (e: any) {
|
||||
console.log('Error running docker')
|
||||
appendFileSync(outFilename, e.message)
|
||||
console.log("Error running docker");
|
||||
appendFileSync(outFilename, e.message);
|
||||
} finally {
|
||||
output.forEach((x: any) => {
|
||||
appendFileSync(outFilename, x.stderr)
|
||||
appendFileSync(outFilename, x.stdout)
|
||||
})
|
||||
appendFileSync(outFilename, x.stderr);
|
||||
appendFileSync(outFilename, x.stdout);
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await axios.put(
|
||||
@@ -65,13 +67,13 @@ export async function runDockerBuild(target: NexeTarget) {
|
||||
await readFileAsync(outFilename),
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
}
|
||||
"Content-Type": "application/octet-stream",
|
||||
},
|
||||
}
|
||||
)
|
||||
console.log('Posted docker log: ', response.data)
|
||||
);
|
||||
console.log("Posted docker log: ", response.data);
|
||||
} catch (e: any) {
|
||||
console.log('Error posting log', e)
|
||||
console.log("Error posting log", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-16
@@ -1,31 +1,31 @@
|
||||
import { writeFileSync, readFileSync } from 'fs'
|
||||
import { template } from 'lodash'
|
||||
import { writeFileSync, readFileSync } from "fs";
|
||||
import { template } from "lodash";
|
||||
|
||||
/**
|
||||
* post build step to insert code files into code files.
|
||||
* And the package.json version.
|
||||
*/
|
||||
cp('src/fs/package.json', 'lib/fs/package.json')
|
||||
cp('src/fs/bootstrap.js', 'lib/fs/bootstrap.js')
|
||||
cp('src/fs/README.md', 'lib/fs/README.md')
|
||||
cp("src/fs/package.json", "lib/fs/package.json");
|
||||
cp("src/fs/bootstrap.js", "lib/fs/bootstrap.js");
|
||||
cp("src/fs/README.md", "lib/fs/README.md");
|
||||
|
||||
inject('lib/patches/third-party-main.js')
|
||||
inject('lib/steps/shim.js')
|
||||
inject('lib/options.js', JSON.stringify(require('../package.json').version))
|
||||
inject("lib/patches/third-party-main.js");
|
||||
inject("lib/steps/shim.js");
|
||||
inject("lib/options.js", JSON.stringify(require("../package.json").version));
|
||||
|
||||
function inject(filename: string, version?: string) {
|
||||
const contents = template(readFileSync(filename, 'utf8'), {
|
||||
const contents = template(readFileSync(filename, "utf8"), {
|
||||
interpolate: /'\{\{([\s\S]*?)\}\}'/,
|
||||
})({
|
||||
file: (path: string) => JSON.stringify(readFileSync(path, 'utf8')),
|
||||
file: (path: string) => JSON.stringify(readFileSync(path, "utf8")),
|
||||
version,
|
||||
})
|
||||
writeFileSync(filename, contents)
|
||||
console.log(`Wrote: ${filename}`)
|
||||
});
|
||||
writeFileSync(filename, contents);
|
||||
console.log(`Wrote: ${filename}`);
|
||||
}
|
||||
|
||||
function cp(from: string, to: string) {
|
||||
const file = readFileSync(from)
|
||||
writeFileSync(to, file)
|
||||
console.log('Copied: ', from, 'To: ', to)
|
||||
const file = readFileSync(from);
|
||||
writeFileSync(to, file);
|
||||
console.log("Copied: ", from, "To: ", to);
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"rulesDirectory": ["tslint-plugin-prettier", "tslint-config-prettier"],
|
||||
"linterOptions": {
|
||||
"exclude": ["**/node_modules/**/*"]
|
||||
},
|
||||
"rules": {
|
||||
"prettier": [true, { "semi": false, "printWidth": 100, "singleQuote": true }]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user