Update to eslint

This commit is contained in:
oxmc
2025-12-07 01:30:14 -08:00
parent 5522ecde39
commit 4072dc6f0a
36 changed files with 2696 additions and 1554 deletions
+33
View File
@@ -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"]
}
+1092 -236
View File
File diff suppressed because it is too large Load Diff
+6 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
);
+12 -9
View File
@@ -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();
}
+41 -30
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
+68 -63
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+13 -13
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
-9
View File
@@ -1,9 +0,0 @@
{
"rulesDirectory": ["tslint-plugin-prettier", "tslint-config-prettier"],
"linterOptions": {
"exclude": ["**/node_modules/**/*"]
},
"rules": {
"prettier": [true, { "semi": false, "printWidth": 100, "singleQuote": true }]
}
}