Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ad7346afb2 | |||
| 0b0ac7109c | |||
| d361ed0c34 |
@@ -0,0 +1,4 @@
|
||||
FROM node:fermium-alpine3.10
|
||||
RUN apk add --no-cache build-base linux-headers bash clang python3
|
||||
SHELL ["/bin/bash", "--login", "-c"]
|
||||
CMD ["bash"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# action.yml
|
||||
name: 'Hello World'
|
||||
description: 'Greet someone and record the time'
|
||||
inputs:
|
||||
who-to-greet: # id of input
|
||||
description: 'Who to greet'
|
||||
required: true
|
||||
default: 'World'
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we greeted you'
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
args:
|
||||
- ${{ inputs.who-to-greet }}
|
||||
@@ -0,0 +1,23 @@
|
||||
FROM centos:7
|
||||
|
||||
RUN yum install -y which centos-release-scl && \
|
||||
yum install -y devtoolset-9-gcc \
|
||||
devtoolset-9-gcc-c++ \
|
||||
make \
|
||||
wget \
|
||||
python3 && \
|
||||
echo "source /opt/rh/devtoolset-9/enable" >> /etc/bashrc
|
||||
|
||||
SHELL ["/bin/bash", "--login", "-c"]
|
||||
|
||||
ADD flags.sh flags.sh
|
||||
|
||||
ARG NODE_VERSION=14.20.0
|
||||
|
||||
RUN wget https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}.tar.gz -O node.tar.gz && \
|
||||
mkdir node && tar -zxf node.tar.gz -C node --strip-components=1 && \
|
||||
rm node.tar.gz && \
|
||||
cd node && \
|
||||
. ../flags.sh && \
|
||||
./configure $(echo $CONFIGURE_FLAGS) && \
|
||||
make $(echo $MAKE_FLAGS) && make install
|
||||
@@ -0,0 +1,16 @@
|
||||
# action.yml
|
||||
name: 'Hello World'
|
||||
description: 'Greet someone and record the time'
|
||||
inputs:
|
||||
who-to-greet: # id of input
|
||||
description: 'Who to greet'
|
||||
required: true
|
||||
default: 'World'
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we greeted you'
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
args:
|
||||
- ${{ inputs.who-to-greet }}
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
ARCH=$(uname -m)
|
||||
|
||||
case $ARCH in
|
||||
"x86_64")
|
||||
CONFIGURE_FLAGS="--dest-cpu=x64"
|
||||
;;
|
||||
"aarch64")
|
||||
;&
|
||||
"armv7l")
|
||||
CONFIGURE_FLAGS="--openssl-no-asm --dest-cpu=arm --with-arm-float-abi=hard"
|
||||
;;
|
||||
*)
|
||||
CONFIGURE_FLAGS=""
|
||||
;;
|
||||
esac
|
||||
|
||||
MAKE_FLAGS="-j$(getconf _NPROCESSORS_ONLN)"
|
||||
@@ -6,7 +6,6 @@ on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
schedule:
|
||||
# 9AM Central
|
||||
- cron: '0 14 * * *'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
name: Base Builds
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- refactor-old-node
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: ['14.20.0', '16.16.0', '18.5.0']
|
||||
arch: [x64, x86]
|
||||
plat: [arm, macos, windows, linux, alpine]
|
||||
exclude:
|
||||
- plat: arm
|
||||
arch: x86
|
||||
- plat: macos
|
||||
arch: x86
|
||||
include:
|
||||
- plat: arm
|
||||
os: ubuntu-20.04
|
||||
- plat: macos
|
||||
os: macos-12
|
||||
- plat: windows
|
||||
os: windows-2022
|
||||
- plat: linux
|
||||
os: ubuntu-20.04
|
||||
- plat: alpine
|
||||
os: ubuntu-20.04
|
||||
name: 'Target: ${{matrix.plat}}-${{ matrix.arch }}-${{ matrix.version }} on ${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v3.1.2
|
||||
with:
|
||||
python-version: '3.10.4'
|
||||
- name: Setup Nasm
|
||||
if: ${{ matrix.plat == 'windows' }}
|
||||
uses: ilammy/setup-nasm@v1.3.0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.version }}
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: 'Install Dependencies'
|
||||
run: npm ci
|
||||
- name: 'Build nexe base binary'
|
||||
env:
|
||||
NEXE_TARGET: ${{ matrix.plat }}-${{ matrix.arch }}-${{ matrix.version }}
|
||||
run: npx ts-node tasks/build.ts
|
||||
@@ -0,0 +1 @@
|
||||
18.12.1
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"module": {
|
||||
"type": "commonjs",
|
||||
"ignoreDynamic": true
|
||||
|
||||
},
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"dynamicImport": true,
|
||||
"decorators": true
|
||||
},
|
||||
"transform": {
|
||||
"legacyDecorator": true
|
||||
},
|
||||
"target": "es2019",
|
||||
"externalHelpers": false
|
||||
}
|
||||
}
|
||||
Vendored
+4
-1
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"recommendations": [ "ms-vscode.vscode-typescript-tslint-plugin" ]
|
||||
"recommendations": [
|
||||
"esbenp.prettier-vscode",
|
||||
"dbaeumer.vscode-eslint"
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+28
-4
@@ -1,7 +1,4 @@
|
||||
{
|
||||
"tslint.enable": true,
|
||||
"tslint.run": "onType",
|
||||
"tslint.autoFixOnSave": true,
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
@@ -14,5 +11,32 @@
|
||||
"**/node_modules": true,
|
||||
"**/bower_components": true
|
||||
},
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib"
|
||||
"eslint.workingDirectories": [{ "mode": "auto" }],
|
||||
"eslint.format.enable": true,
|
||||
"editor.formatOnSave": true,
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact"
|
||||
],
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2013-2018 Nexe Contributors
|
||||
Copyright (c) 2013-2022 Nexe Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
- Distribute binaries without needing node / npm.
|
||||
- Idempotent builds
|
||||
- Start and deploy faster.
|
||||
- Lockdown specific application versions, and easily rollback.
|
||||
- Lock-down specific application versions, and easily rollback.
|
||||
- Flexible build pipeline
|
||||
- Cross platform builds
|
||||
|
||||
@@ -79,7 +79,6 @@ npm config set python python3.8
|
||||
Where `2019` is the version of Visual Studio you have (if you have it).
|
||||
|
||||
**Notes:**
|
||||
- The above works and has been tested with node.js `14.5.4` and `15.8.0`
|
||||
- Python 3 and Python 2 can coexist and `nexe` will still work, considering the `set config` area above
|
||||
- Don't use `npm install windows-build-tools` unless you're having some type of issue, because the above commands configures and installs the latest/preferred too.
|
||||
|
||||
@@ -117,14 +116,14 @@ compile({
|
||||
- Output executable file path
|
||||
- default: same as `name` with an OS specific extension.
|
||||
- #### `target: string | object`
|
||||
- An object or string describing platform-arch-version. e.g. `'windows-ia32-10.13.0'`
|
||||
- An object or string describing platform-arch-version. e.g. `'windows-ia32-18.1.0'`
|
||||
- each segment is optional, and will be merged with the current environment
|
||||
- Examples: ([full list](https://github.com/nexe/nexe/releases))
|
||||
- `'win32-x86-10.13.0`
|
||||
- `'win32-x86-18.1.0`
|
||||
- `{ platform: 'alpine' }`
|
||||
- `darwin-10.13.0`
|
||||
- `darwin-14.19.1`
|
||||
- `linux-x64`
|
||||
- `macos-10.13.0`
|
||||
- `macos-16.15.0`
|
||||
|
||||
See [test/target.spec.ts](test/target.spec.ts)
|
||||
- If the [`build`](#build-boolean) flag is set, the platform portion of the target is ignored.
|
||||
@@ -171,9 +170,6 @@ compile({
|
||||
- #### `vcBuild: string[]`
|
||||
- Options for windows build
|
||||
- default: `['nosign', 'release']`
|
||||
- #### `snapshot: string`
|
||||
- path to a file to be used as the warmup snapshot for the build
|
||||
- default: `null`
|
||||
- #### `resources: string[]`
|
||||
- Array of globs with files to include in the build
|
||||
- Example: `['./public/**/*']`
|
||||
@@ -182,20 +178,6 @@ compile({
|
||||
- Path to use for storing nexe's build files
|
||||
- Override in the env with `NEXE_TEMP`
|
||||
- default: `~/.nexe`
|
||||
- #### `ico: string`
|
||||
- Path to a user provided icon to be used (Windows only). Requires `--build` to be set.
|
||||
- #### `rc: object`
|
||||
- Settings for patching the [node.rc](https://github.com/nodejs/node/blob/master/src/res/node.rc) configuration file (Windows only).
|
||||
- Example (keys may vary depending on the version. Reference the file linked above):
|
||||
```javascript
|
||||
{
|
||||
CompanyName: "ACME Corp",
|
||||
PRODUCTVERSION: "17,3,0,0",
|
||||
FILEVERSION: "1,2,3,4"
|
||||
...
|
||||
}
|
||||
```
|
||||
- default: `{}`
|
||||
- #### `clean: boolean`
|
||||
- If included, nexe will remove temporary files for the accompanying configuration and exit
|
||||
- #### `enableNodeCli: boolean`
|
||||
@@ -254,14 +236,12 @@ In order to use native modules, the native binaries must be shipped alongside th
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
`Error: Entry file "" not found!` means you need to provide `nexe` with input. Either use `-i` or pipe data to it.
|
||||
|
||||
`Error: https://github.com/nexe/nexe/releases/download/v3.3.3/windows-x64-15.8.0 is not available, create it using the --build flag` or similar message means that it either:
|
||||
- You are having networking issues such as the download being blocked
|
||||
- You should specify the target so `nexe` knows what version of the executable to use.
|
||||
- See the [releases page](https://github.com/nexe/nexe/releases) to find the executable's version number
|
||||
- Example
|
||||
- `nexe -i "app.js" -r "public/**/*.html" -o "dist/myApp.exe" -t x64-14.15.3`
|
||||
- `nexe -i "app.js" -r "public/**/*.html" -o "dist/myApp.exe" -t x64-18.1.0`
|
||||
- where `-i` specifies the input, `-r` specifies resources to embed, `-o` specifies the output, `-t` specifies the target.
|
||||
- Alternatively you can compile the executable yourself, see that section for details
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
schedules:
|
||||
- cron: "0 8 * * *"
|
||||
displayName: "Daily Build 1"
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
- cron: "0 13 * * *"
|
||||
displayName: "Daily Build 2"
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
- cron: "0 19 * * *"
|
||||
displayName: "Daily Build 3"
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
jobs:
|
||||
- job: BaseBuild
|
||||
timeoutInMinutes: 360
|
||||
strategy:
|
||||
matrix:
|
||||
linux:
|
||||
imageName: 'ubuntu-16.04'
|
||||
mac:
|
||||
imageName: 'macos-10.15'
|
||||
windows_2017:
|
||||
imageName: 'vs2017-win2016'
|
||||
windows_2015:
|
||||
imageName: 'vs2017-win2016'
|
||||
alpine:
|
||||
imageName: 'ubuntu-16.04'
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
displayName: Setup Python
|
||||
inputs:
|
||||
versionSpec: '2.7'
|
||||
architecture: 'x64'
|
||||
- task: NodeTool@0
|
||||
displayName: Install Node
|
||||
inputs:
|
||||
versionSpec: '12.x'
|
||||
- script: sudo apt-get update && sudo apt-get install -y libc6-dev-i386 gcc-multilib g++-multilib
|
||||
condition: contains(variables['Agent.JobName'], 'linux')
|
||||
displayName: Install Linux build dependencies
|
||||
- script: npm config set msvs_version 2015
|
||||
condition: contains(variables['Agent.JobName'], 'windows_2015')
|
||||
displayName: Install Windows build dependencies
|
||||
- script: npm install && npm run ci:build
|
||||
displayName: Build Nexe
|
||||
env:
|
||||
GITHUB_TOKEN: $(PersonalGithubToken)
|
||||
NEXE_VERBOSE: $(NEXE_VERBOSE)
|
||||
msbuild_args: /3G
|
||||
condition: not(contains(variables['Agent.JobName'], 'windows'))
|
||||
- powershell: |
|
||||
choco install nasm
|
||||
npm install
|
||||
npm run ci:build
|
||||
displayName: Build Nexe (Windows)
|
||||
condition: contains(variables['Agent.JobName'], 'windows')
|
||||
env:
|
||||
GITHUB_TOKEN: $(PersonalGithubToken)
|
||||
NEXE_VERBOSE: $(NEXE_VERBOSE)
|
||||
- script: npm run test:integration
|
||||
displayName: Test built artefact
|
||||
env:
|
||||
NEXE_VERBOSE: $(NEXE_VERBOSE)
|
||||
BUILD: ''
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM centos:7
|
||||
|
||||
RUN yum install -y which centos-release-scl \
|
||||
&& yum install -y devtoolset-9-gcc \
|
||||
devtoolset-9-gcc-c++ \
|
||||
make \
|
||||
python3 \
|
||||
curl \
|
||||
&& curl -sL https://rpm.nodesource.com/setup_16.x | bash - \
|
||||
&& yum install -y nodejs && npm install nexe -g \
|
||||
&& echo "source /opt/rh/devtoolset-9/enable" >> /etc/bashrc
|
||||
|
||||
SHELL ["/bin/bash", "--login", "-c"]
|
||||
@@ -0,0 +1,4 @@
|
||||
FROM node:16-alpine
|
||||
RUN apk add --no-cache build-base linux-headers bash clang python3
|
||||
SHELL ["/bin/bash", "--login", "-c"]
|
||||
CMD ["bash"]
|
||||
@@ -1,8 +0,0 @@
|
||||
const express = require('express')
|
||||
const path = require('path')
|
||||
const app = express()
|
||||
app.use('/', express.static(path.join(__dirname, 'public')))
|
||||
|
||||
|
||||
app.listen(8888)
|
||||
|
||||
Generated
-373
@@ -1,373 +0,0 @@
|
||||
{
|
||||
"name": "express-app",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"accepts": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
|
||||
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
|
||||
"requires": {
|
||||
"mime-types": "~2.1.18",
|
||||
"negotiator": "0.6.1"
|
||||
}
|
||||
},
|
||||
"array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"body-parser": {
|
||||
"version": "1.18.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
|
||||
"integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
|
||||
"requires": {
|
||||
"bytes": "3.0.0",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.1",
|
||||
"http-errors": "~1.6.2",
|
||||
"iconv-lite": "0.4.19",
|
||||
"on-finished": "~2.3.0",
|
||||
"qs": "6.5.1",
|
||||
"raw-body": "2.3.2",
|
||||
"type-is": "~1.6.15"
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
||||
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
|
||||
},
|
||||
"content-disposition": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
|
||||
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
|
||||
},
|
||||
"content-type": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
||||
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||
},
|
||||
"destroy": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
||||
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||
},
|
||||
"encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||
},
|
||||
"etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||
},
|
||||
"express": {
|
||||
"version": "4.16.3",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz",
|
||||
"integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
|
||||
"requires": {
|
||||
"accepts": "~1.3.5",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.18.2",
|
||||
"content-disposition": "0.5.2",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.3.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.1.1",
|
||||
"fresh": "0.5.2",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.2",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"proxy-addr": "~2.0.3",
|
||||
"qs": "6.5.1",
|
||||
"range-parser": "~1.2.0",
|
||||
"safe-buffer": "5.1.1",
|
||||
"send": "0.16.2",
|
||||
"serve-static": "1.13.2",
|
||||
"setprototypeof": "1.1.0",
|
||||
"statuses": "~1.4.0",
|
||||
"type-is": "~1.6.16",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
}
|
||||
},
|
||||
"finalhandler": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
|
||||
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
|
||||
"requires": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.2",
|
||||
"statuses": "~1.4.0",
|
||||
"unpipe": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||
},
|
||||
"fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
|
||||
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
|
||||
"requires": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.3",
|
||||
"setprototypeof": "1.1.0",
|
||||
"statuses": ">= 1.4.0 < 2"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.19",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
|
||||
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
|
||||
"integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||
},
|
||||
"merge-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||
},
|
||||
"methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
|
||||
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.35.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz",
|
||||
"integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.19",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz",
|
||||
"integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==",
|
||||
"requires": {
|
||||
"mime-db": "~1.35.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
|
||||
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||
"requires": {
|
||||
"ee-first": "1.1.1"
|
||||
}
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
|
||||
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
|
||||
"integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
|
||||
"requires": {
|
||||
"forwarded": "~0.1.2",
|
||||
"ipaddr.js": "1.8.0"
|
||||
}
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
|
||||
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
|
||||
},
|
||||
"range-parser": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
|
||||
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
|
||||
"integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
|
||||
"requires": {
|
||||
"bytes": "3.0.0",
|
||||
"http-errors": "1.6.2",
|
||||
"iconv-lite": "0.4.19",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"depd": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
|
||||
"integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
|
||||
"integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
|
||||
"requires": {
|
||||
"depd": "1.1.1",
|
||||
"inherits": "2.0.3",
|
||||
"setprototypeof": "1.0.3",
|
||||
"statuses": ">= 1.3.1 < 2"
|
||||
}
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
|
||||
"integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
|
||||
}
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
||||
},
|
||||
"send": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
|
||||
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
|
||||
"requires": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"destroy": "~1.0.4",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "~1.6.2",
|
||||
"mime": "1.4.1",
|
||||
"ms": "2.0.0",
|
||||
"on-finished": "~2.3.0",
|
||||
"range-parser": "~1.2.0",
|
||||
"statuses": "~1.4.0"
|
||||
}
|
||||
},
|
||||
"serve-static": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
|
||||
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
|
||||
"requires": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.2",
|
||||
"send": "0.16.2"
|
||||
}
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
|
||||
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
|
||||
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
|
||||
},
|
||||
"type-is": {
|
||||
"version": "1.6.16",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
|
||||
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
|
||||
"requires": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.18"
|
||||
}
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
||||
},
|
||||
"utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "express-app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.16.3"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<h1>Hello World!</h1>
|
||||
@@ -1,32 +1,45 @@
|
||||
#!/usr/bin/env node
|
||||
const options = require('./lib/options')
|
||||
if (require.main === module) {
|
||||
//fast path for help/version
|
||||
const argv = options.argv
|
||||
const eol = require('os').EOL
|
||||
const showHelp = argv.help || argv._.some(x => x === 'help')
|
||||
const showVersion = argv.version || argv._.some(x => x === 'version')
|
||||
if (showHelp || showVersion) {
|
||||
process.stderr.write(showHelp ? options.help : options.version + eol)
|
||||
} else {
|
||||
const nexe = require('./lib/nexe')
|
||||
nexe.compile(argv).catch((error) => {
|
||||
const NexeError = require('./lib/compiler').NexeError
|
||||
const chalk = require('chalk')
|
||||
const isSilent = Boolean(argv.silent === true || argv.loglevel === 'silent')
|
||||
if (!isSilent) {
|
||||
if (error instanceof NexeError) {
|
||||
process.stderr.write(eol + chalk.red('Error: ') + error.message + eol
|
||||
+ eol + 'See nexe -h for usage..' + eol + eol
|
||||
)
|
||||
} else {
|
||||
process.stderr.write(error.stack + eol)
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
/* eslint-disable no-undef */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
|
||||
const eol = require('os').EOL
|
||||
async function main() {
|
||||
const util = require('./lib/cjs/util.js')
|
||||
await util.initEsm()
|
||||
const color = util.color,
|
||||
options = require('./lib/cjs/options.js'),
|
||||
nexe = require('./lib/cjs/nexe.js'),
|
||||
NexeError = require('./lib/cjs/compiler.js').NexeError,
|
||||
del = eol + eol,
|
||||
argv = options.argv,
|
||||
showHelp = argv.help || argv._.some((x) => x === 'help'),
|
||||
showVersion = argv.version || argv._.some((x) => x === 'version')
|
||||
|
||||
try {
|
||||
if (!(showHelp || showVersion)) {
|
||||
await nexe.compile(argv)
|
||||
} else {
|
||||
process.stderr.write(showHelp ? options.help() : options.version + eol)
|
||||
}
|
||||
} catch (error) {
|
||||
if (argv.loglevel !== 'silent') {
|
||||
if (error instanceof NexeError) {
|
||||
process.stderr.write(
|
||||
`${eol}${color('red', 'Error: ')}${error.message}${del}See nexe -h for usage..${del}`
|
||||
)
|
||||
} else {
|
||||
process.stderr.write(error.stack + eol)
|
||||
}
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
} else {
|
||||
module.exports = require('./lib/nexe')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main()
|
||||
} else {
|
||||
process.stderr.write('Cannot require(..) the nexe cli')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
Generated
+5043
-5887
File diff suppressed because it is too large
Load Diff
+101
-71
@@ -2,93 +2,123 @@
|
||||
"name": "nexe",
|
||||
"description": "Create a single executable out of your Node.js application",
|
||||
"license": "MIT",
|
||||
"version": "5.0.0-beta.1",
|
||||
"contributors": [
|
||||
"Craig Condon <craig.j.condon@gmail.com> (http://crcn.io)",
|
||||
"Jared Allard <jaredallard@outlook.com>",
|
||||
"Caleb Boyd <caleb.boyd@hotmail.com>"
|
||||
],
|
||||
"scripts": {
|
||||
"ci:build": "ts-node tasks/build",
|
||||
"test": "mocha",
|
||||
"test:integration": "node index.js --target 14.15.3 -i test/integration/index.js -o integration-tests --verbose -r test/integration -r node_modules/ && npm run test:integration:run",
|
||||
"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",
|
||||
"prepare": "npm run lint && npm run build && npm test",
|
||||
"prebuild": "rimraf lib",
|
||||
"build": "tsc --declaration && tsc -p tasks && webpack",
|
||||
"postbuild": "ts-node tasks/post-build"
|
||||
"type": "commonjs",
|
||||
"version": "4.0.0-rc.local",
|
||||
"bin": {
|
||||
"nexe": "./index.js"
|
||||
},
|
||||
"types": "./lib/types/nexe.d.ts",
|
||||
"main": "./lib/cjs/nexe.js",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"types": "./lib/types/nexe.d.ts",
|
||||
"node": {
|
||||
"import": "./lib/esm/nexe.js",
|
||||
"require": "./lib/cjs/nexe.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/nexe/nexe.git"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
"lib",
|
||||
"index.js"
|
||||
],
|
||||
"typings": "lib/nexe.d.ts",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"nexe": "index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"mocha": {
|
||||
"spec": "./test/**/*.spec.ts",
|
||||
"checkLeaks": true,
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
]
|
||||
"scripts": {
|
||||
"lint": "tsc --noEmit && eslint \"{src,tasks,test}/**/*.ts\" --fix",
|
||||
"test": "vitest run && node test/exports.mjs",
|
||||
"prepublishOnly": "npm run lint && npm run build && npm test",
|
||||
"build": "npm run cjs:build & npm run esm:build",
|
||||
"prebuild": "rm -rf lib",
|
||||
"cjs:build": "swc src -d lib/cjs --copy-files",
|
||||
"esm:build": "swc src -d lib/esm --copy-files -C module.type=es6",
|
||||
"postbuild": "node -p \"JSON.stringify({type:'module'})\" > lib/esm/package.json && tsc --emitDeclarationOnly && ts-node tasks/post-build"
|
||||
},
|
||||
"contributors": [
|
||||
"Craig Condon <craig.j.condon@gmail.com> (http://crcn.io)",
|
||||
"Jared Allard <jaredallard@outlook.com>",
|
||||
"Caleb Boyd <caleb.boyd@hotmail.com>"
|
||||
],
|
||||
"dependencies": {
|
||||
"@yarnpkg/fslib": "^2.6.0-rc.8",
|
||||
"@yarnpkg/libzip": "^2.2.2",
|
||||
"app-builder": "^7.0.4",
|
||||
"archiver": "^5.3.1",
|
||||
"caw": "^2.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"chalk": "^5.2.0",
|
||||
"download": "^8.0.0",
|
||||
"globby": "^11.0.2",
|
||||
"got": "^11.8.2",
|
||||
"fast-glob": "^3.2.12",
|
||||
"got": "^12.5.3",
|
||||
"meriyah": "^4.3.3",
|
||||
"minimist": "^1.2.6",
|
||||
"mkdirp": "^1.0.4",
|
||||
"minimist": "^1.2.7",
|
||||
"multistream": "^4.1.0",
|
||||
"ora": "^3.4.0",
|
||||
"resolve-dependencies": "^6.0.8",
|
||||
"rimraf": "^3.0.2",
|
||||
"run-script-os": "^1.1.6",
|
||||
"webpack-config-prefabs": "0.0.5"
|
||||
"ora": "^6.1.2",
|
||||
"resolve-dependencies": "^6.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/archiver": "^5.3.2",
|
||||
"@types/chai": "^4",
|
||||
"@types/download": "^6",
|
||||
"@types/globby": "^9",
|
||||
"@types/got": "^9.6.12",
|
||||
"@types/highland": "^2.12.14",
|
||||
"@types/lodash": "^4.14.173",
|
||||
"@swc/cli": "^0.1.57",
|
||||
"@swc/core": "^1.3.24",
|
||||
"@types/minimist": "^1.2.2",
|
||||
"@types/mkdirp": "^1.0.2",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/multistream": "^4.1.0",
|
||||
"@types/node": "^16.10.2",
|
||||
"@types/ora": "^3.2.0",
|
||||
"@types/rimraf": "3.0.2",
|
||||
"@types/semver": "^7.3.8",
|
||||
"chai": "^4.3.6",
|
||||
"execa": "^5.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mocha": "^10.0.0",
|
||||
"prettier": "^2.6.2",
|
||||
"ts-node": "^10.7.0",
|
||||
"tslint": "^6.1.1",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"tslint-plugin-prettier": "^2.3.0",
|
||||
"typescript": "^4.6.4",
|
||||
"webpack-cli": "^4.8.0"
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.0",
|
||||
"@typescript-eslint/parser": "^5.47.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-prettier": "~8.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"prettier": "^2.8.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.4",
|
||||
"vitest": "^0.26.2"
|
||||
},
|
||||
"prettier": {
|
||||
"parser": "typescript",
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100
|
||||
},
|
||||
"eslintConfig": {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"import",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/consistent-type-imports": [
|
||||
2,
|
||||
{
|
||||
"prefer": "type-imports",
|
||||
"disallowTypeAnnotations": false
|
||||
}
|
||||
],
|
||||
"import/extensions": [
|
||||
2,
|
||||
"ignorePackages"
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
2,
|
||||
{
|
||||
"args": "after-used",
|
||||
"ignoreRestSiblings": true,
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-use-before-define": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"no-warning-comments": 1,
|
||||
"one-var": [
|
||||
"error",
|
||||
"consecutive"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+80
-116
@@ -1,30 +1,22 @@
|
||||
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,
|
||||
pathExistsAsync,
|
||||
statAsync,
|
||||
dequote,
|
||||
isWindows,
|
||||
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'
|
||||
import { resolve, normalize, join } from 'node:path'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import type { ReadStream } from 'node:fs'
|
||||
import { createReadStream } from 'node:fs'
|
||||
import { spawn } from 'node:child_process'
|
||||
|
||||
const isBsd = Boolean(~process.platform.indexOf('bsd'))
|
||||
const make = isWindows ? 'vcbuild.bat' : isBsd ? 'gmake' : 'make'
|
||||
const configure = isWindows ? 'configure' : './configure'
|
||||
import type { File } from 'resolve-dependencies'
|
||||
import MultiStream from 'multistream'
|
||||
|
||||
import type { LogStep } from './logger.js'
|
||||
import { Logger } from './logger.js'
|
||||
import { readFile, writeFile, pathExists, isWindows, bound, wrap } from './util.js'
|
||||
import { version } from './options.js'
|
||||
import type { NexeOptions } from './options.js'
|
||||
import type { NexeTarget } from './target.js'
|
||||
import { Bundle, toStream } from './fs/bundle.js'
|
||||
|
||||
const isBsd = Boolean(~process.platform.indexOf('bsd')),
|
||||
make = isWindows ? 'vcbuild.bat' : isBsd ? 'gmake' : 'make'
|
||||
|
||||
type StringReplacer = (match: string) => string
|
||||
|
||||
@@ -47,7 +39,7 @@ export class NexeCompiler {
|
||||
/**
|
||||
* Epoch of when compilation started
|
||||
*/
|
||||
private start = Date.now()
|
||||
private readonly start = Date.now()
|
||||
private compileStep: LogStep | undefined
|
||||
public log = new Logger(this.options.loglevel)
|
||||
/**
|
||||
@@ -73,7 +65,7 @@ export class NexeCompiler {
|
||||
/**
|
||||
* The last shim (defaults to "require('module').runMain()")
|
||||
*/
|
||||
public startup: string = ''
|
||||
public startup = ''
|
||||
/**
|
||||
* The main entrypoint filename for your application - eg. node mainFile.js
|
||||
*/
|
||||
@@ -94,10 +86,6 @@ export class NexeCompiler {
|
||||
* Flag to indicate whether or notstdin was used for input
|
||||
*/
|
||||
public stdinUsed = false
|
||||
/**
|
||||
* Path to the configure script
|
||||
*/
|
||||
public configureScript: string
|
||||
/**
|
||||
* The file path of node binary
|
||||
*/
|
||||
@@ -109,37 +97,29 @@ export class NexeCompiler {
|
||||
|
||||
constructor(public options: NexeOptions) {
|
||||
const { python } = (this.options = options)
|
||||
//SOMEDAY iterate over multiple targets with `--outDir`
|
||||
// SOMEDAY iterate over multiple targets with `--outDir`
|
||||
this.targets = options.targets as NexeTarget[]
|
||||
this.target = this.targets[0]
|
||||
if (!/https?\:\/\//.test(options.remote)) {
|
||||
if (!/https?:\/\//.test(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.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)
|
||||
if (isWindows) {
|
||||
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
|
||||
} else {
|
||||
this.env = { ...process.env }
|
||||
python && (this.env.PYTHON = python)
|
||||
|
||||
this.env = { ...process.env }
|
||||
if (python) {
|
||||
this.env.PYTHON = python
|
||||
}
|
||||
}
|
||||
|
||||
@bound
|
||||
addResource(absoluteFileName: string, content?: Buffer | string | File) {
|
||||
return this.bundle.addResource(absoluteFileName, content)
|
||||
async addResource(absoluteFileName: string, content?: Buffer | string | File) {
|
||||
return await this.bundle.addResource(absoluteFileName, content)
|
||||
}
|
||||
|
||||
@bound
|
||||
@@ -151,7 +131,7 @@ export class NexeCompiler {
|
||||
cachedFile = {
|
||||
absPath,
|
||||
filename: file,
|
||||
contents: await readFileAsync(absPath, 'utf-8').catch((x) => {
|
||||
contents: await readFile(absPath, 'utf-8').catch((x) => {
|
||||
if (x.code === 'ENOENT') return ''
|
||||
throw x
|
||||
}),
|
||||
@@ -162,15 +142,26 @@ export class NexeCompiler {
|
||||
}
|
||||
|
||||
@bound
|
||||
writeFileAsync(file: string, contents: string | Buffer) {
|
||||
async writeFileAsync(file: string, contents: string | Buffer) {
|
||||
this.assertBuild()
|
||||
return writeFileAsync(join(this.src, file), contents)
|
||||
return await writeFile(join(this.src, file), contents)
|
||||
}
|
||||
|
||||
@bound
|
||||
async replaceInFileAsync(file: string, replace: string | RegExp, value: string | StringReplacer) {
|
||||
async replaceInFileAsync(
|
||||
file: string,
|
||||
replace: string | RegExp,
|
||||
value: string | StringReplacer
|
||||
): Promise<boolean> {
|
||||
const entry = await this.readFileAsync(file)
|
||||
entry.contents = entry.contents.toString().replace(replace, value as any)
|
||||
let replaced = false
|
||||
const replacer = (match: string) => {
|
||||
replaced = true
|
||||
if (typeof value === 'function') return value(match)
|
||||
return value
|
||||
}
|
||||
entry.contents = entry.contents.toString().replace(replace, replacer)
|
||||
return replaced
|
||||
}
|
||||
|
||||
@bound
|
||||
@@ -179,10 +170,10 @@ export class NexeCompiler {
|
||||
entry.contents = contents
|
||||
}
|
||||
|
||||
quit(error?: any) {
|
||||
async quit(error?: any) {
|
||||
const time = Date.now() - this.start
|
||||
this.log.write(`Finished in ${time / 1000}s`, error ? 'red' : 'green')
|
||||
return this.log.flush()
|
||||
return await this.log.flush()
|
||||
}
|
||||
|
||||
assertBuild() {
|
||||
@@ -201,11 +192,11 @@ export class NexeCompiler {
|
||||
return this.nodeSrcBinPath
|
||||
}
|
||||
|
||||
private _runBuildCommandAsync(command: string, args: string[]) {
|
||||
private async _runBuildCommandAsync(command: string, args: string[]) {
|
||||
if (this.log.verbose) {
|
||||
this.compileStep!.pause()
|
||||
this.compileStep?.pause()
|
||||
}
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
return await new Promise<void>((resolve, reject) => {
|
||||
spawn(command, args, {
|
||||
cwd: this.src,
|
||||
env: this.env,
|
||||
@@ -213,13 +204,13 @@ export class NexeCompiler {
|
||||
})
|
||||
.once('error', (e: Error) => {
|
||||
if (this.log.verbose) {
|
||||
this.compileStep!.resume()
|
||||
this.compileStep?.resume()
|
||||
}
|
||||
reject(e)
|
||||
})
|
||||
.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}`
|
||||
@@ -230,93 +221,66 @@ export class NexeCompiler {
|
||||
})
|
||||
}
|
||||
|
||||
private _configureAsync() {
|
||||
if (isWindows && semverGt(this.target.version, '10.15.99')) {
|
||||
return Promise.resolve()
|
||||
private async _configureAsync() {
|
||||
if (isWindows) {
|
||||
return await Promise.resolve()
|
||||
}
|
||||
return this._runBuildCommandAsync(this.env.PYTHON || 'python', [
|
||||
this.configureScript,
|
||||
return await this._runBuildCommandAsync(this.env.PYTHON || 'python3', [
|
||||
'./configure.py',
|
||||
...this.options.configure,
|
||||
])
|
||||
}
|
||||
|
||||
public async build(): Promise<ReadStream> {
|
||||
this.compileStep!.log(
|
||||
this.compileStep?.log(
|
||||
`Configuring node build${
|
||||
this.options.configure.length ? ': ' + this.options.configure : '...'
|
||||
this.options.configure.length > 0 ? ': ' + this.options.configure : '...'
|
||||
}`
|
||||
)
|
||||
await this._configureAsync()
|
||||
const buildOptions = this.options.make
|
||||
this.compileStep!.log(
|
||||
`Compiling Node${buildOptions.length ? ' with arguments: ' + buildOptions : '...'}`
|
||||
this.compileStep?.log(
|
||||
`Compiling Node${buildOptions.length > 0 ? ' with arguments: ' + buildOptions : '...'}`
|
||||
)
|
||||
await this._runBuildCommandAsync(make, buildOptions)
|
||||
return createReadStream(this.getNodeExecutableLocation())
|
||||
}
|
||||
|
||||
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
|
||||
async compileAsync(target: NexeTarget) {
|
||||
const step = (this.compileStep = this.log.step('Compiling result')),
|
||||
build = this.options.build,
|
||||
location = this.getNodeExecutableLocation(build ? undefined : target)
|
||||
let binary = (await pathExists(location)) ? createReadStream(location) : null
|
||||
|
||||
if (!binary) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (build && snapshot != null && (await pathExistsAsync(snapshot))) {
|
||||
const snapshotLastModified = (await statAsync(snapshot)).mtimeMs
|
||||
const binaryLastModified = (await statAsync(location)).mtimeMs
|
||||
return snapshotLastModified > binaryLastModified
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if (await this._shouldCompileBinaryAsync(binary, location)) {
|
||||
binary = await this.build()
|
||||
step.log('Node binary compiled')
|
||||
}
|
||||
return this._assembleDeliverable(binary!)
|
||||
return await this._assembleDeliverable(binary)
|
||||
}
|
||||
|
||||
code() {
|
||||
return [this.shims.join(''), this.startup].join(';')
|
||||
}
|
||||
|
||||
private async _assembleDeliverable(binary: Readable) {
|
||||
private async _assembleDeliverable(binary: NodeJS.ReadableStream) {
|
||||
if (!this.options.mangle) {
|
||||
return binary
|
||||
}
|
||||
const launchCode = this.code()
|
||||
const codeSize = Buffer.byteLength(launchCode)
|
||||
const sentinel = Buffer.from('<nexe~~sentinel>')
|
||||
const resources = this.bundle.renderIndex()
|
||||
this.shims.unshift(wrap(`process.__nexe = ${JSON.stringify({ resources })};\n`))
|
||||
|
||||
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)
|
||||
},
|
||||
}))]
|
||||
const code = this.code(),
|
||||
codeSize = Buffer.byteLength(code),
|
||||
lengths = Buffer.from(Array(16))
|
||||
|
||||
let done = false
|
||||
return new MultiStream((cb) => {
|
||||
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])))
|
||||
}
|
||||
})
|
||||
lengths.writeDoubleLE(codeSize, 0)
|
||||
lengths.writeDoubleLE(this.bundle.size, 8)
|
||||
return new (MultiStream as any)([
|
||||
binary,
|
||||
toStream(code),
|
||||
this.bundle.toStream(),
|
||||
toStream(Buffer.concat([Buffer.from('<nexe~~sentinel>'), lengths])),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,383 +0,0 @@
|
||||
import { Libzip } from '@yarnpkg/libzip'
|
||||
import {
|
||||
FakeFS,
|
||||
PortablePath,
|
||||
ZipFS,
|
||||
ZipOpenFS,
|
||||
RmdirOptions,
|
||||
OpendirOptions,
|
||||
} from '@yarnpkg/fslib'
|
||||
import { BasePortableFakeFS, Dirent, MkdirOptions, Dir } from '@yarnpkg/fslib/lib/FakeFS'
|
||||
import { CustomDir } from '@yarnpkg/fslib/lib/algorithms/opendir'
|
||||
import { FSPath, ppath, npath, Filename } from '@yarnpkg/fslib/lib/path'
|
||||
import { resolve, toNamespacedPath } from 'path'
|
||||
import { constants } from 'fs'
|
||||
|
||||
export type SnapshotZipFSOptions = {
|
||||
baseFs: FakeFS<PortablePath>
|
||||
libzip: Libzip | (() => Libzip)
|
||||
zipFs: ZipFS
|
||||
}
|
||||
|
||||
const uniqBy = (arr: Array<string | Dirent>, pick: (...arg: any[]) => any) => {
|
||||
return arr.filter((x, i, self) => i === self.findIndex((y) => pick(x) === pick(y)))
|
||||
}
|
||||
function uniqReaddir(arr: Array<string | Dirent>) {
|
||||
return uniqBy(arr, (s: string | Dirent) => (typeof s === 'string' ? s : s.name))
|
||||
}
|
||||
|
||||
export class SnapshotZipFS extends BasePortableFakeFS {
|
||||
zipFs: ZipFS
|
||||
baseFs: FakeFS<PortablePath>
|
||||
constructor(opts: SnapshotZipFSOptions) {
|
||||
super()
|
||||
this.zipFs = opts.zipFs
|
||||
this.baseFs = opts.baseFs
|
||||
}
|
||||
private readonly fdMap: Map<number, [ZipFS, number]> = new Map()
|
||||
private nextFd = 3
|
||||
|
||||
async makeCallPromise<T>(
|
||||
p: FSPath<PortablePath>,
|
||||
discard: () => Promise<T>,
|
||||
accept: (zipFS: ZipFS, zipInfo: { subPath: PortablePath }) => Promise<T>,
|
||||
{ requireSubpath = true }: { requireSubpath?: boolean } = {}
|
||||
): Promise<T> {
|
||||
if (typeof p !== 'string') return await discard()
|
||||
|
||||
const normalizedP = this.resolve(p)
|
||||
|
||||
const zipInfo = this.findZip(normalizedP)
|
||||
if (!zipInfo) return await discard()
|
||||
|
||||
if (requireSubpath && zipInfo.subPath === '/') return await discard()
|
||||
|
||||
return await accept(this.zipFs, zipInfo)
|
||||
}
|
||||
|
||||
makeCallSync<T>(
|
||||
p: FSPath<PortablePath>,
|
||||
discard: () => T,
|
||||
accept: (zipFS: ZipFS, zipInfo: { subPath: PortablePath; archivePath: string }) => T,
|
||||
{ requireSubpath = true }: { requireSubpath?: boolean } = {}
|
||||
): T {
|
||||
if (typeof p !== 'string') return discard()
|
||||
|
||||
const normalizedP = this.resolve(p)
|
||||
|
||||
const zipInfo = this.findZip(normalizedP)
|
||||
if (!zipInfo) return discard()
|
||||
|
||||
if (requireSubpath && zipInfo.subPath === '/') return discard()
|
||||
|
||||
return accept(this.zipFs, { archivePath: '', ...zipInfo })
|
||||
}
|
||||
|
||||
async realpathPromise(p: PortablePath) {
|
||||
return await this.makeCallPromise(
|
||||
p,
|
||||
async () => {
|
||||
return await this.baseFs.realpathPromise(p)
|
||||
},
|
||||
async (zipFs, { subPath }) => {
|
||||
return await zipFs.realpathPromise(subPath)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
realpathSync(p: PortablePath) {
|
||||
return this.makeCallSync(
|
||||
p,
|
||||
() => {
|
||||
return this.baseFs.realpathSync(p)
|
||||
},
|
||||
(zipFs, { subPath }) => {
|
||||
return zipFs.realpathSync(subPath)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
findZip(p: PortablePath) {
|
||||
p = this.resolve(p)
|
||||
const pathsToTry = Array.from(
|
||||
new Set([
|
||||
p,
|
||||
resolve('/snapshot', p),
|
||||
ppath.resolve(
|
||||
npath.toPortablePath('/snapshot'),
|
||||
npath.toPortablePath(
|
||||
npath.relative(npath.fromPortablePath(process.cwd()), npath.fromPortablePath(p))
|
||||
)
|
||||
),
|
||||
ppath.resolve(
|
||||
npath.toPortablePath('/snapshot'),
|
||||
npath.toPortablePath(
|
||||
npath.relative(
|
||||
toNamespacedPath(npath.fromPortablePath(process.cwd())),
|
||||
toNamespacedPath(npath.fromPortablePath(p))
|
||||
)
|
||||
)
|
||||
),
|
||||
])
|
||||
)
|
||||
for (const path of pathsToTry) {
|
||||
const portablePath = npath.toPortablePath(path)
|
||||
if (this.zipFs.existsSync(portablePath)) {
|
||||
return {
|
||||
subPath: portablePath,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async copyFilePromise(sourceP: PortablePath, destP: PortablePath, flags: number = 0) {
|
||||
const fallback = async (
|
||||
sourceFs: FakeFS<PortablePath>,
|
||||
sourceP: PortablePath,
|
||||
destFs: FakeFS<PortablePath>,
|
||||
destP: PortablePath
|
||||
) => {
|
||||
if ((flags & constants.COPYFILE_FICLONE_FORCE) !== 0)
|
||||
throw Object.assign(
|
||||
new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`),
|
||||
{ code: `EXDEV` }
|
||||
)
|
||||
if (flags & constants.COPYFILE_EXCL && (await this.existsPromise(sourceP)))
|
||||
throw Object.assign(
|
||||
new Error(`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`),
|
||||
{ code: `EEXIST` }
|
||||
)
|
||||
|
||||
let content
|
||||
try {
|
||||
content = await sourceFs.readFilePromise(sourceP)
|
||||
} catch (error) {
|
||||
throw Object.assign(
|
||||
new Error(`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`),
|
||||
{ code: `EINVAL` }
|
||||
)
|
||||
}
|
||||
|
||||
await destFs.writeFilePromise(destP, content)
|
||||
}
|
||||
|
||||
return await this.makeCallPromise(
|
||||
sourceP,
|
||||
async () => {
|
||||
return await this.baseFs.copyFilePromise(sourceP, destP, flags)
|
||||
},
|
||||
async (zipFsS, { subPath: subPathS }) => {
|
||||
return await fallback(zipFsS, subPathS, this.baseFs, destP)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
copyFileSync(sourceP: PortablePath, destP: PortablePath, flags: number = 0) {
|
||||
const fallback = (
|
||||
sourceFs: FakeFS<PortablePath>,
|
||||
sourceP: PortablePath,
|
||||
destFs: FakeFS<PortablePath>,
|
||||
destP: PortablePath
|
||||
) => {
|
||||
if ((flags & constants.COPYFILE_FICLONE_FORCE) !== 0)
|
||||
throw Object.assign(
|
||||
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}'`),
|
||||
{ code: `EEXIST` }
|
||||
)
|
||||
|
||||
let content
|
||||
try {
|
||||
content = sourceFs.readFileSync(sourceP)
|
||||
} catch (error) {
|
||||
throw Object.assign(
|
||||
new Error(`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`),
|
||||
{ code: `EINVAL` }
|
||||
)
|
||||
}
|
||||
|
||||
destFs.writeFileSync(destP, content)
|
||||
}
|
||||
|
||||
return this.makeCallSync(
|
||||
sourceP,
|
||||
() => {
|
||||
return this.baseFs.copyFileSync(sourceP, destP, flags)
|
||||
},
|
||||
(zipFsS, { subPath: subPathS }) => {
|
||||
return fallback(zipFsS, subPathS, this.baseFs, destP)
|
||||
}
|
||||
)
|
||||
}
|
||||
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>>
|
||||
async readdirPromise(
|
||||
p: PortablePath,
|
||||
opts: { withFileTypes: boolean }
|
||||
): Promise<Array<Filename> | Array<Dirent>>
|
||||
async readdirPromise(
|
||||
p: PortablePath,
|
||||
opts?: { withFileTypes?: boolean } | null
|
||||
): Promise<Array<string | Dirent>> {
|
||||
const fallback = async () => {
|
||||
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(() => [])
|
||||
return Promise.resolve(
|
||||
uniqReaddir(
|
||||
(fallbackPaths as any[]).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> {
|
||||
const fallback = () => {
|
||||
return this.baseFs.readdirSync(p, opts as any)
|
||||
}
|
||||
return this.makeCallSync(
|
||||
p,
|
||||
fallback,
|
||||
(zipFs, { subPath }) => {
|
||||
let fallbackPaths: Array<string | Dirent> = []
|
||||
try {
|
||||
fallbackPaths = fallback()
|
||||
} catch (e) {}
|
||||
return (fallbackPaths as any[]).concat(uniqReaddir(zipFs.readdirSync(subPath, opts as any)))
|
||||
},
|
||||
{
|
||||
requireSubpath: false,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async mkdirPromise(p: PortablePath, opts?: MkdirOptions) {
|
||||
return await this.baseFs.mkdirPromise(p, opts)
|
||||
}
|
||||
|
||||
mkdirSync(p: PortablePath, opts?: MkdirOptions) {
|
||||
return this.baseFs.mkdirSync(p, opts)
|
||||
}
|
||||
|
||||
async rmdirPromise(p: PortablePath, opts?: RmdirOptions) {
|
||||
return await this.baseFs.rmdirPromise(p, opts)
|
||||
}
|
||||
|
||||
rmdirSync(p: PortablePath, opts?: RmdirOptions) {
|
||||
return this.baseFs.rmdirSync(p, opts)
|
||||
}
|
||||
|
||||
async opendirPromise(p: PortablePath, opts?: OpendirOptions) {
|
||||
return Promise.resolve(this.zipFs.opendirSync(p, opts))
|
||||
}
|
||||
|
||||
opendirSync(p: PortablePath, opts?: OpendirOptions) {
|
||||
const zipInfo = this.findZip(p)
|
||||
let zipFsDir: Dir<PortablePath> | null = null
|
||||
if (zipInfo) {
|
||||
zipFsDir = this.zipFs.opendirSync(zipInfo.subPath)
|
||||
}
|
||||
let realFsDir: Dir<PortablePath> | null = null
|
||||
try {
|
||||
realFsDir = this.baseFs.opendirSync(p)
|
||||
} catch (e) {
|
||||
if (!zipFsDir) throw e
|
||||
}
|
||||
const seen = new Set()
|
||||
const nextDirent = () => {
|
||||
const entry = realFsDir?.readSync() || zipFsDir?.readSync()
|
||||
if (entry && !seen.has(entry.name)) {
|
||||
seen.add(entry.name)
|
||||
return entry
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const onClose = () => {
|
||||
zipFsDir?.closeSync()
|
||||
realFsDir?.closeSync()
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
Vendored
+14
-13
@@ -1,20 +1,21 @@
|
||||
if (true) {
|
||||
const __nexe_patches = (process.nexe = { patches: {} }).patches
|
||||
const slice = [].slice
|
||||
const __nexe_noop_patch = function (original) {
|
||||
const args = slice.call(arguments, 1)
|
||||
const patches = (process.nexe = { patches: {} }).patches
|
||||
|
||||
;["ReadFile", "ReadJSON", "Stat"].forEach((x) => {
|
||||
patch(process.binding("fs"), "internalModule" + x, noop)
|
||||
})
|
||||
|
||||
function noop(original, ...args) {
|
||||
return original.apply(this, args)
|
||||
}
|
||||
const __nexe_patch = function (obj, method, patch) {
|
||||
const original = obj[method]
|
||||
|
||||
function patch(obj, method, patch) {
|
||||
const original = obj[method];
|
||||
if (!original) return
|
||||
__nexe_patches[method] = patch
|
||||
obj[method] = function() {
|
||||
const args = [original].concat(slice.call(arguments))
|
||||
return __nexe_patches[method].apply(this, args)
|
||||
patches[method] = patch
|
||||
obj[method] = function (...args) {
|
||||
args.unshift(original)
|
||||
return patches[method].apply(this, args)
|
||||
}
|
||||
}
|
||||
__nexe_patch((process).binding('fs'), 'internalModuleReadFile', __nexe_noop_patch)
|
||||
__nexe_patch((process).binding('fs'), 'internalModuleReadJSON', __nexe_noop_patch)
|
||||
__nexe_patch((process).binding('fs'), 'internalModuleStat', __nexe_noop_patch)
|
||||
}
|
||||
|
||||
+127
-24
@@ -1,10 +1,19 @@
|
||||
import { relative } from 'path'
|
||||
import { Readable } from 'stream'
|
||||
import { File } from 'resolve-dependencies'
|
||||
import archiver from 'archiver'
|
||||
import { lstat, realpath, stat } from 'node:fs/promises'
|
||||
import { createReadStream } from 'node:fs'
|
||||
import { relative } from 'node:path'
|
||||
import { Readable } from 'node:stream'
|
||||
|
||||
function makeRelativeToZip(cwd: string, path: string) {
|
||||
return '/snapshot/' + relative(cwd, path)
|
||||
import type { File } from 'resolve-dependencies'
|
||||
import MultiStream from 'multistream'
|
||||
|
||||
export type MultiStreams = Array<Readable | (() => Readable)>
|
||||
|
||||
function makeRelative(cwd: string, path: string) {
|
||||
return './' + relative(cwd, path)
|
||||
}
|
||||
|
||||
function sortEntry(a: [string, File], b: [string, File]) {
|
||||
return a[0] > b[0] ? 1 : -1
|
||||
}
|
||||
|
||||
export function toStream(content: Buffer | string) {
|
||||
@@ -16,36 +25,130 @@ export function toStream(content: Buffer | string) {
|
||||
})
|
||||
return readable
|
||||
}
|
||||
|
||||
async function createFile(absoluteFileName: string) {
|
||||
const stats = await lstat(absoluteFileName),
|
||||
file: File = {
|
||||
size: stats.size,
|
||||
moduleType: 'commonjs',
|
||||
contents: '',
|
||||
absPath: absoluteFileName,
|
||||
deps: {},
|
||||
}
|
||||
if (stats.isSymbolicLink()) {
|
||||
file.size = stats.size
|
||||
const [realPath, realStat] = await Promise.all([
|
||||
realpath(absoluteFileName),
|
||||
stat(absoluteFileName),
|
||||
])
|
||||
file.realPath = realPath
|
||||
file.realSize = realStat.size
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
export class Bundle {
|
||||
size = 0
|
||||
cwd: string
|
||||
files = new Set<string>()
|
||||
zip: any
|
||||
rendered = false
|
||||
private offset = 0
|
||||
private index: { [key: string]: [number, number] } = {}
|
||||
private files: { [key: string]: File } = {}
|
||||
streams: MultiStreams = []
|
||||
constructor({ cwd }: { cwd: string } = { cwd: process.cwd() }) {
|
||||
this.cwd = cwd
|
||||
this.zip = archiver('zip')
|
||||
}
|
||||
|
||||
get list() {
|
||||
return Array.from(this.files)
|
||||
return Object.keys(this.files)
|
||||
}
|
||||
|
||||
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 })
|
||||
} else {
|
||||
this.zip.append(content, { name: destPath })
|
||||
public async addResource(
|
||||
absoluteFileName: string,
|
||||
content?: File | Buffer | string
|
||||
): Promise<number> {
|
||||
if (this.files[absoluteFileName]) {
|
||||
return this.size
|
||||
}
|
||||
if (typeof content === 'string' || Buffer.isBuffer(content)) {
|
||||
this.files[absoluteFileName] = {
|
||||
size: Buffer.byteLength(content),
|
||||
moduleType: 'commonjs',
|
||||
contents: content.toString(),
|
||||
deps: {},
|
||||
absPath: absoluteFileName,
|
||||
}
|
||||
this.files.add(destPath)
|
||||
} else if (content) {
|
||||
this.files[content.absPath] = content
|
||||
} else {
|
||||
this.files[absoluteFileName] = {
|
||||
absPath: absoluteFileName,
|
||||
moduleType: 'commonjs',
|
||||
contents: '',
|
||||
deps: {},
|
||||
size: 0,
|
||||
}
|
||||
this.files[absoluteFileName] = await createFile(absoluteFileName)
|
||||
}
|
||||
return (this.size += this.files[absoluteFileName].size)
|
||||
}
|
||||
|
||||
/**
|
||||
* De-dupe files by absolute path, partition by symlink/real
|
||||
* Iterate over real, add entries
|
||||
* Iterate over symlinks, add symlinks
|
||||
*/
|
||||
renderIndex() {
|
||||
if (this.rendered) {
|
||||
throw new Error('Bundle index already rendered')
|
||||
}
|
||||
const files = Object.entries(this.files),
|
||||
realFiles: Array<[string, File]> = [],
|
||||
symLinks: Array<[string, File]> = []
|
||||
for (const entry of files) {
|
||||
if (entry[1].realPath) {
|
||||
symLinks.push(entry)
|
||||
} else {
|
||||
realFiles.push(entry)
|
||||
}
|
||||
}
|
||||
realFiles.sort(sortEntry)
|
||||
symLinks.sort(sortEntry)
|
||||
|
||||
for (const [absPath, file] of realFiles) {
|
||||
this.addEntry(absPath, file)
|
||||
}
|
||||
for (const [absPath, file] of symLinks) {
|
||||
this.addEntry(file.realPath as string, file)
|
||||
this.addEntry(absPath, file, file.realPath)
|
||||
}
|
||||
this.rendered = true
|
||||
return this.index
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a stream if needed and an entry with the required offset and size
|
||||
* Ensure the calling order of this method is idempotent (eg, while iterating a sorted set)
|
||||
* @param entryPath
|
||||
* @param file
|
||||
* @param useEntry
|
||||
*/
|
||||
addEntry(entryPath: string, file: File, useEntry?: string) {
|
||||
const existingName = useEntry && makeRelative(this.cwd, useEntry),
|
||||
name = makeRelative(this.cwd, entryPath),
|
||||
size = file.realSize ?? file.size,
|
||||
existingEntry = this.index[existingName ?? name]
|
||||
|
||||
this.index[name] = existingEntry || [this.offset, size]
|
||||
if (!existingEntry) {
|
||||
this.streams.push(() =>
|
||||
file.contents ? toStream(file.contents) : createReadStream(file.absPath)
|
||||
)
|
||||
this.offset += size
|
||||
}
|
||||
}
|
||||
|
||||
public finalize() {
|
||||
return this.zip.finalize()
|
||||
}
|
||||
|
||||
public toStream(): Readable {
|
||||
return this.zip
|
||||
toStream() {
|
||||
return new MultiStream(this.streams)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "nexe-fs",
|
||||
"version": "1.0.1",
|
||||
"description": "Virtual File System used by nexe",
|
||||
"main": "bundle.js",
|
||||
"files": [
|
||||
"*.d.ts",
|
||||
"*.js"
|
||||
],
|
||||
"author": "Caleb Boyd",
|
||||
"dependencies": {
|
||||
"multistream": "^2.1.1"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
+393
-61
@@ -1,101 +1,433 @@
|
||||
import { getLibzipSync } from '@yarnpkg/libzip'
|
||||
import { patchFs, npath, PosixFS, NodeFS, ZipFS } from '@yarnpkg/fslib'
|
||||
import { SnapshotZipFS } from './SnapshotZipFS'
|
||||
import * as assert from 'assert'
|
||||
import * as constants from 'constants'
|
||||
/* eslint-disable prefer-rest-params */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import type { Stats } from 'fs'
|
||||
import type { Func } from '../util'
|
||||
|
||||
export interface NexeHeader {
|
||||
export interface NexeBinary {
|
||||
blobPath: string
|
||||
resources: { [key: string]: number[] }
|
||||
layout: {
|
||||
stat: Stats
|
||||
resourceStart: number
|
||||
resourceSize: number
|
||||
contentSize: number
|
||||
contentStart: number
|
||||
contentSize?: number
|
||||
contentStart?: number
|
||||
resourceSize?: number
|
||||
}
|
||||
}
|
||||
|
||||
let originalFsMethods: any = null
|
||||
let lazyRestoreFs = () => {}
|
||||
const patches = (process as any).nexe.patches || {}
|
||||
delete (process as any).nexe
|
||||
let originalFsMethods: any = null,
|
||||
lazyRestoreFs = () => void 0
|
||||
|
||||
function shimFs(binary: NexeHeader, fs: typeof import('fs') = require('fs')) {
|
||||
// optional Win32 file namespace prefix followed by drive letter and colon
|
||||
const windowsFullPathRegex = /^(\\{2}\?\\)?([a-zA-Z]):/,
|
||||
upcaseDriveLetter = (s: string): string =>
|
||||
s.replace(windowsFullPathRegex, (_match, ns, drive) => `${ns || ''}${drive.toUpperCase()}:`)
|
||||
|
||||
function shimFs(binary: NexeBinary, fs: any = require('fs')) {
|
||||
if (originalFsMethods !== null) {
|
||||
return
|
||||
}
|
||||
|
||||
originalFsMethods = Object.assign({}, fs)
|
||||
const { blobPath, resources: manifest } = binary,
|
||||
{ resourceStart, stat } = binary.layout,
|
||||
directories: { [key: string]: { [key: string]: boolean } } = {},
|
||||
notAFile = '!@#$%^&*',
|
||||
isWin = process.platform.startsWith('win'),
|
||||
isString = (x: any): x is string => typeof x === 'string' || x instanceof String,
|
||||
noop = () => void 0
|
||||
const path = require('path')
|
||||
const winPath: (key: string) => string = isWin ? upcaseDriveLetter : (s) => s,
|
||||
baseDir = winPath(path.dirname(process.execPath))
|
||||
|
||||
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 bytesRead = realFs.readSync(
|
||||
blobFd,
|
||||
blob,
|
||||
0,
|
||||
binary.layout.resourceSize,
|
||||
binary.layout.resourceStart
|
||||
)
|
||||
assert.equal(bytesRead, binary.layout.resourceSize)
|
||||
|
||||
const zipFs = new ZipFS(blob, {
|
||||
libzip: getLibzipSync(),
|
||||
readOnly: true,
|
||||
})
|
||||
const snapshotZipFS = new SnapshotZipFS({
|
||||
libzip: getLibzipSync(),
|
||||
zipFs,
|
||||
baseFs: nodeFs,
|
||||
})
|
||||
const posixSnapshotZipFs = new PosixFS(snapshotZipFS)
|
||||
patchFs(fs, posixSnapshotZipFs)
|
||||
const { readFileSync } = fs
|
||||
|
||||
let log = (_: string) => true
|
||||
let loggedManifest = false
|
||||
let log = (_: string) => true,
|
||||
loggedManifest = false
|
||||
if ((process.env.DEBUG || '').toLowerCase().includes('nexe:require')) {
|
||||
log = (text: string) => {
|
||||
setupManifest()
|
||||
if (!loggedManifest) {
|
||||
// TODO anything meaningful to log here?
|
||||
process.stderr.write('[nexe] - MANIFEST' + JSON.stringify(manifest, null, 4) + '\n')
|
||||
process.stderr.write('[nexe] - DIRECTORIES' + JSON.stringify(directories, null, 4) + '\n')
|
||||
loggedManifest = true
|
||||
}
|
||||
return process.stderr.write('[nexe] - ' + text + '\n')
|
||||
}
|
||||
}
|
||||
patches.internalModuleReadFile = function (this: any, original: any, ...args: any[]) {
|
||||
log(`internalModuleReadFile ${args[0]}`)
|
||||
try {
|
||||
return fs.readFileSync(args[0], 'utf-8')
|
||||
} catch (e) {
|
||||
return null
|
||||
|
||||
const getKey = function getKey(filepath: string | Buffer | null): string {
|
||||
if (Buffer.isBuffer(filepath)) {
|
||||
filepath = filepath.toString()
|
||||
}
|
||||
if (!isString(filepath)) {
|
||||
return notAFile
|
||||
}
|
||||
const key = path.resolve(baseDir, filepath)
|
||||
|
||||
return winPath(key)
|
||||
},
|
||||
statTime = function () {
|
||||
return {
|
||||
atime: new Date(stat.atime),
|
||||
mtime: new Date(stat.mtime),
|
||||
ctime: new Date(stat.ctime),
|
||||
birthtime: new Date(stat.birthtime),
|
||||
}
|
||||
}
|
||||
|
||||
let BigInt: any
|
||||
try {
|
||||
BigInt = eval('BigInt')
|
||||
} catch (ignored) {
|
||||
void ignored
|
||||
}
|
||||
|
||||
const minBlocks = Math.max(Math.ceil(stat.blksize / 512), 1),
|
||||
createStat = function (extensions: any, options: any) {
|
||||
const stat = Object.assign(new fs.Stats(), binary.layout.stat, statTime(), extensions)
|
||||
if ('size' in extensions) {
|
||||
// Assume non adjustable allocation size for file system
|
||||
stat.blocks = Math.ceil(stat.size / stat.blksize) * minBlocks
|
||||
}
|
||||
if (options && options.bigint && typeof BigInt !== 'undefined') {
|
||||
for (const k in stat) {
|
||||
if (Object.prototype.hasOwnProperty.call(stat, k) && typeof stat[k] === 'number') {
|
||||
stat[k] = BigInt(stat[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
return stat
|
||||
},
|
||||
ownStat = function (filepath: any, options: any) {
|
||||
setupManifest()
|
||||
const key = getKey(filepath)
|
||||
if (directories[key]) {
|
||||
let mode = binary.layout.stat.mode
|
||||
mode |= fs.constants.S_IFDIR
|
||||
mode &= ~fs.constants.S_IFREG
|
||||
return createStat({ mode, size: 0 }, options)
|
||||
}
|
||||
if (manifest[key]) {
|
||||
return createStat({ size: manifest[key][1] }, options)
|
||||
}
|
||||
},
|
||||
getStat = function (fn: string) {
|
||||
return function stat(filepath: string | Buffer, options: any, callback: any) {
|
||||
let stat: any
|
||||
if (typeof options === 'function') {
|
||||
callback = options
|
||||
stat = ownStat(filepath, null)
|
||||
} else {
|
||||
stat = ownStat(filepath, options)
|
||||
}
|
||||
if (stat) {
|
||||
process.nextTick(() => {
|
||||
callback(null, stat)
|
||||
})
|
||||
} else {
|
||||
return originalFsMethods[fn].apply(fs, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makeLong(filepath: string) {
|
||||
return path._makeLong && path._makeLong(filepath)
|
||||
}
|
||||
|
||||
function fileOpts(options: any) {
|
||||
return !options ? {} : isString(options) ? { encoding: options } : options
|
||||
}
|
||||
|
||||
let setupManifest = () => {
|
||||
Object.keys(manifest).forEach((filepath) => {
|
||||
const entry = manifest[filepath],
|
||||
absolutePath = getKey(filepath),
|
||||
longPath = makeLong(absolutePath),
|
||||
normalizedPath = winPath(path.normalize(filepath))
|
||||
|
||||
if (!manifest[absolutePath]) {
|
||||
manifest[absolutePath] = entry
|
||||
}
|
||||
if (longPath && !manifest[longPath]) {
|
||||
manifest[longPath] = entry
|
||||
}
|
||||
if (!manifest[normalizedPath]) {
|
||||
manifest[normalizedPath] = manifest[filepath]
|
||||
}
|
||||
|
||||
let currentDir = path.dirname(absolutePath),
|
||||
prevDir = absolutePath
|
||||
|
||||
while (currentDir !== prevDir) {
|
||||
directories[currentDir] = directories[currentDir] || {}
|
||||
directories[currentDir][path.basename(prevDir)] = true
|
||||
const longDir = makeLong(currentDir)
|
||||
if (longDir && !directories[longDir]) {
|
||||
directories[longDir] = directories[currentDir]
|
||||
}
|
||||
prevDir = currentDir
|
||||
currentDir = path.dirname(currentDir)
|
||||
}
|
||||
})
|
||||
;(manifest[notAFile] as any) = false
|
||||
;(directories[notAFile] as any) = false
|
||||
setupManifest = noop
|
||||
}
|
||||
|
||||
// naive patches intended to work for most use cases
|
||||
const nfs: any = {
|
||||
existsSync: function existsSync(filepath: string) {
|
||||
setupManifest()
|
||||
const key = getKey(filepath)
|
||||
if (manifest[key] || directories[key]) {
|
||||
return true
|
||||
}
|
||||
return originalFsMethods.existsSync.apply(fs, arguments)
|
||||
},
|
||||
realpath: function realpath(filepath: any, options: any, cb: any): void {
|
||||
setupManifest()
|
||||
const key = getKey(filepath)
|
||||
if (isString(filepath) && (manifest[filepath] || manifest[key])) {
|
||||
return process.nextTick(() => cb(null, filepath))
|
||||
}
|
||||
return originalFsMethods.realpath.call(fs, filepath, options, cb)
|
||||
},
|
||||
realpathSync: function realpathSync(filepath: any, options: any) {
|
||||
setupManifest()
|
||||
const key = getKey(filepath)
|
||||
if (manifest[key]) {
|
||||
return filepath
|
||||
}
|
||||
return originalFsMethods.realpathSync.call(fs, filepath, options)
|
||||
},
|
||||
readdir: function readdir(filepath: string | Buffer, options: any, callback: any) {
|
||||
setupManifest()
|
||||
const dir = directories[getKey(filepath)]
|
||||
if (dir) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options
|
||||
options = { encoding: 'utf8' }
|
||||
}
|
||||
process.nextTick(() => callback(null, Object.keys(dir)))
|
||||
} else {
|
||||
return originalFsMethods.readdir.apply(fs, arguments)
|
||||
}
|
||||
},
|
||||
readdirSync: function readdirSync(filepath: string | Buffer) {
|
||||
setupManifest()
|
||||
const dir = directories[getKey(filepath)]
|
||||
if (dir) {
|
||||
return Object.keys(dir)
|
||||
}
|
||||
return originalFsMethods.readdirSync.apply(fs, arguments)
|
||||
},
|
||||
|
||||
readFile: function readFile(filepath: any, options: any, callback: any) {
|
||||
setupManifest()
|
||||
const entry = manifest[getKey(filepath)]
|
||||
if (!entry) {
|
||||
return originalFsMethods.readFile.apply(fs, arguments)
|
||||
}
|
||||
const [offset, length] = entry,
|
||||
resourceOffset = resourceStart + offset,
|
||||
encoding = fileOpts(options).encoding
|
||||
callback = typeof options === 'function' ? options : callback
|
||||
|
||||
originalFsMethods.open(blobPath, 'r', function (err: Error, fd: number) {
|
||||
if (err) return callback(err, null)
|
||||
originalFsMethods.read(
|
||||
fd,
|
||||
Buffer.alloc(length),
|
||||
0,
|
||||
length,
|
||||
resourceOffset,
|
||||
function (error: Error, bytesRead: number, result: Buffer) {
|
||||
if (error) {
|
||||
return originalFsMethods.close(fd, function () {
|
||||
callback(error, null)
|
||||
})
|
||||
}
|
||||
originalFsMethods.close(fd, function (err: Error) {
|
||||
if (err) {
|
||||
return callback(err, result)
|
||||
}
|
||||
callback(err, encoding ? result.toString(encoding) : result)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
},
|
||||
createReadStream: function createReadStream(filepath: any, options: any) {
|
||||
setupManifest()
|
||||
const entry = manifest[getKey(filepath)]
|
||||
if (!entry) {
|
||||
return originalFsMethods.createReadStream.apply(fs, arguments)
|
||||
}
|
||||
const [offset, length] = entry,
|
||||
resourceOffset = resourceStart + offset,
|
||||
opts = fileOpts(options)
|
||||
|
||||
return originalFsMethods.createReadStream(
|
||||
blobPath,
|
||||
Object.assign({}, opts, {
|
||||
start: resourceOffset,
|
||||
end: resourceOffset + length - 1,
|
||||
})
|
||||
)
|
||||
},
|
||||
readFileSync: function readFileSync(filepath: any, options: any) {
|
||||
setupManifest()
|
||||
const entry = manifest[getKey(filepath)]
|
||||
if (!entry) {
|
||||
return originalFsMethods.readFileSync.apply(fs, arguments)
|
||||
}
|
||||
const [offset, length] = entry,
|
||||
resourceOffset = resourceStart + offset,
|
||||
encoding = fileOpts(options).encoding,
|
||||
fd = originalFsMethods.openSync(process.execPath, 'r'),
|
||||
result = Buffer.alloc(length)
|
||||
originalFsMethods.readSync(fd, result, 0, length, resourceOffset)
|
||||
originalFsMethods.closeSync(fd)
|
||||
return encoding ? result.toString(encoding) : result
|
||||
},
|
||||
statSync: function statSync(filepath: string | Buffer, options: any) {
|
||||
const stat = ownStat(filepath, options)
|
||||
if (stat) {
|
||||
return stat
|
||||
}
|
||||
return originalFsMethods.statSync.apply(fs, arguments)
|
||||
},
|
||||
stat: getStat('stat'),
|
||||
lstat: getStat('lstat'),
|
||||
lstatSync: function statSync(filepath: string | Buffer, options: any) {
|
||||
const stat = ownStat(filepath, options)
|
||||
if (stat) {
|
||||
return stat
|
||||
}
|
||||
return originalFsMethods.lstatSync.apply(fs, arguments)
|
||||
},
|
||||
}
|
||||
if (typeof fs.exists === 'function') {
|
||||
nfs.exists = function (filepath: string, cb: Func) {
|
||||
cb = cb || noop
|
||||
const exists = nfs.existsSync(filepath)
|
||||
process.nextTick(() => cb(exists))
|
||||
}
|
||||
}
|
||||
|
||||
const patches = (process as any).nexe.patches || {}
|
||||
delete (process as any).nexe
|
||||
patches.internalModuleReadFile = function (this: any, original: any, ...args: any[]) {
|
||||
setupManifest()
|
||||
const filepath = getKey(args[0])
|
||||
if (manifest[filepath]) {
|
||||
log('read (hit) ' + filepath)
|
||||
return nfs.readFileSync(filepath, 'utf-8')
|
||||
}
|
||||
log('read (miss) ' + filepath)
|
||||
return original.call(this, ...args)
|
||||
}
|
||||
let returningArray: boolean
|
||||
patches.internalModuleReadJSON = function (this: any, original: any, ...args: any[]) {
|
||||
if (returningArray == null) returningArray = Array.isArray(original.call(this, ''))
|
||||
if (!returningArray) returningArray = Array.isArray(original.call(this, ''))
|
||||
const res = patches.internalModuleReadFile.call(this, original, ...args)
|
||||
return returningArray && !Array.isArray(res)
|
||||
? [res, /"(main|name|type|exports|imports)"/.test(res)]
|
||||
: res
|
||||
}
|
||||
patches.internalModuleStat = function (this: any, original: any, ...args: any[]) {
|
||||
log(`internalModuleStat ${args[0]}`)
|
||||
try {
|
||||
const stat = fs.statSync(args[0])
|
||||
if (stat.isDirectory()) return 1
|
||||
setupManifest()
|
||||
const filepath = getKey(args[0])
|
||||
if (manifest[filepath]) {
|
||||
log('stat (hit) ' + filepath + ' ' + 0)
|
||||
return 0
|
||||
} catch (e) {
|
||||
return -constants.ENOENT
|
||||
}
|
||||
if (directories[filepath]) {
|
||||
log('stat dir (hit) ' + filepath + ' ' + 1)
|
||||
return 1
|
||||
}
|
||||
const res = original.call(this, ...args)
|
||||
if (res === 0) {
|
||||
log('stat (miss) ' + filepath + ' ' + res)
|
||||
} else if (res === 1) {
|
||||
log('stat dir (miss) ' + filepath + ' ' + res)
|
||||
} else {
|
||||
log('stat (fail) ' + filepath + ' ' + res)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
if (typeof fs.exists === 'function') {
|
||||
nfs.exists = function (filepath: string, cb: Func) {
|
||||
cb = cb || noop
|
||||
const exists = nfs.existsSync(filepath)
|
||||
if (!exists) {
|
||||
return originalFsMethods.exists(filepath, cb)
|
||||
}
|
||||
process.nextTick(() => cb(exists))
|
||||
}
|
||||
}
|
||||
// TODO restore patches in restoreFs
|
||||
|
||||
if (typeof fs.copyFile === 'function') {
|
||||
nfs.copyFile = function (filepath: string, dest: string, flags: number, callback: Func) {
|
||||
setupManifest()
|
||||
const entry = manifest[getKey(filepath)]
|
||||
if (!entry) {
|
||||
return originalFsMethods.copyFile.apply(fs, arguments)
|
||||
}
|
||||
if (typeof flags === 'function') {
|
||||
callback = flags
|
||||
flags = 0
|
||||
}
|
||||
nfs.readFile(filepath, (err: any, buffer: any) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
originalFsMethods.writeFile(dest, buffer, (err: any) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
callback(null)
|
||||
})
|
||||
})
|
||||
}
|
||||
nfs.copyFileSync = function (filepath: string, dest: string) {
|
||||
setupManifest()
|
||||
const entry = manifest[getKey(filepath)]
|
||||
if (!entry) {
|
||||
return originalFsMethods.copyFileSync.apply(fs, arguments)
|
||||
}
|
||||
return originalFsMethods.writeFileSync(dest, nfs.readFileSync(filepath))
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof fs.realpath.native === 'function') {
|
||||
nfs.realpath.native = function realpathNative(filepath: any, options: any, cb: any): void {
|
||||
setupManifest()
|
||||
const key = getKey(filepath)
|
||||
if (isString(filepath) && (manifest[filepath] || manifest[key])) {
|
||||
return process.nextTick(() => cb(null, filepath))
|
||||
}
|
||||
return originalFsMethods.realpath.native.call(fs, filepath, options, cb)
|
||||
}
|
||||
nfs.realpathSync.native = function realpathSyncNative(filepath: any, options: any) {
|
||||
setupManifest()
|
||||
const key = getKey(filepath)
|
||||
if (manifest[key]) {
|
||||
return filepath
|
||||
}
|
||||
return originalFsMethods.realpathSync.native.call(fs, filepath, options)
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(fs, nfs)
|
||||
|
||||
lazyRestoreFs = () => {
|
||||
Object.assign(fs, originalFsMethods)
|
||||
lazyRestoreFs = () => {}
|
||||
Object.keys(nfs).forEach((key) => {
|
||||
fs[key] = originalFsMethods[key]
|
||||
})
|
||||
lazyRestoreFs = () => void 0
|
||||
return void 0
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function restoreFs() {
|
||||
|
||||
+17
-15
@@ -1,45 +1,45 @@
|
||||
import colors from 'chalk'
|
||||
import ora from 'ora'
|
||||
import type { Func } from './util.js'
|
||||
import { color, esm } from './util.js'
|
||||
|
||||
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 readonly silent: boolean
|
||||
private ora: any
|
||||
private modify: Function
|
||||
private readonly modify: Func
|
||||
public write: (text: string, color?: string) => void
|
||||
|
||||
constructor(level: 'verbose' | 'silent' | 'info') {
|
||||
this.verbose = level === 'verbose'
|
||||
this.silent = level === 'silent'
|
||||
if (!this.silent) {
|
||||
this.ora = ora({
|
||||
this.ora = esm.ora?.default({
|
||||
text: 'Starting...',
|
||||
color: 'blue',
|
||||
spinner: 'dots',
|
||||
})
|
||||
this.ora.stop()
|
||||
}
|
||||
const noop = () => {}
|
||||
const noop = () => void 0
|
||||
this.modify = this.silent ? noop : this._modify.bind(this)
|
||||
this.write = this.silent ? noop : this._write.bind(this)
|
||||
}
|
||||
|
||||
flush() {
|
||||
async flush() {
|
||||
!this.silent && this.ora.succeed()
|
||||
return new Promise((resolve) => setTimeout(resolve, frameLength))
|
||||
return await new Promise((resolve) => setTimeout(resolve, frameLength))
|
||||
}
|
||||
|
||||
_write(update: string, color = 'green') {
|
||||
this.ora.succeed().text = (colors as any)[color](update)
|
||||
_write(update: string, clr = 'green') {
|
||||
this.ora.succeed().text = color(clr, update)
|
||||
this.ora.start()
|
||||
}
|
||||
|
||||
@@ -48,10 +48,12 @@ export class Logger {
|
||||
this.ora.color = color
|
||||
}
|
||||
|
||||
step(text: string, method: string = 'succeed'): LogStep {
|
||||
step(text: string, method = 'succeed', clr?: string): LogStep {
|
||||
if (this.silent) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
return { modify() {}, log() {}, pause() {}, resume() {} }
|
||||
}
|
||||
text = color(clr, text)
|
||||
if (!this.ora.id) {
|
||||
this.ora.start().text = text
|
||||
if (method !== 'succeed') {
|
||||
|
||||
+31
-25
@@ -1,20 +1,22 @@
|
||||
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 { NexeCompiler } from './compiler.js'
|
||||
import type { NexeOptions, NexePatch } from './options.js'
|
||||
import { normalizeOptions } from './options.js'
|
||||
|
||||
import resource from './steps/resource.js'
|
||||
import clean from './steps/clean.js'
|
||||
import cli from './steps/cli.js'
|
||||
import bundle from './steps/bundle.js'
|
||||
import download from './steps/download.js'
|
||||
import shim from './steps/shim.js'
|
||||
import artifacts from './steps/artifacts.js'
|
||||
import patches from './patches/index.js'
|
||||
import { initEsm } from './util.js'
|
||||
|
||||
async function compile(
|
||||
compilerOptions?: Partial<NexeOptions>,
|
||||
callback?: (err: Error | null) => void
|
||||
) {
|
||||
await initEsm()
|
||||
let error: Error | null = null,
|
||||
options: NexeOptions | null = null,
|
||||
compiler: NexeCompiler | null = null
|
||||
@@ -22,28 +24,32 @@ async function compile(
|
||||
try {
|
||||
options = normalizeOptions(compilerOptions)
|
||||
compiler = new NexeCompiler(options)
|
||||
await compose(
|
||||
clean,
|
||||
resource,
|
||||
cli,
|
||||
bundle,
|
||||
shim,
|
||||
download,
|
||||
options.build ? [artifacts, ...patches, ...(options.patches as NexePatch[])] : [],
|
||||
options.plugins as NexePatch[]
|
||||
)(compiler)
|
||||
const plugins = options.plugins as NexePatch[],
|
||||
buildSteps = options.build ? [artifacts, ...patches, ...(options.patches as NexePatch[])] : []
|
||||
await exec([clean, resource, cli, bundle, shim, download, ...buildSteps, ...plugins], compiler)
|
||||
} catch (e: any) {
|
||||
error = e
|
||||
}
|
||||
|
||||
if (error) {
|
||||
compiler && compiler.quit(error)
|
||||
compiler?.quit(error)
|
||||
if (callback) return callback(error)
|
||||
return Promise.reject(error)
|
||||
throw error
|
||||
}
|
||||
|
||||
if (callback) callback(null)
|
||||
}
|
||||
|
||||
export { compile, NexeCompiler }
|
||||
export { argv, version, NexeOptions, help } from './options'
|
||||
export { argv, version, help } from './options.js'
|
||||
export type { NexeOptions } from './options.js'
|
||||
|
||||
function exec<T>(mw: any[], ctx: T): Promise<void> | void {
|
||||
let i = -1
|
||||
const nxt = (): void | Promise<void> => {
|
||||
if (++i < mw.length) {
|
||||
return mw[i](ctx, nxt)
|
||||
}
|
||||
}
|
||||
return nxt()
|
||||
}
|
||||
|
||||
+88
-101
@@ -1,25 +1,32 @@
|
||||
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 { basename, extname, join, isAbsolute, resolve, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { EOL, homedir } from 'node:os'
|
||||
import { globalAgent } from 'node:https'
|
||||
import { createRequire } from 'node:module'
|
||||
|
||||
const dir = typeof __dirname === 'string' ? __dirname : dirname(fileURLToPath(import.meta.url)),
|
||||
cjs = createRequire(dir)
|
||||
|
||||
import { resolveSync } from 'resolve-dependencies'
|
||||
const caw = require('caw')
|
||||
const c = process.platform === 'win32' ? chalk.constructor({ enabled: false }) : chalk
|
||||
import parseArgv from 'minimist'
|
||||
|
||||
export const version = '{{ version }}'
|
||||
import caw from 'caw'
|
||||
|
||||
export interface NexePatch {
|
||||
(compiler: NexeCompiler, next: () => Promise<void>): Promise<void>
|
||||
}
|
||||
import type { NexeCompiler } from './compiler.js'
|
||||
import { NexeError } from './compiler.js'
|
||||
import type { NexeTarget } from './target.js'
|
||||
import { getTarget } from './target.js'
|
||||
import { isWindows, STDIN_FLAG, esm } from './util.js'
|
||||
|
||||
export const version = '{{replace:0}}'
|
||||
|
||||
export type NexePatch = (compiler: NexeCompiler, next: () => Promise<void>) => Promise<void>
|
||||
|
||||
export interface NexeOptions {
|
||||
build: boolean
|
||||
input: string
|
||||
output: string
|
||||
targets: (string | NexeTarget)[]
|
||||
targets: Array<string | NexeTarget>
|
||||
name: string
|
||||
remote: string
|
||||
asset: string
|
||||
@@ -29,14 +36,12 @@ export interface NexeOptions {
|
||||
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)[]
|
||||
patches: Array<string | NexePatch>
|
||||
plugins: Array<string | NexePatch>
|
||||
native: any
|
||||
mangle: boolean
|
||||
ghToken: string
|
||||
@@ -48,7 +53,6 @@ export interface NexeOptions {
|
||||
fakeArgv?: boolean
|
||||
verbose?: boolean
|
||||
info?: boolean
|
||||
ico?: string
|
||||
debugBundle?: boolean
|
||||
warmup?: string
|
||||
clean?: boolean
|
||||
@@ -59,66 +63,67 @@ export interface NexeOptions {
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
flags: [],
|
||||
cwd: process.cwd(),
|
||||
fs: true,
|
||||
configure: [],
|
||||
mangle: true,
|
||||
make: [],
|
||||
targets: [],
|
||||
vcBuild: isWindows ? ['nosign', 'release'] : [],
|
||||
enableNodeCli: false,
|
||||
build: false,
|
||||
bundle: true,
|
||||
patches: [],
|
||||
plugins: [],
|
||||
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 } })
|
||||
let help = `
|
||||
flags: [],
|
||||
cwd: process.cwd(),
|
||||
fs: true,
|
||||
configure: [],
|
||||
mangle: true,
|
||||
make: [],
|
||||
targets: [],
|
||||
vcBuild: isWindows ? ['nosign', 'release'] : [],
|
||||
enableNodeCli: false,
|
||||
build: false,
|
||||
bundle: true,
|
||||
patches: [],
|
||||
plugins: [],
|
||||
remote: 'https://github.com/nexe/nexe/releases/download/v3.3.3/',
|
||||
},
|
||||
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',
|
||||
},
|
||||
argv = parseArgv(process.argv, { alias, default: { ...defaults } }),
|
||||
help = () => {
|
||||
const c = esm.chalk?.default as typeof import('chalk').default
|
||||
return (
|
||||
EOL +
|
||||
`
|
||||
${c.bold('nexe <entry-file> [options]')}
|
||||
|
||||
${c.underline.bold('Options:')}
|
||||
|
||||
-i --input -- application entry point
|
||||
-o --output -- path to output file
|
||||
-t --target -- node version description
|
||||
-t --target -- * node version description
|
||||
-n --name -- main app module name
|
||||
-r --resource -- *embed files (glob) within the binary
|
||||
--remote -- alternate location (URL) to download pre-built base (nexe) binaries from
|
||||
-r --resource -- * embed additional files (glob) within the binary
|
||||
--remote -- alternate root directory for pre-built base (nexe) binaries from
|
||||
--plugin -- extend nexe runtime behavior
|
||||
|
||||
${c.underline.bold('Building from source:')}
|
||||
|
||||
-b --build -- build from source
|
||||
-p --python -- python3 (as python) executable path
|
||||
-f --flag -- *v8 flags to include during compilation
|
||||
-c --configure -- *arguments to the configure step
|
||||
-m --make -- *arguments to the make/build step
|
||||
--patch -- module with middleware default export for adding a build patch
|
||||
-p --python -- python3 executable path
|
||||
-f --flag -- * v8 flags to include during compilation
|
||||
-c --configure -- * arguments to the configure step
|
||||
-m --make -- * arguments to the make/build step
|
||||
--patch -- * extend patches applied to source
|
||||
--no-mangle -- used when generating base binaries, or when patching _third_party_main manually
|
||||
--snapshot -- path to a warmup snapshot
|
||||
--ico -- file name for alternate icon file (windows)
|
||||
--rc-* -- populate rc file options (windows)
|
||||
--sourceUrl -- pass an alternate source (node.tar.gz) url
|
||||
--sourceUrl -- pass an alternate source tarball (node.tar.gz) url
|
||||
--enableNodeCli -- enable node cli enforcement (blocks app cli)
|
||||
|
||||
${c.underline.bold('Other options:')}
|
||||
@@ -131,30 +136,15 @@ ${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
|
||||
"*" indicates an option can be used more than once`.trim() +
|
||||
EOL
|
||||
)
|
||||
}
|
||||
|
||||
function flatten(...args: any[]): string[] {
|
||||
return ([] as string[]).concat(...args).filter((x) => x)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract keys such as { "rc-CompanyName": "Node.js" } to
|
||||
* { CompanyName: "Node.js" }
|
||||
* @param {*} match
|
||||
* @param {*} options
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
function extractLogLevel(options: NexeOptions) {
|
||||
if (options.loglevel) return options.loglevel
|
||||
if (options.silent) return 'silent'
|
||||
@@ -168,11 +158,11 @@ function isName(name: string) {
|
||||
|
||||
function extractName(options: NexeOptions) {
|
||||
let name = options.name
|
||||
//try and use the input filename as the output filename if its not index
|
||||
// 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), '')
|
||||
}
|
||||
//try and use the directory as the filename
|
||||
// try and use the directory as the filename
|
||||
if (!isName(name) && basename(options.cwd)) {
|
||||
name = basename(options.cwd)
|
||||
}
|
||||
@@ -213,14 +203,14 @@ export function resolveEntry(
|
||||
const inputPath = padRelative(maybeEntry)
|
||||
result = resolveSync(cwd, inputPath)
|
||||
}
|
||||
if (!process.stdin.isTTY && (!result || !result.absPath) && bundle === defaults.bundle) {
|
||||
if (!process.stdin.isTTY && !result?.absPath && bundle === defaults.bundle) {
|
||||
return STDIN_FLAG
|
||||
}
|
||||
if (!result || !result.absPath) {
|
||||
if (!result?.absPath) {
|
||||
result = resolveSync(cwd, '.')
|
||||
}
|
||||
if (!result.absPath) {
|
||||
throw new NexeError(`Entry file "${input || ''}" not found!`)
|
||||
throw new NexeError(`Entry file "${input || maybeEntry}" not found!`)
|
||||
}
|
||||
return result.absPath
|
||||
}
|
||||
@@ -230,9 +220,9 @@ function isCli(options?: Partial<NexeOptions>) {
|
||||
}
|
||||
|
||||
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,
|
||||
opts = options as any,
|
||||
cwd = (options.cwd = resolve(options.cwd))
|
||||
options.temp = options.temp
|
||||
? resolve(cwd, options.temp)
|
||||
: process.env.NEXE_TEMP || join(homedir(), '.nexe')
|
||||
@@ -243,7 +233,7 @@ function normalizeOptions(input?: Partial<NexeOptions>): NexeOptions {
|
||||
options.loglevel = extractLogLevel(options)
|
||||
options.flags = flatten(opts.flag, options.flags)
|
||||
options.targets = flatten(opts.target, options.targets).map(getTarget)
|
||||
if (!options.targets.length) {
|
||||
if (options.targets.length === 0) {
|
||||
options.targets.push(getTarget())
|
||||
}
|
||||
options.ghToken = options.ghToken || process.env.GITHUB_TOKEN || ''
|
||||
@@ -260,12 +250,9 @@ function normalizeOptions(input?: Partial<NexeOptions>): NexeOptions {
|
||||
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
|
||||
? false
|
||||
: true
|
||||
: options.downloadOptions.agent || globalAgent
|
||||
options.downloadOptions.rejectUnauthorized = !process.env.NODE_TLS_REJECT_UNAUTHORIZED
|
||||
|
||||
options.rc = options.rc || extractCliMap(/^rc-.*/, options)
|
||||
options.output =
|
||||
(options.targets[0] as NexeTarget).platform === 'windows'
|
||||
? `${(options.output || options.name).replace(/\.exe$/, '')}.exe`
|
||||
@@ -274,7 +261,7 @@ function normalizeOptions(input?: Partial<NexeOptions>): NexeOptions {
|
||||
|
||||
const requireDefault = (x: string) => {
|
||||
if (typeof x === 'string') {
|
||||
return require(x).default
|
||||
return cjs(x).default
|
||||
}
|
||||
return x
|
||||
}
|
||||
@@ -283,7 +270,7 @@ function normalizeOptions(input?: Partial<NexeOptions>): NexeOptions {
|
||||
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) {
|
||||
if ((!options.mangle && !options.bundle) || options.patches.length > 0) {
|
||||
options.build = true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
const fs = require('fs'),
|
||||
fd = fs.openSync(process.execPath, 'r'),
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const fs = require('fs')
|
||||
const fd = fs.openSync(process.execPath, 'r'),
|
||||
stat = fs.statSync(process.execPath),
|
||||
tailSize = Math.min(stat.size, 4096),
|
||||
tailSize = Math.min(stat.size, 16000),
|
||||
tailWindow = Buffer.from(Array(tailSize))
|
||||
|
||||
fs.readSync(fd, tailWindow, 0, tailSize, stat.size - tailSize)
|
||||
|
||||
const footerPosition = tailWindow.indexOf('<nexe~~sentinel>')
|
||||
if (footerPosition === -1) {
|
||||
process.stderr.write('Invalid Nexe binary')
|
||||
if (footerPosition == -1) {
|
||||
process.stderr.write('\nInvalid Nexe binary\n')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -49,8 +50,8 @@ Object.defineProperty(
|
||||
})()
|
||||
)
|
||||
|
||||
const contentBuffer = Buffer.from(Array(contentSize)),
|
||||
Module = require('module')
|
||||
const contentBuffer = Buffer.from(Array(contentSize))
|
||||
const Module = require('module')
|
||||
|
||||
fs.readSync(fd, contentBuffer, 0, contentSize, contentStart)
|
||||
fs.closeSync(fd)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
|
||||
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')
|
||||
|
||||
await compiler.replaceInFileAsync(
|
||||
'./tools/msvs/find_python.cmd',
|
||||
'%p%python.exe -V 2>&1',
|
||||
'"%p%python.exe" -V 2>&1'
|
||||
)
|
||||
|
||||
return next()
|
||||
}
|
||||
@@ -1,44 +1,20 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { semverGt } from '../util'
|
||||
import type { NexeCompiler } from '../compiler.js'
|
||||
import { NexeError } from '../compiler.js'
|
||||
import { semverGt } from '../util.js'
|
||||
|
||||
export default async function disableNodeCli(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
if (compiler.options.enableNodeCli) {
|
||||
return next()
|
||||
return await next()
|
||||
}
|
||||
|
||||
if (semverGt(compiler.target.version, '11.6.0')) {
|
||||
await compiler.replaceInFileAsync(
|
||||
'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')) {
|
||||
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
|
||||
})
|
||||
} else {
|
||||
const nodeccMarker = 'argv[index][0] =='
|
||||
const [regex, replacement] = semverGt(compiler.target.version, '18.99')
|
||||
? [/(?<!int )ProcessGlobalArgsInternal\(argv[^;]*;/gm, 'ExitCode::kNoFailure']
|
||||
: [/(?<!int )ProcessGlobalArgs\(argv[^;]*;/gm, 0],
|
||||
replaced = await compiler.replaceInFileAsync('src/node.cc', regex, `${replacement};/*$&*/`)
|
||||
|
||||
await compiler.replaceInFileAsync(
|
||||
'src/node.cc',
|
||||
`${nodeccMarker} '-'`,
|
||||
// allow NODE_OPTIONS, introduced in 8.0
|
||||
semverGt(compiler.target.version, '7.99')
|
||||
? `(${nodeccMarker} (is_env ? '-' : ']'))`
|
||||
: `(${nodeccMarker} ']')`
|
||||
)
|
||||
if (!replaced) {
|
||||
throw new NexeError('There was an error applying the Node CLI patch. Please open an issue.')
|
||||
}
|
||||
|
||||
return next()
|
||||
return await next()
|
||||
}
|
||||
|
||||
+10
-5
@@ -1,16 +1,21 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import type { NexeCompiler } from '../compiler.js'
|
||||
import { NexeError } from '../compiler.js'
|
||||
|
||||
export default async function flags(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
const nodeflags = compiler.options.flags
|
||||
if (!nodeflags.length) {
|
||||
return next()
|
||||
if (nodeflags.length === 0) {
|
||||
return await next()
|
||||
}
|
||||
|
||||
await compiler.replaceInFileAsync(
|
||||
const replaced = await compiler.replaceInFileAsync(
|
||||
'node.gyp',
|
||||
"'node_v8_options%': ''",
|
||||
`'node_v8_options%': '${nodeflags.join(' ')}'`
|
||||
)
|
||||
|
||||
return next()
|
||||
if (!replaced) {
|
||||
throw new NexeError('Node Flags not applied. Please report an issue')
|
||||
}
|
||||
|
||||
return await next()
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
|
||||
export default async function nodeGyp(
|
||||
{ files, replaceInFileAsync }: NexeCompiler,
|
||||
next: () => Promise<void>
|
||||
) {
|
||||
await next()
|
||||
|
||||
const nodeGypMarker = "'lib/fs.js',"
|
||||
await replaceInFileAsync(
|
||||
'node.gyp',
|
||||
nodeGypMarker,
|
||||
`
|
||||
${nodeGypMarker}
|
||||
${files
|
||||
.filter((x) => x.filename.startsWith('lib'))
|
||||
.map((x) => `'${x.filename}'`)
|
||||
.toString()},
|
||||
`.trim()
|
||||
)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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
|
||||
if (!iconFile) {
|
||||
return next()
|
||||
}
|
||||
await compiler.setFileContentsAsync('src/res/node.ico', await readFileAsync(normalize(iconFile)))
|
||||
return next()
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
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 bootNexe from './third-party-main.js'
|
||||
import cli from './disable-node-cli.js'
|
||||
import flags from './flags.js'
|
||||
|
||||
const patches = [gyp, bootNexe, buildFixes, cli, flags, ico, rc, snapshot]
|
||||
const patches = [bootNexe, cli, flags]
|
||||
|
||||
export default patches
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
|
||||
export default async function nodeRc(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
const options = compiler.options.rc
|
||||
if (!options) {
|
||||
return next()
|
||||
}
|
||||
|
||||
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}"`
|
||||
file.contents = file.contents
|
||||
.toString()
|
||||
.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]}`)
|
||||
}
|
||||
})
|
||||
|
||||
return next()
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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
|
||||
|
||||
if (!snapshot) {
|
||||
return next()
|
||||
}
|
||||
|
||||
const variablePrefix = semverGt(compiler.target.version, '11.0.0') ? 'v8_' : ''
|
||||
|
||||
await compiler.replaceInFileAsync(
|
||||
compiler.configureScript,
|
||||
'def configure_v8(o):',
|
||||
`def configure_v8(o):\n o['variables']['${variablePrefix}embed_script'] = r'${resolve(
|
||||
cwd,
|
||||
snapshot
|
||||
)}'\n o['variables']['${variablePrefix}warmup_script'] = r'${resolve(
|
||||
cwd,
|
||||
warmup || snapshot
|
||||
)}'`
|
||||
)
|
||||
|
||||
return next()
|
||||
}
|
||||
@@ -1,14 +1,78 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { parse } from 'meriyah'
|
||||
import { wrap, semverGt } from '../util'
|
||||
import type { NexeCompiler } from '../compiler.js'
|
||||
import { NexeError } from '../compiler.js'
|
||||
import { wrap, semverGt } from '../util.js'
|
||||
|
||||
function walkSome(node: any, visit: Function) {
|
||||
export default async function main(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
let bootFile = 'lib/internal/bootstrap/pre_execution.js'
|
||||
if (semverGt(compiler.target.version, '18.11.99')) {
|
||||
bootFile = 'lib/internal/process/pre_execution.js'
|
||||
}
|
||||
const file = await compiler.readFileAsync(bootFile),
|
||||
ast = parse(file.contents.toString(), {
|
||||
next: true,
|
||||
globalReturn: true,
|
||||
loc: true,
|
||||
specDeviation: true,
|
||||
}),
|
||||
location = { start: { line: 0 } }
|
||||
|
||||
walkSome(ast, (node: any) => {
|
||||
if (!location.start.line && node.type === 'BlockStatement') {
|
||||
// Find the first block statement and mark the location
|
||||
Object.assign(location, node.loc)
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
const fileLines = file.contents.toString().split('\n')
|
||||
|
||||
fileLines.splice(
|
||||
location.start.line,
|
||||
0,
|
||||
'{{replace:lib/MOD/fs/bootstrap.js}}' + '\n' + 'expandArgv1 = false;\n'
|
||||
)
|
||||
file.contents = fileLines.join('\n')
|
||||
|
||||
const boot = await compiler.replaceInFileAsync(
|
||||
bootFile,
|
||||
'initializeFrozenIntrinsics();',
|
||||
'initializeFrozenIntrinsics();\n' + wrap('{{replace:lib/MOD/patches/boot-nexe.js}}')
|
||||
),
|
||||
userModuleAssertion = await compiler.replaceInFileAsync(
|
||||
bootFile,
|
||||
'assert(!CJSLoader.hasLoadedAnyUserCJSModule)',
|
||||
'/*assert(!CJSLoader.hasLoadedAnyUserCJSModule)*/'
|
||||
),
|
||||
workerSupport = await compiler.replaceInFileAsync(
|
||||
'src/node.cc',
|
||||
'if (env->worker_context() != nullptr) {',
|
||||
'if (env->worker_context() == nullptr) {\n' +
|
||||
' return StartExecution(env, "internal/main/run_main_module"); } else {\n'
|
||||
)
|
||||
|
||||
assertPatches({ boot, userModuleAssertion, workerSupport })
|
||||
|
||||
return await next()
|
||||
}
|
||||
|
||||
function assertPatches(patches: Record<string, boolean>) {
|
||||
if (!Object.values(patches).every((x) => x)) {
|
||||
let message = ''
|
||||
Object.entries(patches).forEach(([k, v]) => {
|
||||
if (!v) message += `\nFailed to apply patch for: "${k}". Please open an issue.`
|
||||
})
|
||||
throw new NexeError(message.trim())
|
||||
}
|
||||
}
|
||||
|
||||
function walkSome(node: any, visit: (node: any) => boolean | undefined) {
|
||||
if (!node || typeof node.type !== 'string' || node._visited) {
|
||||
return false
|
||||
}
|
||||
visit(node)
|
||||
node._visited = true
|
||||
for (let childNode in node) {
|
||||
for (const childNode in node) {
|
||||
const child = node[childNode]
|
||||
if (Array.isArray(child)) {
|
||||
for (let i = 0; i < child.length; i++) {
|
||||
@@ -22,88 +86,3 @@ function walkSome(node: any, visit: Function) {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
const file = await compiler.readFileAsync(bootFile),
|
||||
ast = parse(file.contents.toString(), {
|
||||
next: true,
|
||||
globalReturn: true,
|
||||
loc: true,
|
||||
specDeviation: true,
|
||||
}),
|
||||
location = { start: { line: 0 } }
|
||||
|
||||
walkSome(ast, (node: any) => {
|
||||
if (!location.start.line && node.type === 'BlockStatement') {
|
||||
//Find the first block statement and mark the location
|
||||
Object.assign(location, node.loc)
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
const fileLines = file.contents.toString().split('\n')
|
||||
fileLines.splice(
|
||||
location.start.line,
|
||||
0,
|
||||
'{{ file("lib/fs/bootstrap.js") }}' +
|
||||
'\n' +
|
||||
(semverGt(version, '11.99') ? 'expandArgv1 = false;\n' : '')
|
||||
)
|
||||
file.contents = fileLines.join('\n')
|
||||
|
||||
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") }}')
|
||||
)
|
||||
} else {
|
||||
await compiler.replaceInFileAsync(
|
||||
bootFile,
|
||||
'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) {')) {
|
||||
await compiler.replaceInFileAsync(
|
||||
'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' +
|
||||
' return StartExecution(env, "internal/main/run_main_module");\n'
|
||||
)
|
||||
}
|
||||
} else {
|
||||
await compiler.setFileContentsAsync(
|
||||
'lib/_third_party_main.js',
|
||||
'{{ file("lib/patches/boot-nexe.js") }}'
|
||||
)
|
||||
}
|
||||
return next()
|
||||
}
|
||||
|
||||
+11
-9
@@ -1,5 +1,4 @@
|
||||
import got = require('got')
|
||||
import { platforms, architectures, NexeTarget, getTarget, targetsEqual } from './target'
|
||||
import { platforms, architectures, NexeTarget, getTarget, targetsEqual } from './target.js'
|
||||
export { NexeTarget }
|
||||
|
||||
const versionsToSkip = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 17, 19, 21, 22]
|
||||
@@ -21,6 +20,7 @@ interface NodeRelease {
|
||||
}
|
||||
|
||||
async function getJson<T>(url: string, options?: any) {
|
||||
const { default: got } = await import('got')
|
||||
return JSON.parse((await got(url, options)).body) as T
|
||||
}
|
||||
|
||||
@@ -31,17 +31,19 @@ function isBuildableVersion(version: string) {
|
||||
return !versionsToSkip.includes(Number(version.split('.')[0]))
|
||||
}
|
||||
|
||||
export function getLatestGitRelease(options?: any) {
|
||||
return getJson<GitRelease>('https://api.github.com/repos/nexe/nexe/releases/latest', options)
|
||||
export async function getLatestGitRelease(options?: any) {
|
||||
return await 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))
|
||||
|
||||
const versionMap: { [key: string]: true } = {}
|
||||
'https://nodejs.org/download/release/index.json'
|
||||
),
|
||||
existingVersions = (await getLatestGitRelease(options)).assets.map((x) => getTarget(x.name)),
|
||||
versionMap: { [key: string]: true } = {}
|
||||
return nodeReleases
|
||||
.reduce((versions: NexeTarget[], { version }) => {
|
||||
version = version.replace('v', '').trim()
|
||||
|
||||
+23
-29
@@ -1,28 +1,22 @@
|
||||
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 'node:path'
|
||||
import { unlink, readdir, mkdir } from 'node:fs/promises'
|
||||
|
||||
const unlinkAsync = promisify(fs.unlink),
|
||||
readdirAsync = promisify(fs.readdir)
|
||||
import { readFile, writeFile, isDirectory } from '../util.js'
|
||||
import type { NexeCompiler } from '../compiler.js'
|
||||
|
||||
function readDirAsync(dir: string): Promise<string[]> {
|
||||
return readdirAsync(dir).then((paths) => {
|
||||
return Promise.all(
|
||||
paths.map((file: string) => {
|
||||
async function readDirAsync(dir: string): Promise<string[]> {
|
||||
return await readdir(dir).then(async (paths) => {
|
||||
return await Promise.all(
|
||||
paths.map(async (file: string) => {
|
||||
const path = join(dir, file)
|
||||
return isDirectoryAsync(path).then((x) => (x ? readDirAsync(path) : (path as any)))
|
||||
return await isDirectory(path).then((x) => (x ? readDirAsync(path) : (path as any)))
|
||||
})
|
||||
).then((result) => {
|
||||
return [].concat(...(result as any))
|
||||
})
|
||||
).then((result) => result.flat())
|
||||
})
|
||||
}
|
||||
|
||||
function maybeReadFileContentsAsync(file: string) {
|
||||
return readFileAsync(file, 'utf-8').catch((e) => {
|
||||
async function maybeReadFileContentsAsync(file: string) {
|
||||
return await readFile(file, 'utf-8').catch((e) => {
|
||||
if (e.code === 'ENOENT') {
|
||||
return ''
|
||||
}
|
||||
@@ -40,28 +34,28 @@ function maybeReadFileContentsAsync(file: string) {
|
||||
* - 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 { src } = compiler,
|
||||
temp = join(src, 'nexe')
|
||||
await mkdir(temp, { recursive: true })
|
||||
const tmpFiles = await readDirAsync(temp)
|
||||
|
||||
await Promise.all(
|
||||
tmpFiles.map(async (path) => {
|
||||
return compiler.writeFileAsync(path.replace(temp, ''), await readFileAsync(path, 'utf-8'))
|
||||
return await compiler.writeFileAsync(path.replace(temp, ''), await readFile(path, 'utf-8'))
|
||||
})
|
||||
)
|
||||
|
||||
await next()
|
||||
|
||||
await Promise.all(tmpFiles.map((x) => unlinkAsync(x)))
|
||||
return Promise.all(
|
||||
await Promise.all(tmpFiles.map(async (x) => await unlink(x)))
|
||||
return await 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),
|
||||
tempFile = join(temp, file.filename),
|
||||
fileContents = await maybeReadFileContentsAsync(sourceFile)
|
||||
|
||||
await mkdirpAsync(dirname(tempFile))
|
||||
await writeFileAsync(tempFile, fileContents)
|
||||
await mkdir(dirname(tempFile), { recursive: true })
|
||||
await writeFile(tempFile, fileContents)
|
||||
await compiler.writeFileAsync(file.filename, file.contents)
|
||||
})
|
||||
)
|
||||
|
||||
+17
-21
@@ -1,12 +1,14 @@
|
||||
import { NexeCompiler, NexeError } from '../compiler'
|
||||
import { resolve, relative } from 'path'
|
||||
import { resolve, relative } from 'node:path'
|
||||
import type { Readable } from 'node:stream'
|
||||
import resolveFiles, { resolveSync } from 'resolve-dependencies'
|
||||
import { dequote, STDIN_FLAG, semverGt, each } from '../util'
|
||||
import { Readable } from 'stream'
|
||||
|
||||
function getStdIn(stdin: Readable): Promise<string> {
|
||||
import type { NexeCompiler } from '../compiler.js'
|
||||
import { NexeError } from '../compiler.js'
|
||||
import { dequote, STDIN_FLAG } from '../util.js'
|
||||
|
||||
async function getStdIn(stdin: Readable): Promise<string> {
|
||||
let out = ''
|
||||
return new Promise((resolve) => {
|
||||
return await new Promise((resolve) => {
|
||||
stdin
|
||||
.setEncoding('utf8')
|
||||
.on('readable', () => {
|
||||
@@ -28,12 +30,7 @@ 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)
|
||||
|
||||
if (semverGt(compiler.target.version, '11.99')) {
|
||||
compiler.startup = ''
|
||||
} else {
|
||||
compiler.startup = ';require("module").runMain();'
|
||||
}
|
||||
compiler.startup = ''
|
||||
|
||||
if (!doBundle) {
|
||||
await compiler.addResource(resolve(cwd, input))
|
||||
@@ -42,6 +39,7 @@ export default async function bundle(compiler: NexeCompiler, next: any) {
|
||||
|
||||
let code = ''
|
||||
if (typeof doBundle === 'string') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
code = await require(doBundle).createBundle(compiler.options)
|
||||
}
|
||||
|
||||
@@ -61,26 +59,24 @@ export default async function bundle(compiler: NexeCompiler, next: any) {
|
||||
compiler.entrypoint = './' + relative(cwd, input)
|
||||
}
|
||||
|
||||
const step = compiler.log.step('Resolving dependencies...')
|
||||
|
||||
const { files, warnings } = await resolveFiles(
|
||||
const { files, warnings } = resolveFiles(
|
||||
input,
|
||||
...Object.keys(compiler.bundle.list).filter((x) => x.endsWith('.js') || x.endsWith('.mjs')),
|
||||
...Object.keys(compiler.bundle.list).filter((x) => x.endsWith('.js')),
|
||||
{ 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 > 0
|
||||
) {
|
||||
throw new NexeError('Parsing Error:\n' + warnings.join('\n'))
|
||||
}
|
||||
|
||||
//TODO: warnings.forEach((x) => console.log(x))
|
||||
warnings.forEach((x: string) => compiler.log.step('[WARNING]: ' + x, 'stopAndPersist', 'yellow'))
|
||||
|
||||
await Promise.all(
|
||||
Object.entries(files).map(([key]) => {
|
||||
step.log(`Including dependency: ${key}`)
|
||||
return compiler.addResource(key)
|
||||
Object.entries(files).map(async ([key, file]) => {
|
||||
return await compiler.addResource(key, file)
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
+7
-6
@@ -1,6 +1,7 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { rimrafAsync } from '../util'
|
||||
import { NexeTarget } from '../target'
|
||||
import { rm } from 'node:fs/promises'
|
||||
|
||||
import type { NexeCompiler } from '../compiler.js'
|
||||
import type { NexeTarget } from '../target.js'
|
||||
|
||||
export default async function clean(compiler: NexeCompiler, next: () => Promise<any>) {
|
||||
const { options } = compiler
|
||||
@@ -11,9 +12,9 @@ export default async function clean(compiler: NexeCompiler, next: () => Promise<
|
||||
}
|
||||
const step = compiler.log.step('Cleaning up nexe build artifacts...')
|
||||
step.log(`Deleting contents at: ${path}`)
|
||||
await rimrafAsync(path)
|
||||
await rm(path, { recursive: true, force: true })
|
||||
step.log(`Deleted contents at: ${path}`)
|
||||
return compiler.quit()
|
||||
return await compiler.quit()
|
||||
}
|
||||
return next()
|
||||
return await next()
|
||||
}
|
||||
|
||||
+8
-9
@@ -1,9 +1,8 @@
|
||||
import { dirname, normalize, relative, resolve } from 'path'
|
||||
import { createWriteStream, chmodSync, statSync } from 'fs'
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { NexeTarget } from '../target'
|
||||
import { STDIN_FLAG } from '../util'
|
||||
import mkdirp = require('mkdirp')
|
||||
import { dirname, normalize, relative, resolve } from 'node:path'
|
||||
import { createWriteStream, chmodSync, statSync, mkdirSync } from 'node:fs'
|
||||
import type { NexeCompiler } from '../compiler.js'
|
||||
import type { NexeTarget } from '../target.js'
|
||||
import { STDIN_FLAG } from '../util.js'
|
||||
|
||||
/**
|
||||
* The "cli" step detects the appropriate input. If no input options are passed,
|
||||
@@ -20,11 +19,11 @@ export default async function cli(compiler: NexeCompiler, next: () => Promise<vo
|
||||
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))
|
||||
mkdirSync(dirname(output), { recursive: true })
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
return await new Promise((res, rej) => {
|
||||
const step = log.step('Writing result to file')
|
||||
deliverable
|
||||
.pipe(createWriteStream(output))
|
||||
|
||||
+17
-13
@@ -1,15 +1,19 @@
|
||||
import download = require('download')
|
||||
import { pathExistsAsync } from '../util'
|
||||
import { LogStep } from '../logger'
|
||||
import { IncomingMessage } from 'http'
|
||||
import { NexeCompiler, NexeError } from '../compiler'
|
||||
import { dirname } from 'path'
|
||||
import { dirname } from 'node:path'
|
||||
|
||||
import download from 'download'
|
||||
|
||||
import { pathExists } from '../util.js'
|
||||
import type { LogStep } from '../logger.js'
|
||||
import type { NexeCompiler } from '../compiler.js'
|
||||
import { NexeError } from '../compiler.js'
|
||||
|
||||
import type { IncomingMessage } from 'node:http'
|
||||
|
||||
function fetchNodeSourceAsync(dest: string, url: string, step: LogStep, options = {}) {
|
||||
const setText = (p: number) => step.modify(`Downloading Node: ${p.toFixed()}%...`)
|
||||
return download(url, dest, Object.assign(options, { extract: true, strip: 1 }))
|
||||
.on('response', (res: IncomingMessage) => {
|
||||
const total = +res.headers['content-length']!
|
||||
const total = Number(res.headers['content-length'])
|
||||
let current = 0
|
||||
res.on('data', (data) => {
|
||||
current += data.length
|
||||
@@ -30,11 +34,11 @@ async function fetchPrebuiltBinary(compiler: NexeCompiler, step: any) {
|
||||
await download(remoteAsset, dirname(filename), compiler.options.downloadOptions).on(
|
||||
'response',
|
||||
(res: IncomingMessage) => {
|
||||
const total = +res.headers['content-length']!
|
||||
const total = Number(res.headers['content-length'])
|
||||
let current = 0
|
||||
res.on('data', (data) => {
|
||||
current += data.length
|
||||
step!.modify(`Downloading...${((current / total) * 100).toFixed()}%`)
|
||||
step?.modify(`Downloading...${((current / total) * 100).toFixed()}%`)
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -58,14 +62,14 @@ export default async function downloadNode(compiler: NexeCompiler, next: () => P
|
||||
{ sourceUrl, downloadOptions, build } = compiler.options,
|
||||
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)
|
||||
downloadExists = await pathExists(build ? src : exeLocation)
|
||||
|
||||
if (downloadExists) {
|
||||
step.log('Already downloaded...')
|
||||
return next()
|
||||
return await next()
|
||||
}
|
||||
|
||||
if (build) {
|
||||
@@ -74,5 +78,5 @@ export default async function downloadNode(compiler: NexeCompiler, next: () => P
|
||||
await fetchPrebuiltBinary(compiler, step)
|
||||
}
|
||||
|
||||
return next()
|
||||
return await next()
|
||||
}
|
||||
|
||||
+26
-17
@@ -1,25 +1,34 @@
|
||||
import { each } from '../util'
|
||||
import globs from 'globby'
|
||||
import { resolve } from 'path'
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { relative } from 'node:path'
|
||||
|
||||
import fg from 'fast-glob'
|
||||
|
||||
import { each } from '../util.js'
|
||||
import type { NexeCompiler } from '../compiler.js'
|
||||
|
||||
export default async function resource(compiler: NexeCompiler, next: () => Promise<any>) {
|
||||
const { cwd, resources } = compiler.options
|
||||
if (!resources.length) {
|
||||
return next()
|
||||
if (resources.length === 0) {
|
||||
return await next()
|
||||
}
|
||||
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, '/'))
|
||||
|
||||
await each(globs(resourcesWithForwardSlashes, { cwd, onlyFiles: true }), async (file) => {
|
||||
count++
|
||||
step.log(`Including file: ${file}`)
|
||||
await compiler.addResource(resolve(cwd, file))
|
||||
resources.forEach((x: string) => {
|
||||
if (x.includes('node_modules/**')) {
|
||||
compiler.log.step(
|
||||
`[WARNING]: pattern "${x}" will include many files, consider narrowing the pattern`,
|
||||
'stopAndPersist',
|
||||
'yellow'
|
||||
)
|
||||
}
|
||||
})
|
||||
step.log(`Included ${count} file(s)`)
|
||||
return next()
|
||||
|
||||
const step = compiler.log.step('Bundling Resources...')
|
||||
|
||||
await each(fg(resources, { cwd, absolute: true, onlyFiles: true }), async (file) => {
|
||||
count++
|
||||
step.log(`Including file: ${relative(cwd, file)}`)
|
||||
await compiler.addResource(file)
|
||||
})
|
||||
compiler.log.step(`Included ${count} file(s)`, 'stopAndPersist')
|
||||
return await next()
|
||||
}
|
||||
|
||||
+6
-14
@@ -1,22 +1,14 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { wrap } from '../util'
|
||||
import type { NexeCompiler } from '../compiler.js'
|
||||
import { wrap } from '../util.js'
|
||||
|
||||
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;',
|
||||
'{{file("lib/fs/patch.bundle.js")}}',
|
||||
'return module.exports;',
|
||||
'})()',
|
||||
'fsPatcher.shimFs(process.__nexe);',
|
||||
compiler.options.fs ? '' : 'restoreFs();',
|
||||
].join('\n')
|
||||
//TODO support only restoring specific methods
|
||||
'' +
|
||||
'{{replace:lib/MOD/fs/patch.js}}' +
|
||||
'\nshimFs(process.__nexe)' +
|
||||
`\n${compiler.options.fs ? '' : 'restoreFs()'}`
|
||||
)
|
||||
)
|
||||
compiler.shims.push(
|
||||
|
||||
+24
-24
@@ -13,30 +13,29 @@ export interface NexeTarget {
|
||||
}
|
||||
|
||||
const prettyPlatform: { [key: string]: NodePlatform } = {
|
||||
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',
|
||||
}
|
||||
win32: 'windows',
|
||||
windows: 'windows',
|
||||
win: 'windows',
|
||||
darwin: 'mac',
|
||||
macos: 'mac',
|
||||
mac: 'mac',
|
||||
linux: 'linux',
|
||||
static: 'alpine',
|
||||
alpine: 'alpine',
|
||||
},
|
||||
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',
|
||||
}
|
||||
|
||||
function isVersion(x: string) {
|
||||
if (!x) {
|
||||
@@ -58,6 +57,7 @@ class Target implements NexeTarget {
|
||||
toJSON() {
|
||||
return this.toString()
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `${this.platform}-${this.arch}-${this.version}`
|
||||
}
|
||||
|
||||
Vendored
+4
-1
@@ -6,8 +6,11 @@ declare module 'got' {
|
||||
const got: GotFn
|
||||
export = got
|
||||
}
|
||||
|
||||
declare module 'caw'
|
||||
|
||||
declare module 'download' {
|
||||
import { Duplex } from 'stream'
|
||||
import type { Duplex } from 'stream'
|
||||
interface DownloadOptions {
|
||||
extract?: boolean
|
||||
strip?: number
|
||||
|
||||
+49
-32
@@ -1,17 +1,40 @@
|
||||
import { readFile, writeFile, stat } from 'fs'
|
||||
import { execFile } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
import rimraf = require('rimraf')
|
||||
import { readFile, writeFile, stat } from 'node:fs/promises'
|
||||
import { execFile as exec } from 'node:child_process'
|
||||
import { promisify } from 'node:util'
|
||||
|
||||
const rimrafAsync = promisify(rimraf)
|
||||
export const STDIN_FLAG = '[stdin]'
|
||||
|
||||
export const esm: {
|
||||
chalk: typeof import('chalk') | null
|
||||
ora: typeof import('ora') | null
|
||||
got: typeof import('got') | null
|
||||
} = {
|
||||
got: null,
|
||||
chalk: null,
|
||||
ora: null,
|
||||
}
|
||||
|
||||
export async function initEsm() {
|
||||
await Promise.all(
|
||||
(['chalk', 'got', 'ora'] as const).map(async (x) => {
|
||||
esm[x] = await import(x)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const id = (x: any) => x
|
||||
export function color(color?: string, text?: string) {
|
||||
return !color ? text : ((esm.chalk?.default as any)[color] || id)(text)
|
||||
}
|
||||
|
||||
export type Func<T = any, Args = any> = (...args: Args[]) => T
|
||||
|
||||
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))
|
||||
return await Promise.all(l.map(action))
|
||||
}
|
||||
|
||||
export function wrap(code: string) {
|
||||
@@ -30,8 +53,8 @@ function padRight(str: string, l: number) {
|
||||
}
|
||||
|
||||
const bound: MethodDecorator = function bound<T>(
|
||||
target: Object,
|
||||
propertyKey: string | Symbol,
|
||||
target: object,
|
||||
propertyKey: string | symbol,
|
||||
descriptor: TypedPropertyDescriptor<T>
|
||||
) {
|
||||
const configurable = true
|
||||
@@ -51,8 +74,8 @@ const bound: MethodDecorator = function bound<T>(
|
||||
|
||||
function dequote(input: string) {
|
||||
input = input.trim()
|
||||
const singleQuote = input.startsWith("'") && input.endsWith("'")
|
||||
const doubleQuote = input.startsWith('"') && input.endsWith('"')
|
||||
const singleQuote = input.startsWith("'") && input.endsWith("'"),
|
||||
doubleQuote = input.startsWith('"') && input.endsWith('"')
|
||||
if (singleQuote || doubleQuote) {
|
||||
return input.slice(1).slice(0, -1)
|
||||
}
|
||||
@@ -64,30 +87,25 @@ export interface ReadFileAsync {
|
||||
(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 execFile = promisify(exec),
|
||||
isWindows = process.platform === 'win32'
|
||||
|
||||
function pathExistsAsync(path: string) {
|
||||
return statAsync(path)
|
||||
.then((x) => true)
|
||||
async function pathExists(path: string) {
|
||||
return await stat(path)
|
||||
.then(() => true)
|
||||
.catch(falseOnEnoent)
|
||||
}
|
||||
|
||||
function isDirectoryAsync(path: string) {
|
||||
return statAsync(path)
|
||||
async function isDirectory(path: string) {
|
||||
return await stat(path)
|
||||
.then((x) => x.isDirectory())
|
||||
.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)
|
||||
major = Number(major)
|
||||
if (!minor) minor = 0
|
||||
if (!patch) patch = 0
|
||||
return (
|
||||
@@ -100,14 +118,13 @@ function semverGt(version: string, operand: string) {
|
||||
export {
|
||||
dequote,
|
||||
padRight,
|
||||
semverGt,
|
||||
bound,
|
||||
isWindows,
|
||||
rimrafAsync,
|
||||
statAsync,
|
||||
execFileAsync,
|
||||
readFileAsync,
|
||||
pathExistsAsync,
|
||||
isDirectoryAsync,
|
||||
writeFileAsync,
|
||||
stat,
|
||||
execFile,
|
||||
readFile,
|
||||
pathExists,
|
||||
isDirectory,
|
||||
writeFile,
|
||||
semverGt,
|
||||
}
|
||||
|
||||
+45
-96
@@ -1,104 +1,43 @@
|
||||
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 got from 'got'
|
||||
import { cpus } from 'os'
|
||||
import { env } from 'node:process'
|
||||
import { execSync } from 'node:child_process'
|
||||
import { buildArm, buildAlpine } from './docker.js'
|
||||
|
||||
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',
|
||||
headers = {
|
||||
Authorization: 'token ' + env.GITHUB_TOKEN,
|
||||
'User-Agent': 'nexe (https://www.npmjs.com/package/nexe)',
|
||||
export async function build(target = env.NEXE_TARGET || null) {
|
||||
if (!target?.trim()) {
|
||||
throw new Error('Environment variable NEXE_TARGET not found. Received: ' + target)
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
if (!isPullRequest) {
|
||||
build().catch((x) => {
|
||||
console.error(x)
|
||||
process.exit(1)
|
||||
})
|
||||
const done = keepalive()
|
||||
let file = ''
|
||||
try {
|
||||
if (target.includes('alpine')) {
|
||||
file = await buildAlpine(target)
|
||||
} else if (target.includes('arm')) {
|
||||
file = await buildArm(target)
|
||||
} else {
|
||||
file = await nexeBuild(target)
|
||||
assertNexeBinary(file)
|
||||
}
|
||||
} catch (e: any) {
|
||||
done()
|
||||
throw e
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
throw new Error('Binary failed to build')
|
||||
}
|
||||
|
||||
uploadBinary(target, file)
|
||||
}
|
||||
|
||||
async function build() {
|
||||
const releases = await getUnBuiltReleases({ headers })
|
||||
if (!releases.length) {
|
||||
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')
|
||||
function nexeBuild(target: string) {
|
||||
console.log('building: ' + target)
|
||||
return Promise.resolve('')
|
||||
}
|
||||
|
||||
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 (!target) {
|
||||
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('2015') && semverGt(target.version, '9.99.99')) {
|
||||
return console.log(`Not building ${target} on this host...`)
|
||||
}
|
||||
|
||||
const output = isWindows ? './out.exe' : './out',
|
||||
options = {
|
||||
mangle: false,
|
||||
build: true,
|
||||
verbose: Boolean(env.NEXE_VERBOSE!),
|
||||
target,
|
||||
make: ['-j' + cpus().length],
|
||||
output,
|
||||
}
|
||||
console.log('Building: ' + target + ' on ' + buildHost)
|
||||
const stop = keepalive()
|
||||
|
||||
if (
|
||||
[/*'arm7l', 'arm6l', 'arm64', */ 'alpine'].includes(target.platform) &&
|
||||
buildHost.includes('alpine')
|
||||
) {
|
||||
await runDockerBuild(target)
|
||||
} else {
|
||||
await nexe.compile(options)
|
||||
}
|
||||
stop()
|
||||
if (await pathExistsAsync(output)) {
|
||||
await assertNexeBinary(output)
|
||||
const gitRelease = await getLatestGitRelease({ headers }),
|
||||
unbuiltReleases = await getUnBuiltReleases({ headers })
|
||||
if (!unbuiltReleases.some((x) => targetsEqual(x, target!))) {
|
||||
console.log(`${target} already uploaded.`)
|
||||
process.exit(0)
|
||||
return
|
||||
}
|
||||
await got(gitRelease.upload_url.split('{')[0], {
|
||||
searchParams: { name: target.toString() },
|
||||
body: await readFileAsync(output),
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Type': 'application/octet-stream',
|
||||
},
|
||||
}).catch((reason) => {
|
||||
console.log(reason && reason.response && reason.response.body)
|
||||
throw reason
|
||||
})
|
||||
console.log(target + ' uploaded.')
|
||||
process.exit(0)
|
||||
}
|
||||
function uploadBinary(target: string, file: string) {
|
||||
void file
|
||||
console.log('uploading to release')
|
||||
}
|
||||
|
||||
function keepalive() {
|
||||
@@ -107,10 +46,20 @@ function keepalive() {
|
||||
}
|
||||
|
||||
function assertNexeBinary(file: string) {
|
||||
return execFileAsync(file).catch((e) => {
|
||||
if (!file) throw new Error('Invalid filename: "' + file + '"')
|
||||
let result = ''
|
||||
try {
|
||||
result = execSync(file).toString('utf8')
|
||||
} catch (e: any) {
|
||||
if (e && e.stack && e.stack.includes('Invalid Nexe binary')) {
|
||||
return
|
||||
}
|
||||
throw e
|
||||
})
|
||||
}
|
||||
throw new Error('Not a bare nexe binary: ' + result)
|
||||
}
|
||||
|
||||
build().catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
export declare function buildArm(target: string): Promise<string>
|
||||
export declare function buildAlpine(target: string): Promise<string>
|
||||
+6
-71
@@ -1,74 +1,9 @@
|
||||
import { NexeTarget, architectures } from '../lib/target'
|
||||
import { writeFileAsync, readFileAsync } from '../lib/util'
|
||||
import got from 'got'
|
||||
import execa = require('execa')
|
||||
import { appendFileSync } from 'fs'
|
||||
|
||||
function alpine(target: NexeTarget) {
|
||||
return `
|
||||
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
|
||||
WORKDIR /
|
||||
|
||||
RUN curl -sSL https://nodejs.org/dist/v\${NODE_VERSION}/node-v\${NODE_VERSION}.tar.gz | tar -xz && \
|
||||
mkdir /nexe_temp && mv /node-v\${NODE_VERSION} /nexe_temp/\${NODE_VERSION} && \
|
||||
cd /nexe_temp/\${NODE_VERSION} && \
|
||||
./configure --prefix=/usr --fully-static && \
|
||||
make && make install && \
|
||||
paxctl -cm /usr/bin/node
|
||||
|
||||
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()
|
||||
export function buildArm(target: string) {
|
||||
console.log('building arm: ' + target)
|
||||
return Promise.resolve('')
|
||||
}
|
||||
|
||||
function arm(target: NexeTarget) {
|
||||
return `
|
||||
FROM hypriot/rpi-node
|
||||
ENV NEXE_VERSION=latest
|
||||
WORKDIR /
|
||||
|
||||
RUN yarn global add nexe@\${NEXE_VERSION} && \
|
||||
nexe --build --no-mangle -o out -t ${target.version}
|
||||
`.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 = []
|
||||
|
||||
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 }))
|
||||
} catch (e: any) {
|
||||
console.log('Error running docker')
|
||||
appendFileSync(outFilename, e.message)
|
||||
} finally {
|
||||
output.forEach((x: any) => {
|
||||
appendFileSync(outFilename, x.stderr)
|
||||
appendFileSync(outFilename, x.stdout)
|
||||
})
|
||||
try {
|
||||
const { body } = await got(
|
||||
`https://transfer.sh/${Math.random().toString(36).substring(2)}.txt`,
|
||||
{
|
||||
body: await readFileAsync(outFilename),
|
||||
method: 'PUT',
|
||||
}
|
||||
)
|
||||
console.log('Posted docker log: ', body)
|
||||
} catch (e) {
|
||||
console.log('Error posting log', e)
|
||||
}
|
||||
}
|
||||
export function buildAlpine(target: string) {
|
||||
console.log('building alpine: ' + target)
|
||||
return Promise.resolve('')
|
||||
}
|
||||
|
||||
+30
-20
@@ -1,31 +1,41 @@
|
||||
import { writeFileSync, readFileSync } from 'fs'
|
||||
import { template } from 'lodash'
|
||||
import { writeFileSync, readFileSync } from 'node:fs'
|
||||
import { equal } from 'node:assert'
|
||||
|
||||
/**
|
||||
* post build step to insert code files into code files.
|
||||
* post build step to insert code files into code files (naively).
|
||||
* '{{replace:path/to/file}}' => "file contents"
|
||||
* 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')
|
||||
const totalReplacements = 8
|
||||
let replaced = 0
|
||||
const mods = ['cjs', 'esm'] as const
|
||||
|
||||
inject('lib/patches/third-party-main.js')
|
||||
inject('lib/steps/shim.js')
|
||||
inject('lib/options.js', JSON.stringify(require('../package.json').version))
|
||||
mods.forEach((x) => {
|
||||
inject(x, `lib/${x}/patches/third-party-main.js`)
|
||||
inject(x, `lib/${x}/steps/shim.js`)
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
inject(x, `lib/${x}/options.js`, JSON.stringify(require('../package.json').version))
|
||||
})
|
||||
|
||||
function inject(filename: string, version?: string) {
|
||||
const contents = template(readFileSync(filename, 'utf8'), {
|
||||
interpolate: /'\{\{([\s\S]*?)\}\}'/,
|
||||
})({
|
||||
file: (path: string) => JSON.stringify(readFileSync(path, 'utf8')),
|
||||
version,
|
||||
function inject(mod: typeof mods[number], filename: string, ...replacements: string[]) {
|
||||
let contents = readFileSync(filename, 'utf8')
|
||||
contents = contents.replace(/('{{(.*)}}')/g, (substring: string, ...matches: string[]) => {
|
||||
if (!matches || !matches[1]) {
|
||||
return substring
|
||||
}
|
||||
const [replace, file] = matches[1].split(':')
|
||||
if (replace !== 'replace') {
|
||||
return substring
|
||||
}
|
||||
++replaced
|
||||
console.log('Replacing: ', substring)
|
||||
|
||||
return replacements[+file]
|
||||
? replacements[+file]
|
||||
: JSON.stringify(readFileSync(file.replace('MOD', mod), 'utf8').replace('"use strict";', ''))
|
||||
})
|
||||
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)
|
||||
}
|
||||
equal(replaced, totalReplacements, `Expected to replace ${totalReplacements} things`)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true
|
||||
},
|
||||
"include":[
|
||||
"*.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import * as nexeEsm from '../lib/esm/nexe.js'
|
||||
import { createRequire } from 'node:module'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { ok } from 'node:assert'
|
||||
|
||||
const cjs = createRequire(fileURLToPath(import.meta.url)),
|
||||
nexeCjs = cjs('../lib/cjs/nexe.js'),
|
||||
exports = ['NexeCompiler', 'argv', 'compile', 'help', 'version']
|
||||
|
||||
for (const exp of exports) {
|
||||
ok(exp in nexeCjs)
|
||||
ok(exp in nexeEsm)
|
||||
}
|
||||
|
||||
console.log('esm and cjs modules match')
|
||||
@@ -1,18 +0,0 @@
|
||||
const { mkdtemp } = require('fs/promises')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const rimraf = require('rimraf')
|
||||
|
||||
async function runTests() {
|
||||
const tempdir = await mkdtemp(path.join(os.tmpdir(), 'nexe-'))
|
||||
process.on('beforeExit', () => rimraf.sync(tempdir))
|
||||
process.chdir(tempdir)
|
||||
const Mocha = require('mocha')
|
||||
const mocha = new Mocha()
|
||||
mocha.addFile('test/integration/tests.integration-spec.js')
|
||||
mocha.run((failures) => {
|
||||
process.exitCode = failures ? 1 : 0
|
||||
})
|
||||
}
|
||||
|
||||
runTests()
|
||||
@@ -1,112 +0,0 @@
|
||||
const { expect } = require('chai')
|
||||
const fs = require('fs')
|
||||
const { dirname, basename }= require('path')
|
||||
|
||||
const files = { zipFs: 'test/integration/tests.integration-spec.js', real: 'real', real2: 'real2' }
|
||||
describe('SnapshotZipFS', () => {
|
||||
console.error('Note: these tests are order dependant, so subsequent failures are likely a flow on effect.')
|
||||
it('checks existance from the zipFs', () => expect(fs.existsSync(files.zipFs)).to.equal(true))
|
||||
it('checks non-existance from the zipFs', () => expect(fs.existsSync('not found')).to.equal(false))
|
||||
it('reads from the zipFs', () => expect(fs.readFileSync(files.zipFs, 'utf8')).to.include('SnapshotZipFS'))
|
||||
it('copies from the zipFs', () => expect(() => fs.copyFileSync(files.zipFs, files.real)).not.to.throw())
|
||||
it('reads from the real fs', () => expect(fs.readFileSync(files.real, 'utf8')).to.include('SnapshotZipFS'))
|
||||
it('checks non-existance from the real fs', () => expect(fs.existsSync(files.real2)).to.equal(false))
|
||||
it('copies from the real fs', () => expect(() => fs.copyFileSync(files.real, files.real2)).not.to.throw())
|
||||
it('checks existance from the real fs', () => expect(fs.existsSync(files.real2)).to.equal(true))
|
||||
it('unlinks from the real fs', () => expect(() => fs.unlinkSync(files.real2)).not.to.throw())
|
||||
it('symlinks the real fs', () => expect(() => fs.symlinkSync('tmp', files.real2)).not.to.throw())
|
||||
it('readlinks the real fs', () => expect(fs.readlinkSync(files.real2)).to.equal('tmp'))
|
||||
it('truncates the real fs', () => expect(() => fs.truncateSync(files.real)).not.to.throw())
|
||||
it('writeFiles the real fs', () => expect(() => fs.writeFileSync(files.real2, 'testing')).not.to.throw())
|
||||
it('accesses the real fs', () => expect(() => fs.accessSync(files.real)).not.to.throw())
|
||||
it('accesses the zipFs', () => expect(() => fs.accessSync(files.zipFs)).not.to.throw())
|
||||
it('stats the real fs', () => expect(fs.statSync(files.real2).size).to.be.greaterThan(0))
|
||||
it('stats the zipFs', () => expect(fs.statSync(files.zipFs).size).to.be.greaterThan(0))
|
||||
it('readdirs from the zipFs', () => expect(fs.readdirSync(dirname(files.zipFs))).to.include(basename(files.zipFs)))
|
||||
it('readdirs from the real fs', () => expect(fs.readdirSync('.')).to.include(files.real))
|
||||
it('readdirs from both', () => {
|
||||
expect(fs.readdirSync('.')).to.include(files.real),
|
||||
expect(fs.readdirSync('.')).to.include('test')
|
||||
})
|
||||
let dir
|
||||
it('opendirs the zipFs fs', async () => {
|
||||
expect(() => dir = fs.opendirSync(dirname(files.zipFs))).not.to.throw()
|
||||
const names = []
|
||||
for await (const dirent of dir) {
|
||||
names.push(dirent.name)
|
||||
}
|
||||
expect(names).to.include(basename(files.zipFs))
|
||||
})
|
||||
it('opendirs the real fs and zipFs', async () => {
|
||||
expect(() => dir = fs.opendirSync('.')).not.to.throw()
|
||||
const names = []
|
||||
for await (const dirent of dir) {
|
||||
names.push(dirent.name)
|
||||
}
|
||||
expect(names).to.include(files.real)
|
||||
expect(names).to.include('test')
|
||||
})
|
||||
it('createWriteStreams to the real fs', () => expect(() => fs.createWriteStream('createWriteStream')).not.to.throw())
|
||||
it('createReadStreams to the real fs', () => expect(() => fs.createReadStream(files.real)).not.to.throw())
|
||||
it('createReadStreams to the zipFs', () => expect(() => fs.createReadStream(files.zipFs)).not.to.throw())
|
||||
it('renames to the realFs', () => {
|
||||
expect(() => fs.renameSync(files.real2, 'rename')).not.to.throw()
|
||||
expect(fs.existsSync('rename')).to.equal(true)
|
||||
})
|
||||
|
||||
describe('write actions to the zipFs', () => {
|
||||
it(`won't truncate to the zipFs`, () => expect(() => fs.truncateSync(files.zipFs)).to.throw())
|
||||
it(`won't unlink from the zipFs`, () => expect(() => fs.unlinkSync(files.zipFs)).to.throw())
|
||||
it(`won't appendFile to the zipFs`, () => expect(() => fs.appendFileSync(files.zipFs, 'xxx')).to.throw())
|
||||
it(`won't symlink to the zipFs`, () => expect(() => fs.symlinkSync('tmp', files.zipFs)).to.throw())
|
||||
it(`won't chmod to the zipFs`, () => expect(() => fs.chmodSync(files.zipFs, 0o666)).to.throw())
|
||||
it(`won't chown to the zipFs`, () => expect(() => fs.chownSync(files.zipFs, 0, 0)).to.throw())
|
||||
it(`won't writeFile the zipFs`, () => expect(() => fs.writeFileSync(files.zipFs, 'testing')).to.throw())
|
||||
it(`won't createWriteStreams to the zipFs`, () => expect(() => fs.createWriteStream(files.zipFs)).to.throw())
|
||||
it(`won't rename to the zipFs`, () => expect(() => fs.renameSync(files.real, files.zipFs)).to.throw())
|
||||
})
|
||||
|
||||
;[ 'zipFs', 'real' ].forEach((k) => {
|
||||
it(`watches the ${k} fs`, () => expect(() => fs.watch(files[k], () => null).close()).not.to.throw())
|
||||
it(`watchFiles the ${k} fs`, () => expect(() => fs.watchFile(files[k], () => null)).not.to.throw())
|
||||
it(`unwatchFiles the ${k} fs`, () => expect(() => fs.unwatchFile(files[k])).not.to.throw())
|
||||
})
|
||||
let buffer = Buffer.alloc(8092), fd
|
||||
describe('zipfs file descriptors', () => {
|
||||
it('opens files in the zipFs', () => {
|
||||
expect(() => (fd = fs.openSync(files.zipFs, 'a+'))).not.to.throw()
|
||||
expect(fd).to.not.be.null
|
||||
})
|
||||
it('reads file descriptors from the zipFs', () => {
|
||||
expect(() => fs.readSync(fd, buffer)).not.to.throw()
|
||||
expect(buffer.toString()).to.include('SnapshotZipFS')
|
||||
})
|
||||
it('fstats the zipFs', () => expect(fs.fstatSync(fd).size).to.be.greaterThan(0))
|
||||
it(`won't write to the zipFs`, () => expect(() => fs.writeSync(fd, 'xxx')).to.throw())
|
||||
it(`won't fchmod the zipFs`, () => expect(() => fs.fchmodSync(fd)).to.throw())
|
||||
it(`won't fchown the zipFs`, () => expect(() => fs.fchownSync(fd)).to.throw())
|
||||
it(`won't ftruncate the zipFs`, () => expect(() => fs.ftruncateSync(fd)).to.throw())
|
||||
it('closes file descriptors from the zipFs', () => expect(() => fs.closeSync(fd)).not.to.throw())
|
||||
})
|
||||
|
||||
fd = null
|
||||
buffer = Buffer.alloc(8092)
|
||||
describe('real file descriptors', () => {
|
||||
it('opens files in the real fs', () => {
|
||||
expect(() => (fd = fs.openSync(files.real2, 'a+'))).not.to.throw()
|
||||
expect(fd).to.not.be.null
|
||||
})
|
||||
it('reads file descriptors from the real', () => {
|
||||
expect(() => fs.readSync(fd, buffer)).not.to.throw()
|
||||
expect(buffer.toString()).to.include('SnapshotZipFS')
|
||||
})
|
||||
it('will write to the real fs', () => expect(() => fs.writeSync(fd, 'xxx')).not.to.throw())
|
||||
it('fstats the real fs', () => expect(fs.fstatSync(fd).size).to.be.greaterThan(0))
|
||||
it('closes file descriptors from the real fs', () => expect(() => fs.closeSync(fd)).not.to.throw())
|
||||
})
|
||||
|
||||
describe('path clashes', () => {
|
||||
it('delegates directory creation to the real fs', () => expect(() => fs.mkdirSync('test')).not.to.throw())
|
||||
it('delegates directory removal to the real fs', () => expect(() => fs.rmdirSync('test')).not.to.throw())
|
||||
})
|
||||
})
|
||||
+14
-13
@@ -1,7 +1,8 @@
|
||||
import { normalizeOptions } from '../src/options'
|
||||
import { expect } from 'chai'
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import * as path from 'path'
|
||||
|
||||
import { normalizeOptions } from '../src/options.js'
|
||||
|
||||
const ext = process.platform === 'win32' ? '.exe' : ''
|
||||
|
||||
describe('options', () => {
|
||||
@@ -27,13 +28,13 @@ describe('options', () => {
|
||||
expect(options.input).to.equal(path.resolve('./test/fixture/entry.js'))
|
||||
})
|
||||
it('should resolve pathed options against cwd', () => {
|
||||
const cwd = path.join(process.cwd(), 'test/fixture')
|
||||
const options = normalizeOptions({
|
||||
cwd,
|
||||
input: 'entry',
|
||||
output: 'abc',
|
||||
temp: './d'
|
||||
})
|
||||
const cwd = path.join(process.cwd(), 'test/fixture'),
|
||||
options = normalizeOptions({
|
||||
cwd,
|
||||
input: 'entry',
|
||||
output: 'abc',
|
||||
temp: './d',
|
||||
})
|
||||
expect(options.temp).to.equal(path.resolve(cwd, './d'))
|
||||
expect(options.input).to.equal(path.resolve(cwd, 'entry.js'))
|
||||
expect(options.output).to.equal(path.resolve(cwd, `abc${ext}`))
|
||||
@@ -47,13 +48,13 @@ describe('options', () => {
|
||||
})
|
||||
it('should append trailing slash to third-party remote if necessary', () => {
|
||||
const options = normalizeOptions({
|
||||
remote: 'https://sitejs.org/nexe'
|
||||
remote: 'https://sitejs.org/nexe',
|
||||
})
|
||||
expect(options.remote).to.equal('https://sitejs.org/nexe/')
|
||||
})
|
||||
it('should not append trailing slash to third-party remote that already has one', () => {
|
||||
const options = normalizeOptions({
|
||||
remote: 'https://sitejs.org/nexe/'
|
||||
remote: 'https://sitejs.org/nexe/',
|
||||
})
|
||||
expect(options.remote).to.equal('https://sitejs.org/nexe/')
|
||||
})
|
||||
@@ -62,13 +63,13 @@ describe('options', () => {
|
||||
describe('output', () => {
|
||||
it('should work', () => {
|
||||
const options = normalizeOptions({
|
||||
output: './some-output'
|
||||
output: './some-output',
|
||||
})
|
||||
expect(options.output).to.equal(path.resolve(`./some-output${ext}`))
|
||||
})
|
||||
it('should default to the input file name if not index', () => {
|
||||
const options = normalizeOptions({
|
||||
input: './test/fixture'
|
||||
input: './test/fixture',
|
||||
})
|
||||
expect(options.output).to.equal(path.resolve(`./entry${ext}`))
|
||||
})
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
export {}
|
||||
+8
-6
@@ -1,9 +1,11 @@
|
||||
import { padRight, isWindows } from '../src/util'
|
||||
import { expect } from 'chai'
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import chalk from 'chalk'
|
||||
import { getTarget, NexeTarget } from '../src/target'
|
||||
const b = chalk.blue
|
||||
const arch = process.arch === 'ia32' ? 'x86' : process.arch
|
||||
|
||||
import { padRight } from '../src/util.js'
|
||||
import { getTarget } from '../src/target.js'
|
||||
|
||||
const b = chalk.blue,
|
||||
arch = process.arch === 'ia32' ? 'x86' : process.arch
|
||||
|
||||
describe('Targets', () => {
|
||||
;[
|
||||
@@ -16,7 +18,7 @@ describe('Targets', () => {
|
||||
['static-x86-6.10.3', 'alpine-x86-6.10.3'],
|
||||
['linux-x32', `linux-x86-${process.version.slice(1)}`],
|
||||
['alpine-notsupported-6.10.3', `alpine-${arch}-6.10.3`],
|
||||
['not-a-thing', getTarget(process).toString()]
|
||||
['not-a-thing', getTarget(process).toString()],
|
||||
].forEach(([input, expected]) => {
|
||||
it(`should accept: ${padRight(JSON.stringify(input), 53)} ${b('->')} ${expected}`, () => {
|
||||
expect(getTarget(input).toString()).to.equal(expected)
|
||||
|
||||
+17
-7
@@ -1,15 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target":"es2015",
|
||||
"outDir": "./lib/types",
|
||||
"target":"es2020",
|
||||
"lib": ["es2020"],
|
||||
"module": "es2020",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"declaration": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["es2018"],
|
||||
"module": "commonjs",
|
||||
"outDir": "./lib",
|
||||
"strict": true
|
||||
},
|
||||
"include":[
|
||||
"src/**/*.ts"
|
||||
]
|
||||
"src/**/*.ts",
|
||||
],
|
||||
"ts-node": {
|
||||
"transpileOnly": true,
|
||||
"transpiler": "ts-node/transpilers/swc",
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"module": "commonjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"rulesDirectory": ["tslint-plugin-prettier", "tslint-config-prettier"],
|
||||
"linterOptions": {
|
||||
"exclude": ["**/node_modules/**/*"]
|
||||
},
|
||||
"rules": {
|
||||
"prettier": [true, { "semi": false, "printWidth": 100, "singleQuote": true }]
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import * as wcp from 'webpack-config-prefabs'
|
||||
export = wcp.nodeLibrary(module, {
|
||||
enableTypescript: true,
|
||||
entry: './src/fs/patch.ts',
|
||||
minimize: false,
|
||||
outputFilepath: './lib/fs/patch.bundle.js',
|
||||
});
|
||||
Reference in New Issue
Block a user