Compare commits

..

15 Commits

Author SHA1 Message Date
calebboyd 6a9434e1ff fix: cleanup zip finalization 2023-04-04 09:12:07 -05:00
Bryce Gibson 049fda37ac WIP: ensure the stream is consumed before end/close are emitted. 2023-04-03 09:10:23 +10:00
calebboyd 7febbbd89d feat: stream zip files to destination binary 2023-04-02 12:06:26 -05:00
calebboyd 5d5513d264 refactor: compile with esmoudle interop 2023-04-02 12:02:35 -05:00
calebboyd 7a8625d92b refactor: move lodash to dev deps 2023-04-02 12:01:44 -05:00
Bryce Gibson ec8597c124 Remove highland as a dependency. 2023-03-29 10:39:35 +11:00
Bryce Gibson 053712c8d3 Fix tsc on Windows - globbing doesn't work there.
There's probably a cleaner way to do this...
2023-03-29 10:39:35 +11:00
Bryce Gibson 60747897dc Try running the integration test. 2023-03-29 10:39:35 +11:00
Bryce Gibson 59c03cd2f5 Attempt to fix ci. 2023-03-29 10:39:35 +11:00
Bryce Gibson cccf51f9bf Add test suite using the built artefact.
Smoke tests the different fs methods.

Not perfect in any world, but better than nothing.
2023-03-29 10:39:32 +11:00
Bryce Gibson 25626c8d92 Use yarn's fslib to implement the fake filesystem.
Leverage the work done by yarn, rather than reimplementing from scratch.

This means the fake fs is now reading from a zip file in the binary.

A few methods need custom implementations as the fake fs is not
writable. These were copied and adjusted from the yarn ZipOpenFS.
2023-03-27 17:15:10 +11:00
Bryce Gibson ba3b91eaf1 Generate zip file bundle. 2023-03-22 20:46:19 +11:00
Bryce Gibson fc09a7d903 Rigidify code replacement by using Lodash templates. 2023-03-22 20:46:18 +11:00
Bryce Gibson bbaa63767d Fix got typescript issues. 2023-03-22 20:44:54 +11:00
Bryce Gibson c3baa1c2a3 Update types to fix rimraf promisify. 2023-03-22 20:44:52 +11:00
11 changed files with 552 additions and 634 deletions
+442 -486
View File
File diff suppressed because it is too large Load Diff
+19 -17
View File
@@ -44,22 +44,21 @@
] ]
}, },
"dependencies": { "dependencies": {
"@calebboyd/semaphore": "^1.3.1", "@yarnpkg/fslib": "^2.6.0-rc.8",
"@yarnpkg/fslib": "^3.0.0-rc.43", "@yarnpkg/libzip": "^2.2.2",
"@yarnpkg/libzip": "^3.0.0-rc.43",
"app-builder": "^7.0.4", "app-builder": "^7.0.4",
"archiver": "^5.3.1", "archiver": "^5.3.1",
"caw": "^2.0.1", "caw": "^2.0.1",
"chalk": "^2.4.2", "chalk": "^2.4.2",
"download": "^8.0.0", "download": "^8.0.0",
"globby": "^11.0.2", "globby": "^11.0.2",
"got": "^12.6.0", "got": "^11.8.2",
"meriyah": "^4.3.5", "meriyah": "^4.3.3",
"minimist": "^1.2.8", "minimist": "^1.2.6",
"mkdirp": "^1.0.4", "mkdirp": "^1.0.4",
"multistream": "^4.1.0", "multistream": "^4.1.0",
"ora": "^3.4.0", "ora": "^3.4.0",
"resolve-dependencies": "^6.0.9", "resolve-dependencies": "^6.0.8",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"run-script-os": "^1.1.6", "run-script-os": "^1.1.6",
"webpack-config-prefabs": "0.0.5" "webpack-config-prefabs": "0.0.5"
@@ -67,26 +66,29 @@
"devDependencies": { "devDependencies": {
"@types/archiver": "^5.3.2", "@types/archiver": "^5.3.2",
"@types/chai": "^4", "@types/chai": "^4",
"@types/download": "^8", "@types/download": "^6",
"@types/globby": "^9", "@types/globby": "^9",
"@types/lodash": "^4.14.192", "@types/got": "^9.6.12",
"@types/highland": "^2.12.14",
"@types/lodash": "^4.14.173",
"@types/minimist": "^1.2.2", "@types/minimist": "^1.2.2",
"@types/mkdirp": "^1.0.2", "@types/mkdirp": "^1.0.2",
"@types/mocha": "^10.0.1", "@types/mocha": "^9.0.0",
"@types/multistream": "^4.1.0", "@types/multistream": "^4.1.0",
"@types/node": "^16.10.2",
"@types/ora": "^3.2.0", "@types/ora": "^3.2.0",
"@types/rimraf": "3.0.2", "@types/rimraf": "3.0.2",
"@types/semver": "^7.3.13", "@types/semver": "^7.3.8",
"chai": "^4.3.7", "chai": "^4.3.6",
"execa": "^5.1.1", "execa": "^5.1.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mocha": "^10.2.0", "mocha": "^10.0.0",
"prettier": "^2.8.7", "prettier": "^2.6.2",
"ts-node": "^10.9.1", "ts-node": "^10.7.0",
"tslint": "^6.1.1", "tslint": "^6.1.1",
"tslint-config-prettier": "^1.18.0", "tslint-config-prettier": "^1.18.0",
"tslint-plugin-prettier": "^2.3.0", "tslint-plugin-prettier": "^2.3.0",
"typescript": "^5.0.3", "typescript": "^4.6.4",
"webpack-cli": "^5.0.1" "webpack-cli": "^4.8.0"
} }
} }
+7 -13
View File
@@ -298,19 +298,13 @@ export class NexeCompiler {
const sentinel = Buffer.from('<nexe~~sentinel>') const sentinel = Buffer.from('<nexe~~sentinel>')
let vfsSize = 0 let vfsSize = 0
const streams = [ const streams = [binary, toStream(launchCode), this.bundle.toStream().pipe(new Transform({
binary, transform: (chunk, _, cb) => {
toStream(launchCode), vfsSize || this.bundle.finalize()
this.bundle.toStream().pipe( chunk && (vfsSize += chunk.length)
new Transform({ cb(null, chunk)
transform: (chunk, _, cb) => { },
vfsSize || this.bundle.finalize() }))]
chunk && (vfsSize += chunk.length)
cb(null, chunk)
},
})
),
]
let done = false let done = false
return new MultiStream((cb) => { return new MultiStream((cb) => {
+28 -40
View File
@@ -2,19 +2,14 @@ import { Libzip } from '@yarnpkg/libzip'
import { import {
FakeFS, FakeFS,
PortablePath, PortablePath,
ZipFS,
ZipOpenFS,
RmdirOptions, RmdirOptions,
OpendirOptions, OpendirOptions,
BasePortableFakeFS,
Dirent,
Dir,
MkdirOptions,
FSPath,
ppath,
npath,
Filename,
CustomDir,
} from '@yarnpkg/fslib' } from '@yarnpkg/fslib'
import { ZipFS, ZipOpenFS } from '@yarnpkg/libzip' 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 { resolve, toNamespacedPath } from 'path'
import { constants } from 'fs' import { constants } from 'fs'
@@ -22,17 +17,10 @@ export type SnapshotZipFSOptions = {
baseFs: FakeFS<PortablePath> baseFs: FakeFS<PortablePath>
libzip: Libzip | (() => Libzip) libzip: Libzip | (() => Libzip)
zipFs: ZipFS zipFs: ZipFS
root: string
} }
const uniqBy = (arr: Array<string | Dirent>, pick: (...arg: any[]) => any) => { const uniqBy = (arr: Array<string | Dirent>, pick: (...arg: any[]) => any) => {
const seen = new Set() return arr.filter((x, i, self) => i === self.findIndex((y) => pick(x) === pick(y)))
return arr.filter((x) => {
const key = pick(x)
if (seen.has(key)) return false
seen.add(key)
return true
})
} }
function uniqReaddir(arr: Array<string | Dirent>) { function uniqReaddir(arr: Array<string | Dirent>) {
return uniqBy(arr, (s: string | Dirent) => (typeof s === 'string' ? s : s.name)) return uniqBy(arr, (s: string | Dirent) => (typeof s === 'string' ? s : s.name))
@@ -41,14 +29,10 @@ function uniqReaddir(arr: Array<string | Dirent>) {
export class SnapshotZipFS extends BasePortableFakeFS { export class SnapshotZipFS extends BasePortableFakeFS {
zipFs: ZipFS zipFs: ZipFS
baseFs: FakeFS<PortablePath> baseFs: FakeFS<PortablePath>
root: string
magic: number
constructor(opts: SnapshotZipFSOptions) { constructor(opts: SnapshotZipFSOptions) {
super() super()
this.zipFs = opts.zipFs this.zipFs = opts.zipFs
this.baseFs = opts.baseFs this.baseFs = opts.baseFs
this.root = opts.root
this.magic = 0x2a << 24
} }
private readonly fdMap: Map<number, [ZipFS, number]> = new Map() private readonly fdMap: Map<number, [ZipFS, number]> = new Map()
private nextFd = 3 private nextFd = 3
@@ -90,42 +74,46 @@ export class SnapshotZipFS extends BasePortableFakeFS {
} }
async realpathPromise(p: PortablePath) { async realpathPromise(p: PortablePath) {
return await this.realpathSync(p) return await this.makeCallPromise(
p,
async () => {
return await this.baseFs.realpathPromise(p)
},
async (zipFs, { subPath }) => {
return await zipFs.realpathPromise(subPath)
}
)
} }
realpathSync(p: PortablePath) { realpathSync(p: PortablePath) {
return this.makeCallSync( return this.makeCallSync(
p, p,
() => this.baseFs.realpathSync(p), () => {
return this.baseFs.realpathSync(p)
},
(zipFs, { subPath }) => { (zipFs, { subPath }) => {
if (zipFs.lstatSync(subPath).isSymbolicLink()) { return zipFs.realpathSync(subPath)
return zipFs.realpathSync(subPath)
} else {
// return the original path in case it wasn't under /snapshot, e.g. if it was for a node module - otherwise the node module parent path is the wrong one (and other things resolve relative to that)
return p
}
} }
) )
} }
findZip(p: PortablePath) { findZip(p: PortablePath) {
p = this.resolve(p) p = this.resolve(p)
const snapshotPP = npath.toPortablePath('/snapshot')
const pathsToTry = Array.from( const pathsToTry = Array.from(
new Set([ new Set([
p, p,
resolve('/snapshot', p), resolve('/snapshot', p),
ppath.resolve( ppath.resolve(
snapshotPP, npath.toPortablePath('/snapshot'),
npath.toPortablePath( npath.toPortablePath(
npath.relative(npath.fromPortablePath(this.root), npath.fromPortablePath(p)) npath.relative(npath.fromPortablePath(process.cwd()), npath.fromPortablePath(p))
) )
), ),
ppath.resolve( ppath.resolve(
snapshotPP, npath.toPortablePath('/snapshot'),
npath.toPortablePath( npath.toPortablePath(
npath.relative( npath.relative(
toNamespacedPath(npath.fromPortablePath(this.root)), toNamespacedPath(npath.fromPortablePath(process.cwd())),
toNamespacedPath(npath.fromPortablePath(p)) toNamespacedPath(npath.fromPortablePath(p))
) )
) )
@@ -248,7 +236,9 @@ export class SnapshotZipFS extends BasePortableFakeFS {
async (zipFs, { subPath }) => { async (zipFs, { subPath }) => {
const fallbackPaths: Array<string | Dirent> = await fallback().catch(() => []) const fallbackPaths: Array<string | Dirent> = await fallback().catch(() => [])
return Promise.resolve( return Promise.resolve(
uniqReaddir(fallbackPaths.concat(await zipFs.readdirPromise(subPath, opts as any))) uniqReaddir(
(fallbackPaths as any[]).concat(await zipFs.readdirPromise(subPath, opts as any))
)
) )
}, },
{ {
@@ -273,7 +263,7 @@ export class SnapshotZipFS extends BasePortableFakeFS {
try { try {
fallbackPaths = fallback() fallbackPaths = fallback()
} catch (e) {} } catch (e) {}
return fallbackPaths.concat(uniqReaddir(zipFs.readdirSync(subPath, opts as any))) return (fallbackPaths as any[]).concat(uniqReaddir(zipFs.readdirSync(subPath, opts as any)))
}, },
{ {
requireSubpath: false, requireSubpath: false,
@@ -298,7 +288,7 @@ export class SnapshotZipFS extends BasePortableFakeFS {
} }
async opendirPromise(p: PortablePath, opts?: OpendirOptions) { async opendirPromise(p: PortablePath, opts?: OpendirOptions) {
return this.opendirSync(p, opts) return Promise.resolve(this.zipFs.opendirSync(p, opts))
} }
opendirSync(p: PortablePath, opts?: OpendirOptions) { opendirSync(p: PortablePath, opts?: OpendirOptions) {
@@ -381,8 +371,6 @@ export class SnapshotZipFS extends BasePortableFakeFS {
unwatchFile = ZipOpenFS.prototype.unwatchFile unwatchFile = ZipOpenFS.prototype.unwatchFile
utimesPromise = ZipOpenFS.prototype.utimesPromise utimesPromise = ZipOpenFS.prototype.utimesPromise
utimesSync = ZipOpenFS.prototype.utimesSync utimesSync = ZipOpenFS.prototype.utimesSync
lutimesPromise = ZipOpenFS.prototype.utimesPromise
lutimesSync = ZipOpenFS.prototype.utimesSync
watch = ZipOpenFS.prototype.watch watch = ZipOpenFS.prototype.watch
watchFile = ZipOpenFS.prototype.watchFile watchFile = ZipOpenFS.prototype.watchFile
writeFilePromise = ZipOpenFS.prototype.writeFilePromise writeFilePromise = ZipOpenFS.prototype.writeFilePromise
+20 -27
View File
@@ -1,9 +1,8 @@
import { ZipFS, getLibzipSync } from '@yarnpkg/libzip' import { getLibzipSync } from '@yarnpkg/libzip'
import { patchFs, npath, PosixFS, NodeFS } from '@yarnpkg/fslib' import { patchFs, npath, PosixFS, NodeFS, ZipFS } from '@yarnpkg/fslib'
import { SnapshotZipFS } from './SnapshotZipFS' import { SnapshotZipFS } from './SnapshotZipFS'
import * as assert from 'assert' import * as assert from 'assert'
import * as constants from 'constants' import * as constants from 'constants'
import { dirname } from 'path'
export interface NexeHeader { export interface NexeHeader {
blobPath: string blobPath: string
@@ -18,7 +17,6 @@ export interface NexeHeader {
let originalFsMethods: any = null let originalFsMethods: any = null
let lazyRestoreFs = () => {} let lazyRestoreFs = () => {}
const patches = (process as any).nexe.patches || {} const patches = (process as any).nexe.patches || {}
const originalPatches = { ...patches }
delete (process as any).nexe delete (process as any).nexe
function shimFs(binary: NexeHeader, fs: typeof import('fs') = require('fs')) { function shimFs(binary: NexeHeader, fs: typeof import('fs') = require('fs')) {
@@ -42,65 +40,60 @@ function shimFs(binary: NexeHeader, fs: typeof import('fs') = require('fs')) {
) )
assert.equal(bytesRead, binary.layout.resourceSize) assert.equal(bytesRead, binary.layout.resourceSize)
const zipFs = new ZipFS(blob, { readOnly: true }) const zipFs = new ZipFS(blob, {
libzip: getLibzipSync(),
readOnly: true,
})
const snapshotZipFS = new SnapshotZipFS({ const snapshotZipFS = new SnapshotZipFS({
libzip: getLibzipSync(), libzip: getLibzipSync(),
zipFs, zipFs,
baseFs: nodeFs, baseFs: nodeFs,
root: dirname(process.argv[0]),
}) })
const posixSnapshotZipFs = new PosixFS(snapshotZipFS) const posixSnapshotZipFs = new PosixFS(snapshotZipFS)
patchFs(fs, posixSnapshotZipFs) patchFs(fs, posixSnapshotZipFs)
const { readFileSync } = fs
let log = (_: string) => true let log = (_: string) => true
let loggedManifest = false
if ((process.env.DEBUG || '').toLowerCase().includes('nexe:require')) { if ((process.env.DEBUG || '').toLowerCase().includes('nexe:require')) {
process.stderr.write(
// @ts-ignore
`[nexe] - FILES ${JSON.stringify(Array.from(zipFs.entries.keys()), null, 4)}\n`
)
process.stderr.write(
// @ts-ignore
`[nexe] - DIRECTORIES ${JSON.stringify(Array.from(zipFs.listings.keys()), null, 4)}\n`
)
log = (text: string) => { log = (text: string) => {
return process.stderr.write(`[nexe] - ${text}\n`) if (!loggedManifest) {
// TODO anything meaningful to log here?
loggedManifest = true
}
return process.stderr.write('[nexe] - ' + text + '\n')
} }
} }
function internalModuleReadFile(this: any, original: any, ...args: any[]) { patches.internalModuleReadFile = function (this: any, original: any, ...args: any[]) {
log(`internalModuleReadFile ${args[0]}`) log(`internalModuleReadFile ${args[0]}`)
try { try {
return posixSnapshotZipFs.readFileSync(args[0], 'utf-8') return fs.readFileSync(args[0], 'utf-8')
} catch (e) { } catch (e) {
return '' return null
} }
} }
if (patches.internalModuleReadFile) {
patches.internalModuleReadFile = internalModuleReadFile
}
let returningArray: boolean let returningArray: boolean
patches.internalModuleReadJSON = function (this: any, original: any, ...args: any[]) { patches.internalModuleReadJSON = function (this: any, original: any, ...args: any[]) {
if (returningArray == null) returningArray = Array.isArray(original.call(this, '')) if (returningArray == null) returningArray = Array.isArray(original.call(this, ''))
const res = internalModuleReadFile.call(this, original, ...args) const res = patches.internalModuleReadFile.call(this, original, ...args)
return returningArray && !Array.isArray(res) return returningArray && !Array.isArray(res)
? res === '' ? [res, /"(main|name|type|exports|imports)"/.test(res)]
? []
: [res, /"(main|name|type|exports|imports)"/.test(res)]
: res : res
} }
patches.internalModuleStat = function (this: any, original: any, ...args: any[]) { patches.internalModuleStat = function (this: any, original: any, ...args: any[]) {
log(`internalModuleStat ${args[0]}`) log(`internalModuleStat ${args[0]}`)
try { try {
const stat = posixSnapshotZipFs.statSync(args[0]) const stat = fs.statSync(args[0])
if (stat.isDirectory()) return 1 if (stat.isDirectory()) return 1
return 0 return 0
} catch (e) { } catch (e) {
return -constants.ENOENT return -constants.ENOENT
} }
} }
// TODO restore patches in restoreFs
lazyRestoreFs = () => { lazyRestoreFs = () => {
Object.assign(fs, originalFsMethods) Object.assign(fs, originalFsMethods)
Object.assign(patches, originalPatches)
lazyRestoreFs = () => {} lazyRestoreFs = () => {}
} }
} }
+4 -3
View File
@@ -1,14 +1,15 @@
const fs = require('fs'), const fs = require('fs'),
fd = fs.openSync(process.execPath, 'r'), fd = fs.openSync(process.execPath, 'r'),
stat = fs.statSync(process.execPath), stat = fs.statSync(process.execPath),
tailSize = Math.min(stat.size, 16000), tailSize = Math.min(stat.size, 4096),
tailWindow = Buffer.from(Array(tailSize)) tailWindow = Buffer.from(Array(tailSize))
fs.readSync(fd, tailWindow, 0, tailSize, stat.size - tailSize) fs.readSync(fd, tailWindow, 0, tailSize, stat.size - tailSize)
const footerPosition = tailWindow.indexOf('<nexe~~sentinel>') const footerPosition = tailWindow.indexOf('<nexe~~sentinel>')
if (footerPosition == -1) { if (footerPosition === -1) {
throw 'Invalid Nexe binary' process.stderr.write('Invalid Nexe binary')
process.exit(1)
} }
const footer = tailWindow.slice(footerPosition, footerPosition + 32), const footer = tailWindow.slice(footerPosition, footerPosition + 32),
+7 -16
View File
@@ -55,22 +55,13 @@ export default async function main(compiler: NexeCompiler, next: () => Promise<v
}) })
const fileLines = file.contents.toString().split('\n') const fileLines = file.contents.toString().split('\n')
if (semverGt(version, '19.99')) { fileLines.splice(
await compiler.replaceInFileAsync( location.start.line,
'lib/internal/modules/cjs/loader.js', 0,
"'use strict';", '{{ file("lib/fs/bootstrap.js") }}' +
"'use strict';\n" + '{{ file("lib/fs/bootstrap.js") }}' + '\n' '\n' +
) (semverGt(version, '11.99') ? 'expandArgv1 = false;\n' : '')
fileLines.splice(location.start.line, 0, 'expandArgv1 = false;') )
} else {
fileLines.splice(
location.start.line,
0,
'{{ file("lib/fs/bootstrap.js") }}' +
'\n' +
(semverGt(version, '11.99') ? 'expandArgv1 = false;\n' : '')
)
}
file.contents = fileLines.join('\n') file.contents = fileLines.join('\n')
if (semverGt(version, '11.99')) { if (semverGt(version, '11.99')) {
+1 -1
View File
@@ -58,7 +58,7 @@ export default async function downloadNode(compiler: NexeCompiler, next: () => P
{ sourceUrl, downloadOptions, build } = compiler.options, { sourceUrl, downloadOptions, build } = compiler.options,
url = sourceUrl || `https://nodejs.org/dist/v${version}/node-v${version}.tar.gz`, url = sourceUrl || `https://nodejs.org/dist/v${version}/node-v${version}.tar.gz`,
step = log.step( 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), exeLocation = compiler.getNodeExecutableLocation(build ? undefined : target),
downloadExists = await pathExistsAsync(build ? src : exeLocation) downloadExists = await pathExistsAsync(build ? src : exeLocation)
+12 -6
View File
@@ -58,11 +58,17 @@ export async function runDockerBuild(target: NexeTarget) {
appendFileSync(outFilename, x.stderr) appendFileSync(outFilename, x.stderr)
appendFileSync(outFilename, x.stdout) appendFileSync(outFilename, x.stdout)
}) })
await got(`https://transfer.sh/${Math.random().toString(36).substring(2)}.txt`, { try {
body: await readFileAsync(outFilename), const { body } = await got(
method: 'PUT', `https://transfer.sh/${Math.random().toString(36).substring(2)}.txt`,
}) {
.then((x) => console.log('Posted docker log: ', x.body)) body: await readFileAsync(outFilename),
.catch((e) => console.log('Error posting log', e)) method: 'PUT',
}
)
console.log('Posted docker log: ', body)
} catch (e) {
console.log('Error posting log', e)
}
} }
} }
+8 -13
View File
@@ -1,22 +1,17 @@
const { mkdtemp, copyFile } = require('fs/promises') const { mkdtemp } = require('fs/promises')
const os = require('os') const os = require('os')
const path = require('path') const path = require('path')
const rimraf = require('rimraf') const rimraf = require('rimraf')
const cp = require('child_process')
async function runTests() { async function runTests() {
const tempdir = await mkdtemp(path.join(os.tmpdir(), 'nexe-integration-tests-')) const tempdir = await mkdtemp(path.join(os.tmpdir(), 'nexe-'))
const executable = path.join(tempdir, path.basename(process.argv[0]))
await copyFile(process.argv[0], executable)
process.on('beforeExit', () => rimraf.sync(tempdir)) process.on('beforeExit', () => rimraf.sync(tempdir))
cp.spawn(executable, process.chdir(tempdir)
[ const Mocha = require('mocha')
path.join(tempdir, 'node_modules/mocha/bin/mocha.js'), const mocha = new Mocha()
path.join(tempdir, 'test/integration/tests.integration-spec.js') mocha.addFile('test/integration/tests.integration-spec.js')
], mocha.run((failures) => {
{ stdio: ['inherit', 'inherit', 'inherit', 'ipc'], cwd: tempdir } process.exitCode = failures ? 1 : 0
).on('exit', (code) => {
process.exitCode = code
}) })
} }
+4 -12
View File
@@ -1,6 +1,6 @@
const { expect } = require('chai') const { expect } = require('chai')
const fs = require('fs') const fs = require('fs')
const { dirname, basename } = require('path') const { dirname, basename }= require('path')
const files = { zipFs: 'test/integration/tests.integration-spec.js', real: 'real', real2: 'real2' } const files = { zipFs: 'test/integration/tests.integration-spec.js', real: 'real', real2: 'real2' }
describe('SnapshotZipFS', () => { describe('SnapshotZipFS', () => {
@@ -88,6 +88,7 @@ describe('SnapshotZipFS', () => {
it(`won't ftruncate the zipFs`, () => expect(() => fs.ftruncateSync(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()) it('closes file descriptors from the zipFs', () => expect(() => fs.closeSync(fd)).not.to.throw())
}) })
fd = null fd = null
buffer = Buffer.alloc(8092) buffer = Buffer.alloc(8092)
describe('real file descriptors', () => { describe('real file descriptors', () => {
@@ -95,7 +96,7 @@ describe('SnapshotZipFS', () => {
expect(() => (fd = fs.openSync(files.real2, 'a+'))).not.to.throw() expect(() => (fd = fs.openSync(files.real2, 'a+'))).not.to.throw()
expect(fd).to.not.be.null expect(fd).to.not.be.null
}) })
it('reads file descriptors from the real fs', () => { it('reads file descriptors from the real', () => {
expect(() => fs.readSync(fd, buffer)).not.to.throw() expect(() => fs.readSync(fd, buffer)).not.to.throw()
expect(buffer.toString()).to.include('SnapshotZipFS') expect(buffer.toString()).to.include('SnapshotZipFS')
}) })
@@ -103,18 +104,9 @@ describe('SnapshotZipFS', () => {
it('fstats the real fs', () => expect(fs.fstatSync(fd).size).to.be.greaterThan(0)) 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()) it('closes file descriptors from the real fs', () => expect(() => fs.closeSync(fd)).not.to.throw())
}) })
describe('path clashes', () => { describe('path clashes', () => {
it('delegates directory creation to the real fs', () => expect(() => fs.mkdirSync('test')).not.to.throw()) 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()) it('delegates directory removal to the real fs', () => expect(() => fs.rmdirSync('test')).not.to.throw())
}) })
describe('requiring modules', () => {
it('resolves modules from the zipFs', () => expect(() => require('resolve-dependencies')).not.to.throw())
it('resolves modules from the real fs', () => {
fs.mkdirSync('node_modules')
fs.mkdirSync('node_modules/fake-package')
fs.writeFileSync('node_modules/fake-package/package.json', '{ "name": "fake-package", "main": "some-file.js"}')
fs.writeFileSync('node_modules/fake-package/some-file.js', `module.exports = { fake: 'package' }`)
expect(require('fake-package')).to.eql({ fake: 'package' })
})
})
}) })