diff --git a/.gitignore b/.gitignore index 7182a48d116..2e90e788faa 100644 --- a/.gitignore +++ b/.gitignore @@ -46,7 +46,6 @@ scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js scripts/generateLocalizedDiagnosticMessages.js scripts/*.js.map scripts/typings/ -scripts/*/dist coverage/ internal/ **/.DS_Store diff --git a/.travis.yml b/.travis.yml index ff0cfe78115..8ec2411cea9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: node_js node_js: - 'stable' - '8' + - '6' sudo: false diff --git a/Gulpfile.ts b/Gulpfile.ts index d6d047b9e2d..7f7e525564c 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -43,7 +43,7 @@ const constEnumCaptureRegexp = /^(\s*)(export )?const enum (\S+) {(\s*)$/gm; const constEnumReplacement = "$1$2enum $3 {$4"; const cmdLineOptions = minimist(process.argv.slice(2), { - boolean: ["debug", "inspect", "light", "colors", "lint", "soft", "bail"], + boolean: ["debug", "inspect", "light", "colors", "lint", "soft"], string: ["browser", "tests", "host", "reporter", "stackTraceLimit", "timeout"], alias: { "b": "browser", @@ -67,7 +67,6 @@ const cmdLineOptions = minimist(process.argv.slice(2), { runners: process.env.runners || process.env.runner || process.env.ru, light: process.env.light === undefined || process.env.light !== "false", reporter: process.env.reporter || process.env.r, - bail: false, lint: process.env.lint || true, workers: process.env.workerCount || os.cpus().length, } @@ -577,7 +576,7 @@ gulp.task(specMd, /*help*/ false, [word2mdJs], (done) => { gulp.task("generate-spec", "Generates a Markdown version of the Language Specification", [specMd]); -gulp.task("clean", "Cleans the compiler output, declare files, and tests", ["clean:private-packages"], () => { +gulp.task("clean", "Cleans the compiler output, declare files, and tests", [], () => { return del([builtDirectory]); }); @@ -603,37 +602,10 @@ gulp.task("LKG", "Makes a new LKG out of the built js files", ["clean", "dontUse return runSequence("LKGInternal", "VerifyLKG"); }); -function compilePrivatePackage(packageName: string) { - const project = tsc.createProject(`scripts/${packageName}/tsconfig.json`, getCompilerSettings({}, /*useBuiltCompiler*/ false)); - return project.src() - .pipe(sourcemaps.init()) - .pipe(newer(`scripts/${packageName}/dist/index.js`)) - .pipe(project()) - .pipe(sourcemaps.write(".", { sourceRoot: "../src", includeContent: false, destPath: `scripts/${packageName}/dist` })) - .pipe(gulp.dest(`scripts/${packageName}/dist`)); -} - -function cleanPrivatePackage(packageName: string) { - return del([`scripts/${packageName}/dist`]); -} - -gulp.task("vfs-core", () => compilePrivatePackage("vfs-core")); -gulp.task("vfs-errors", () => compilePrivatePackage("vfs-errors")); -gulp.task("vfs-path", ["vfs-core", "vfs-errors"], () => compilePrivatePackage("vfs-path")); -gulp.task("vfs", ["vfs-core", "vfs-errors", "vfs-path"], () => compilePrivatePackage("vfs")); -gulp.task("harness-core", ["vfs-core"], () => compilePrivatePackage("harness-core")); -gulp.task("private-packages", ["vfs", "harness-core"]); - -gulp.task("clean:vfs-core", () => cleanPrivatePackage("vfs-core")); -gulp.task("clean:vfs-errors", () => cleanPrivatePackage("vfs-errors")); -gulp.task("clean:vfs-path", ["clean:vfs-core", "clean:vfs-errors"], () => cleanPrivatePackage("vfs-path")); -gulp.task("clean:vfs", ["clean:vfs-core", "clean:vfs-errors", "clean:vfs-path"], () => cleanPrivatePackage("vfs")); -gulp.task("clean:harness-core", ["clean:vfs-core"], () => cleanPrivatePackage("harness-core")); -gulp.task("clean:private-packages", ["clean:vfs", "clean:harness-core"]); // Task to build the tests infrastructure using the built compiler const run = path.join(builtLocalDirectory, "run.js"); -gulp.task(run, /*help*/ false, [servicesFile, tsserverLibraryFile, "private-packages"], () => { +gulp.task(run, /*help*/ false, [servicesFile, tsserverLibraryFile], () => { const testProject = tsc.createProject("src/harness/tsconfig.json", getCompilerSettings({}, /*useBuiltCompiler*/ true)); return testProject.src() .pipe(newer(run)) @@ -672,7 +644,7 @@ function restoreSavedNodeEnv() { process.env.NODE_ENV = savedNodeEnv; } -function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: (e?: any) => void, noExit?: boolean) { +function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: (e?: any) => void) { const lintFlag = cmdLineOptions.lint; cleanTestDirs((err) => { if (err) { console.error(err); failWithStatus(err, 1); } @@ -683,7 +655,6 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: const runners = cmdLineOptions.runners; const light = cmdLineOptions.light; const stackTraceLimit = cmdLineOptions.stackTraceLimit; - const bail = cmdLineOptions.bail; const testConfigFile = "test.config"; if (fs.existsSync(testConfigFile)) { fs.unlinkSync(testConfigFile); @@ -736,9 +707,6 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: else { args.push("-t", testTimeout); } - if (bail) { - args.push("--bail"); - } args.push(run); setNodeEnvToDevelopment(); exec(mocha, args, lintThenFinish, finish); @@ -752,10 +720,8 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: }); function failWithStatus(err?: any, status?: number) { - if (!noExit) { - if (err || status) { - process.exit(typeof status === "number" ? status : 2); - } + if (err || status) { + process.exit(typeof status === "number" ? status : 2); } done(); } @@ -1149,5 +1115,5 @@ gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: gulp.task("default", "Runs 'local'", ["local"]); gulp.task("watch", "Watches the src/ directory for changes and executes runtests-parallel.", [], () => { - gulp.watch(["src/**/*.*"], ["runtests-parallel"]); -}); \ No newline at end of file + gulp.watch("src/**/*.*", ["runtests-parallel"]); +}); diff --git a/Jakefile.js b/Jakefile.js index fc4267c87ca..2714c416bf9 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -228,176 +228,6 @@ var compilerFilename = "tsc.js"; var LKGCompiler = path.join(LKGDirectory, compilerFilename); var builtLocalCompiler = path.join(builtLocalDirectory, compilerFilename); -function execAsync(cmd, callback) { - var ex = jake.createExec([cmd]); - // Add listeners for output and error - ex.addListener("stdout", function (output) { - process.stdout.write(output); - }); - ex.addListener("stderr", function (error) { - process.stderr.write(error); - }); - ex.addListener("cmdEnd", function () { - if (callback) { - callback(); - } - }); - ex.addListener("error", function (error) { - if (callback) { - callback(error || new Error()); - } - }); - ex.run(); -} - -function execNpmInstall(packages, callback) { - var cmd = "npm install --no-save " + packages.join(" "); - console.log(cmd); - execAsync(cmd, callback); -} - -/** - * Executes the compiler - * @param {boolean} useBuiltCompiler - * @param {string[]} args - * @param {function([Error]): void} [callback] A callback to execute after the compilation process ends. - */ -function execCompiler(useBuiltCompiler, args, callback) { - var compilerPath = useBuiltCompiler ? builtLocalCompiler : LKGCompiler; - var cmd = host + " " + compilerPath + " " + args.join(" "); - console.log(cmd + "\n"); - execAsync(cmd, callback); -} - -/** Compiles a file from a list of sources - * @param {string} outFile value for '--out' command line option - * @param {string[]} sources an array of the names of the source files - * @param {string[]} prefixes a list of files to prepend to the target file - * @param {boolean} useBuiltCompiler true to use the built compiler, false to use the LKG - * @param {object} [opts] property bag containing auxiliary options - * @param {string} [opts.outDir] value for '--outDir' command line option - * @param {boolean} [opts.noOutFile] true to compile without using --out - * @param {string[]} [opts.types] array of types to include in compilation - * @param {string} [opts.target] compilation target (default 'es5'). - * @param {string} [opts.lib] explicit libs to include. - * @param {boolean} [opts.generateDeclarations] true to compile using --declaration - * @param {boolean} [opts.keepComments] false to compile using --removeComments - * @param {boolean} [opts.preserveConstEnums] true if compiler should keep const enums in code - * @param {boolean} [opts.noResolve] true if compiler should not include non-rooted files in compilation - * @param {boolean} [opts.stripInternal] true if compiler should remove declarations marked as @internal - * @param {boolean} [opts.sourceMap] true if the compiler should emit source maps - * @param {boolean} [opts.inlineSourceMap] true if compiler should inline sourceMap - * @param {boolean} [opts.allowUnused] Allow unused locals and identifiers. - * @param {boolean} [opts.strict] Compiles with '--strict' - * @param {string} [opts.project] Compiles with '-p' - * @param {function():void} [callback] a function to execute after the compilation process ends - */ -function compile(outFile, sources, prefixes, useBuiltCompiler, opts, callback) { - var startCompileTime = mark(); - opts = opts || {}; - var options = [ - "--noImplicitAny", - "--noImplicitThis", - "--alwaysStrict", - "--noEmitOnError", - "--pretty", - "--newLine LF" - ]; - - if (opts.strict) { - options.push("--strict"); - } - - if (!opts.allowUnused) { - options.push("--noUnusedLocals", "--noUnusedParameters"); - } - - if (opts.types) { - options.push("--types", opts.types.join(",")); - } - - // Keep comments when specifically requested - // or when in debug mode. - if (!(opts.keepComments || useDebugMode)) { - options.push("--removeComments"); - } - - if (opts.generateDeclarations) { - options.push("--declaration"); - } - - if (opts.preserveConstEnums || useDebugMode) { - options.push(" --preserveConstEnums"); - } - - if (!opts.noOutFile) { - options.push("--out", outFile); - } - else { - if (opts.outDir) { - options.push("--outDir", opts.outDir); - } - options.push("--module", "commonjs"); - } - - if (opts.noResolve) { - options.push("--noResolve"); - } - - if (opts.inlineSourceMap || opts.sourceMap || useDebugMode) { - if (opts.inlineSourceMap) { - options.push("--inlineSourceMap", "--inlineSources"); - } - else { - options.push("-sourcemap"); - } - } - - if (opts.stripInternal) { - options.push("--stripInternal"); - } - - if (opts.target) { - options.push("--target", opts.target); - } - else { - options.push("--target", "es5"); - } - - if (opts.lib) { - options.push("--lib", opts.lib); - } - else { - options.push("--lib", "es5"); - } - - execCompiler(useBuiltCompiler, options.concat(sources), function (error) { - if (error) { - if (outFile) { - fs.unlinkSync(outFile); - fail("Compilation of " + outFile + " unsuccessful"); - } - else { - fail("Compilation unsuccessful"); - } - } - else { - if (!useDebugMode && prefixes && outFile && fs.existsSync(outFile)) { - for (var i in prefixes) { - prependFile(prefixes[i], outFile); - } - } - - if (callback) { - callback(); - } - - complete(); - } - measure(startCompileTime); - }); -} - /** * Compiles a file from a list of sources * @param {string} outFile the target file name @@ -412,7 +242,7 @@ function compile(outFile, sources, prefixes, useBuiltCompiler, opts, callback) { * @param {boolean} [opts.keepComments] false to compile using --removeComments * @param {boolean} [opts.preserveConstEnums] true if compiler should keep const enums in code * @param {boolean} [opts.noResolve] true if compiler should not include non-rooted files in compilation - * @param {boolean} [opts.stripInternal] true if compiler should remove declarations marked as @internal + * @param {boolean} [opts.stripInternal] true if compiler should remove declarations marked as internal * @param {boolean} [opts.inlineSourceMap] true if compiler should inline sourceMap * @param {string[]} [opts.types] array of types to include in compilation * @param {string} [opts.lib] explicit libs to include. @@ -420,7 +250,97 @@ function compile(outFile, sources, prefixes, useBuiltCompiler, opts, callback) { */ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts, callback) { file(outFile, prereqs, function() { - compile(outFile, sources, prefixes, useBuiltCompiler, opts, callback); + var startCompileTime = mark(); + opts = opts || {}; + var compilerPath = useBuiltCompiler ? builtLocalCompiler : LKGCompiler; + var options = "--noImplicitAny --noImplicitThis --alwaysStrict --noEmitOnError"; + if (opts.types) { + options += " --types " + opts.types.join(","); + } + options += " --pretty"; + // Keep comments when specifically requested + // or when in debug mode. + if (!(opts.keepComments || useDebugMode)) { + options += " --removeComments"; + } + + if (opts.generateDeclarations) { + options += " --declaration"; + } + + if (opts.preserveConstEnums || useDebugMode) { + options += " --preserveConstEnums"; + } + + if (opts.outDir) { + options += " --outDir " + opts.outDir; + } + + if (!opts.noOutFile) { + options += " --out " + outFile; + } + else { + options += " --module commonjs"; + } + + if (opts.noResolve) { + options += " --noResolve"; + } + + if (useDebugMode) { + if (opts.inlineSourceMap) { + options += " --inlineSourceMap --inlineSources"; + } + else { + options += " --sourcemap"; + } + } + options += " --newLine LF"; + + if (opts.stripInternal) { + options += " --stripInternal"; + } + options += " --target es5"; + if (opts.lib) { + options += " --lib " + opts.lib; + } + else { + options += " --lib es5"; + } + options += " --noUnusedLocals --noUnusedParameters"; + + var cmd = host + " " + compilerPath + " " + options + " "; + cmd = cmd + sources.join(" "); + console.log(cmd + "\n"); + + var ex = jake.createExec([cmd]); + // Add listeners for output and error + ex.addListener("stdout", function (output) { + process.stdout.write(output); + }); + ex.addListener("stderr", function (error) { + process.stderr.write(error); + }); + ex.addListener("cmdEnd", function () { + if (!useDebugMode && prefixes && fs.existsSync(outFile)) { + for (var i in prefixes) { + prependFile(prefixes[i], outFile); + } + } + + if (callback) { + callback(); + } + + measure(startCompileTime); + complete(); + }); + ex.addListener("error", function () { + fs.unlinkSync(outFile); + fail("Compilation of " + outFile + " unsuccessful"); + measure(startCompileTime); + }); + ex.run(); }, { async: true }); } @@ -740,7 +660,7 @@ task("default", ["local"]); // Cleans the built directory desc("Cleans the compiler output, declare files, and tests"); -task("clean", ["clean-private-packages"], function () { +task("clean", function () { jake.rmRf(builtDirectory); }); @@ -805,45 +725,12 @@ task("LKG", ["clean", "release", "local"].concat(libraryTargets), function () { // Test directory directory(builtLocalDirectory); -function privatePackage(packageName, prereqs) { - task(packageName, prereqs || [], function () { - var startCompileTime = mark(); - execCompiler(/*useBuiltCompiler*/ false, ["-p", `scripts/${packageName}/tsconfig.json`], function (error) { - if (error) { - fail("Compilation unsuccessful."); - } - else { - complete(); - } - measure(startCompileTime); - }); - }, { async: true }); -} - -function cleanPrivatePackage(packageName) { - jake.rmRf(`scripts/${packageName}/dist`); -} - -privatePackage("vfs-core"); -privatePackage("vfs-errors"); -privatePackage("vfs-path", ["vfs-core", "vfs-errors"]); -privatePackage("vfs", ["vfs-path"]); -privatePackage("harness-core", ["vfs-core"]); -task("private-packages", ["vfs", "harness-core"]); - -task("clean-vfs-core", () => cleanPrivatePackage("vfs-core")); -task("clean-vfs-errors", () => cleanPrivatePackage("vfs-errors")); -task("clean-vfs-path", ["clean-vfs-core", "clean-vfs-errors"], () => cleanPrivatePackage("vfs-path")); -task("clean-vfs", ["clean-vfs-path"], () => cleanPrivatePackage("vfs")); -task("clean-harness-core", ["clean-vfs-core"], () => cleanPrivatePackage("harness-core")); -task("clean-private-packages", ["clean-vfs", "clean-harness-core"]); - // Task to build the tests infrastructure using the built compiler var run = path.join(builtLocalDirectory, "run.js"); compileFile( /*outFile*/ run, /*source*/ harnessSources, - /*prereqs*/[builtLocalDirectory, tscFile, tsserverLibraryFile, "private-packages"].concat(libraryTargets).concat(servicesSources).concat(harnessSources), + /*prereqs*/[builtLocalDirectory, tscFile, tsserverLibraryFile].concat(libraryTargets).concat(servicesSources).concat(harnessSources), /*prefixes*/[], /*useBuiltCompiler:*/ true, /*opts*/ { types: ["node", "mocha", "chai"], lib: "es6" }); @@ -1340,7 +1227,7 @@ task("lint", ["build-rules"], () => { function lint(project, cb) { const cmd = `node node_modules/tslint/bin/tslint --project ${project} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`; console.log("Linting: " + cmd); - jake.exec([cmd], cb, /** @type {jake.ExecOptions} */{ interactive: true, windowsVerbatimArguments: true }); + jake.exec([cmd], cb, /** @type {jake.ExecOptions} */({ interactive: true, windowsVerbatimArguments: true })); } lint("scripts/tslint/tsconfig.json", () => lint("src/tsconfig-base.json", () => { if (fold.isTravis()) console.log(fold.end("lint")); diff --git a/jenkins.sh b/jenkins.sh index 62d0b6e1509..b716f5bbeb2 100755 --- a/jenkins.sh +++ b/jenkins.sh @@ -1,14 +1,9 @@ #!/usr/bin/env bash -if [ "$1" = "6" ]; then - echo "NodeJS v6 is no longer supported, build skipped."; - exit; -fi; # Set up NVM export NVM_DIR="/home/dotnet-bot/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" - nvm install $1 npm uninstall typescript --no-save diff --git a/netci.groovy b/netci.groovy index e40d3a7e0ff..5fa8b02baf2 100644 --- a/netci.groovy +++ b/netci.groovy @@ -5,7 +5,7 @@ import jobs.generation.Utilities; def project = GithubProject def branch = GithubBranchName -def nodeVersions = ['stable', '8'] +def nodeVersions = ['stable', '8', '6'] nodeVersions.each { nodeVer -> diff --git a/package-lock.json b/package-lock.json index 2e1c6f6207e..b96c2ab0737 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "@types/browserify": { "version": "12.0.33", "resolved": "https://registry.npmjs.org/@types/browserify/-/browserify-12.0.33.tgz", - "integrity": "sha1-5hlxwPmFvx93CQSDJkk/ELDdeL0=", + "integrity": "sha512-mY6dYfq1Ns3Xqz/JFUcyoWaXtm0XDoNhkU1vCwM/ULM5zqNL+SbtacJhce/JCgPeCdbqdVqq77tJ4HwdtypSxg==", "dev": true, "requires": { "@types/insert-module-globals": "7.0.0", @@ -54,13 +54,13 @@ "@types/convert-source-map": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@types/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-1NGA3WrcXLaK2ZvVbgPWN4gfRhY=", + "integrity": "sha512-laiDIXqqthjJlyAMYAXOtN3N8+UlbM+KvZi4BaY5ZOekmVkBs/UxfK5O0HWeJVG2eW8F+Mu2ww13fTX+kY1FlQ==", "dev": true }, "@types/del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/del/-/del-3.0.0.tgz", - "integrity": "sha1-HIzYtuONo7VyNSyo6vVSeTFCYog=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/del/-/del-3.0.1.tgz", + "integrity": "sha512-y6qRq6raBuu965clKgx6FHuiPu3oHdtmzMPXi8Uahsjdq1L6DL5fS/aY5/s71YwM7k6K1QIWvem5vNwlnNGIkQ==", "dev": true, "requires": { "@types/glob": "5.0.35" @@ -86,7 +86,7 @@ "@types/gulp": { "version": "3.8.36", "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-3.8.36.tgz", - "integrity": "sha1-Eer1g78FNGabT/qb/tUVV/qgCkw=", + "integrity": "sha512-u6/zWPzYRNPAtvyFJ3/RSXjmBaBM1dVs5kW22/jU6J786ZGLfSndhLoNOpFI6FGQvqTA+QzFHjSMhpkAN+wxcQ==", "dev": true, "requires": { "@types/node": "8.5.5", @@ -97,7 +97,7 @@ "@types/gulp-concat": { "version": "0.0.32", "resolved": "https://registry.npmjs.org/@types/gulp-concat/-/gulp-concat-0.0.32.tgz", - "integrity": "sha1-ckhgKLHPX6qUyMHPNMYmUxzsrNY=", + "integrity": "sha512-CUCFADlITzzBfBa2bdGzhKtvBr4eFh+evb+4igVbvPoO5RyPfHifmyQlZl6lM7q19+OKncRlFXDU7B4X9Ayo2g==", "dev": true, "requires": { "@types/node": "8.5.5" @@ -106,7 +106,7 @@ "@types/gulp-help": { "version": "0.0.34", "resolved": "https://registry.npmjs.org/@types/gulp-help/-/gulp-help-0.0.34.tgz", - "integrity": "sha1-Dm1mcYySiWZPLtdaIaDmXXqIM+w=", + "integrity": "sha512-MkW7psZznxxJg2MBk2P2qHE+T8jEZVFz3FG/qGjUYazkyJt7hBJWx5Nuewmay5RVNtUvSWPrdZLr/WTXY3T/6A==", "dev": true, "requires": { "@types/gulp": "3.8.36", @@ -117,7 +117,7 @@ "@types/gulp-newer": { "version": "0.0.31", "resolved": "https://registry.npmjs.org/@types/gulp-newer/-/gulp-newer-0.0.31.tgz", - "integrity": "sha1-818j0eT+DXuP9pnknRwhdmy546c=", + "integrity": "sha512-e7J/Zv5Wd7CC0WpuA2syWVitgwrkG0u221e41w7r07XUR6hMH6kHPkq9tUrusHkbeW8QbuLbis5fODOwQCyggQ==", "dev": true, "requires": { "@types/node": "8.5.5" @@ -126,7 +126,7 @@ "@types/gulp-sourcemaps": { "version": "0.0.32", "resolved": "https://registry.npmjs.org/@types/gulp-sourcemaps/-/gulp-sourcemaps-0.0.32.tgz", - "integrity": "sha1-557mF+DLFXKYdL5FM/5ZwHeToXU=", + "integrity": "sha512-+7BAmptW2bxyJnJcCEuie7vLoop3FwWgCdBMzyv7MYXED/HeNMeQuX7uPCkp4vfU1TTu4CYFH0IckNPvo0VePA==", "dev": true, "requires": { "@types/node": "8.5.5" @@ -135,7 +135,16 @@ "@types/insert-module-globals": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@types/insert-module-globals/-/insert-module-globals-7.0.0.tgz", - "integrity": "sha1-jRWN5KY4To2qE7PWPuurbV9nd30=", + "integrity": "sha512-zudCJPwluh1VUDB6Gl/OQdRp+fYy3+47huJB/JMQubMS2p+sH18MCVK4WUz3FqaWLB12yh5ELxVR/+tqwlm/qA==", + "dev": true, + "requires": { + "@types/node": "8.5.5" + } + }, + "@types/jake": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/jake/-/jake-0.0.30.tgz", + "integrity": "sha512-F5txkK3aW+fAi4YExGq4Q0W+jJXIhjUvqwMNFPh8kmM+ZU90S/KdDcEV4H+Ug9fHFPAVHG+veaHE8ZHmJJxHCw==", "dev": true, "requires": { "@types/node": "8.5.5" @@ -144,7 +153,7 @@ "@types/merge2": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/merge2/-/merge2-1.1.4.tgz", - "integrity": "sha1-CmUOHMIVove4BALqtraiNuYC96M=", + "integrity": "sha512-GjaXY4OultxbaOOk7lCLO7xvEcFpdjExC605YmfI6X29vhHKpJfMWKCDZd3x+BITrZaXKg97DgV/SdGVSwdzxA==", "dev": true, "requires": { "@types/node": "8.5.5" @@ -165,7 +174,7 @@ "@types/mkdirp": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-0.5.2.tgz", - "integrity": "sha1-UDqs/lzCcD1UhDJrGyfvpnoznB8=", + "integrity": "sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==", "dev": true, "requires": { "@types/node": "8.5.5" @@ -180,13 +189,13 @@ "@types/node": { "version": "8.5.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.5.tgz", - "integrity": "sha1-b56BZK4aVam+sdJXHPt6z51yDGE=", + "integrity": "sha512-JRnfoh0Ll4ElmIXKxbUfcOodkGvcNHljct6mO1X9hE/mlrMzAx0hYCLAD7sgT53YAY1HdlpzUcV0CkmDqUqTuA==", "dev": true }, "@types/orchestrator": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@types/orchestrator/-/orchestrator-0.3.2.tgz", - "integrity": "sha1-zRXGzql4oyuY5QVCOcvMeOVWcfE=", + "integrity": "sha512-cKB4yTX0wGaRCSkdHDX2fkGQbMAA8UOshC2U7DQky1CE5o+5q2iQQ8VkbPbE/88uaTtsusvBPMcCX7dgmjxBhQ==", "dev": true, "requires": { "@types/node": "8.5.5", @@ -202,13 +211,22 @@ "@types/run-sequence": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/run-sequence/-/run-sequence-0.0.30.tgz", - "integrity": "sha1-s6kMn9KaXu3lgTXdAl6zrEu7Vg4=", + "integrity": "sha512-XwGr1b4yCGUILKeBkzmeWcxmGHQ0vFFFpA6D6y1yLO6gKmYorF+PHqdU5KG+nWt38OvtrkDptmrSmlHX/XtpLw==", "dev": true, "requires": { "@types/gulp": "3.8.36", "@types/node": "8.5.5" } }, + "@types/source-map-support": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.4.0.tgz", + "integrity": "sha512-9oVAi1Jlr274pbMGPEe0S3IPImV9knVNafa6E4MookD/fjOZAE6EmLkFX5ZjtZ9OXNPi2FCIZzUSMvwAUUKeSg==", + "dev": true, + "requires": { + "@types/node": "8.5.5" + } + }, "@types/through2": { "version": "2.0.33", "resolved": "https://registry.npmjs.org/@types/through2/-/through2-2.0.33.tgz", @@ -221,7 +239,7 @@ "@types/vinyl": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.2.tgz", - "integrity": "sha1-TzuNro9YKNOADvcJsM/0iO6FLeM=", + "integrity": "sha512-2iYpNuOl98SrLPBZfEN9Mh2JCJ2EI9HU35SfgBEb51DcmaHkhp8cKMblYeBqMQiwXMgAD3W60DbQ4i/UdLiXhw==", "dev": true, "requires": { "@types/node": "8.5.5" @@ -230,7 +248,7 @@ "@types/xml2js": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.2.tgz", - "integrity": "sha1-pLhLOHn/1HEJU/2Syr/emopOhFY=", + "integrity": "sha512-8aKUBSj3oGcnuiBmDLm3BIk09RYg01mz9HlQ2u4aS17oJ25DxjQrEUVGFSBVNOfM45pQW4OjcBPplq6r/exJdA==", "dev": true, "requires": { "@types/node": "8.5.5" @@ -377,9 +395,9 @@ "dev": true }, "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { "sprintf-js": "1.0.3" @@ -394,7 +412,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "arr-union": { @@ -436,7 +454,7 @@ "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha1-42jqFfibxwaff/uJrsOmx9SsItQ=", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true }, "array-union": { @@ -474,7 +492,7 @@ "requires": { "bn.js": "4.11.8", "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" + "minimalistic-assert": "1.0.1" } }, "assert": { @@ -514,9 +532,9 @@ "dev": true }, "atob": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", - "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.0.tgz", + "integrity": "sha512-SuiKH8vbsOyCALjA/+EINmt/Kdl+TQPrtFgW7XZZcwtryFu9e5kQoX3bjCW6mIvGH1fbeAZZuvwGR5IlBRznGw==", "dev": true }, "babel-code-frame": { @@ -566,16 +584,56 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { "cache-base": "1.0.1", - "class-utils": "0.3.5", + "class-utils": "0.3.6", "component-emitter": "1.2.1", "define-property": "1.0.0", "isobject": "3.0.1", - "mixin-deep": "1.3.0", + "mixin-deep": "1.3.1", "pascalcase": "0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + } } }, "base64-js": { @@ -597,9 +655,9 @@ "dev": true }, "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "1.0.0", @@ -607,22 +665,32 @@ } }, "braces": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.0.tgz", - "integrity": "sha1-pGlBy1+0khVrPWplbgbDU2Tj5m4=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { "arr-flatten": "1.1.0", "array-unique": "0.3.2", - "define-property": "1.0.0", "extend-shallow": "2.0.1", "fill-range": "4.0.0", "isobject": "3.0.1", "repeat-element": "1.1.2", - "snapdragon": "0.8.1", + "snapdragon": "0.8.2", "snapdragon-node": "2.1.1", "split-string": "3.1.0", - "to-regex": "3.0.1" + "to-regex": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } } }, "brorand": { @@ -661,9 +729,9 @@ "dev": true }, "browserify": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.1.1.tgz", - "integrity": "sha512-iSH21jK0+IApV8YHOfmGt1qsGd74oflQ1Ko/28JOkWLFNBngAQfKb6WYIJ9CufH8vycqKX1sYU3y7ZrVhwevAg==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.2.0.tgz", + "integrity": "sha512-yotdAkp/ZbgDesHQBYU37zjc29JDH4iXT8hjzM1fdUVWogjARX0S1cKeX24Ci6zZ+jG+ADmCTRt6xvtmJnI+BQ==", "dev": true, "requires": { "JSONStream": "1.3.2", @@ -698,13 +766,13 @@ "punycode": "1.4.1", "querystring-es3": "0.2.1", "read-only-stream": "2.0.0", - "readable-stream": "2.3.3", + "readable-stream": "2.3.6", "resolve": "1.1.7", "shasum": "1.0.2", "shell-quote": "1.6.1", "stream-browserify": "2.0.1", "stream-http": "2.8.1", - "string_decoder": "1.0.3", + "string_decoder": "1.1.1", "subarg": "1.0.0", "syntax-error": "1.4.0", "through2": "2.0.3", @@ -712,7 +780,7 @@ "tty-browserify": "0.0.1", "url": "0.11.0", "util": "0.10.3", - "vm-browserify": "0.0.4", + "vm-browserify": "1.0.1", "xtend": "4.0.1" } }, @@ -724,27 +792,27 @@ "requires": { "buffer-xor": "1.0.3", "cipher-base": "1.0.4", - "create-hash": "1.1.3", + "create-hash": "1.2.0", "evp_bytestokey": "1.0.3", "inherits": "2.0.3", "safe-buffer": "5.1.1" } }, "browserify-cipher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, "requires": { "browserify-aes": "1.2.0", - "browserify-des": "1.0.0", + "browserify-des": "1.0.1", "evp_bytestokey": "1.0.3" } }, "browserify-des": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.1.tgz", + "integrity": "sha512-zy0Cobe3hhgpiOM32Tj7KQ3Vl91m0njwsjzZQK1L+JDf11dzP9qIvjreVinsvXrgfjhStXwUWAEpB9D7Gwmayw==", "dev": true, "requires": { "cipher-base": "1.0.4", @@ -770,11 +838,11 @@ "requires": { "bn.js": "4.11.8", "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", "elliptic": "6.4.0", "inherits": "2.0.3", - "parse-asn1": "5.1.0" + "parse-asn1": "5.1.1" } }, "browserify-zlib": { @@ -835,7 +903,7 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { "collection-visit": "1.0.0", @@ -871,15 +939,6 @@ "requires": { "align-text": "0.1.4", "lazy-cache": "1.0.4" - }, - "dependencies": { - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true, - "optional": true - } } }, "chai": { @@ -893,18 +952,18 @@ "deep-eql": "3.0.1", "get-func-name": "2.0.0", "pathval": "1.1.0", - "type-detect": "4.0.5" + "type-detect": "4.0.8" } }, "chalk": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", - "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.0.tgz", + "integrity": "sha512-Wr/w0f4o9LuE7K53cD0qmbAMM+2XNLzR29vFn5hqko4sxGlUsyy363NvmyGIyk5tpe9cjTr9SJYbysEyPkRnFw==", "dev": true, "requires": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", - "supports-color": "5.3.0" + "supports-color": "5.4.0" } }, "check-error": { @@ -924,15 +983,14 @@ } }, "class-utils": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.5.tgz", - "integrity": "sha1-F+eTEDdQ+WJ7IXbqNM/RtWWQPIA=", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { "arr-union": "3.1.0", "define-property": "0.2.5", "isobject": "3.0.1", - "lazy-cache": "2.0.2", "static-extend": "0.1.2" }, "dependencies": { @@ -944,63 +1002,6 @@ "requires": { "is-descriptor": "0.1.6" } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", - "dev": true } } }, @@ -1026,9 +1027,9 @@ } }, "clone": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true }, "clone-buffer": { @@ -1044,14 +1045,14 @@ "dev": true }, "cloneable-readable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.0.0.tgz", - "integrity": "sha1-pikNQT8hemEjL5XkWP84QYz7ARc=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", + "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", "dev": true, "requires": { "inherits": "2.0.3", - "process-nextick-args": "1.0.7", - "through2": "2.0.3" + "process-nextick-args": "2.0.0", + "readable-stream": "2.3.6" } }, "collection-visit": { @@ -1082,7 +1083,7 @@ "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha1-k4NDeaHMmgxh+C9S8NBDIiUb1aI=", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, "combine-source-map": { @@ -1131,17 +1132,25 @@ "requires": { "buffer-from": "1.0.0", "inherits": "2.0.3", - "readable-stream": "2.3.3", + "readable-stream": "2.3.6", "typedarray": "0.0.6" } }, "concat-with-sourcemaps": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.0.4.tgz", - "integrity": "sha1-9Vs74q60dgGxCi1SWcz7cP0vHdY=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.0.5.tgz", + "integrity": "sha512-YtnS0VEY+e2Khzsey/6mra9EoM6h/5gxaC0e3mcHpA5yfDxafhygytNmcJWodvUgyXzSiL5MSkPO6bQGgfliHw==", "dev": true, "requires": { - "source-map": "0.5.7" + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "console-browserify": { @@ -1178,9 +1187,9 @@ "dev": true }, "create-ecdh": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.1.tgz", + "integrity": "sha512-iZvCCg8XqHQZ1ioNBTzXS/cQSkqkqcPs8xSX4upNB+DAk9Ht3uzQf2J32uAHNCne8LDmKr29AgZrEs4oIrwLuQ==", "dev": true, "requires": { "bn.js": "4.11.8", @@ -1188,25 +1197,26 @@ } }, "create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { "cipher-base": "1.0.4", "inherits": "2.0.3", + "md5.js": "1.3.4", "ripemd160": "2.0.1", "sha.js": "2.4.11" } }, "create-hmac": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", - "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { "cipher-base": "1.0.4", - "create-hash": "1.1.3", + "create-hash": "1.2.0", "inherits": "2.0.3", "ripemd160": "2.0.1", "safe-buffer": "5.1.1", @@ -1219,15 +1229,15 @@ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "requires": { - "browserify-cipher": "1.0.0", + "browserify-cipher": "1.0.1", "browserify-sign": "4.0.4", - "create-ecdh": "4.0.0", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "diffie-hellman": "5.0.2", + "create-ecdh": "4.0.1", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "diffie-hellman": "5.0.3", "inherits": "2.0.3", "pbkdf2": "3.0.14", - "public-encrypt": "4.0.0", + "public-encrypt": "4.0.2", "randombytes": "2.0.6", "randomfill": "1.0.4" } @@ -1303,7 +1313,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -1347,10 +1357,10 @@ "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha1-38lARACtHI/gI+faHfHBR8S0RN8=", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { - "type-detect": "4.0.5" + "type-detect": "4.0.8" } }, "deep-is": { @@ -1365,7 +1375,7 @@ "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "dev": true, "requires": { - "clone": "1.0.3" + "clone": "1.0.4" } }, "define-properties": { @@ -1379,12 +1389,44 @@ } }, "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + } } }, "defined": { @@ -1401,7 +1443,7 @@ "requires": { "globby": "6.1.0", "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", "p-map": "1.2.0", "pify": "3.0.0", "rimraf": "2.6.2" @@ -1432,7 +1474,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" + "minimalistic-assert": "1.0.1" } }, "detect-file": { @@ -1465,9 +1507,9 @@ "dev": true }, "diffie-hellman": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { "bn.js": "4.11.8", @@ -1487,7 +1529,7 @@ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, "requires": { - "readable-stream": "2.3.3" + "readable-stream": "2.3.6" } }, "duplexify": { @@ -1498,7 +1540,7 @@ "requires": { "end-of-stream": "1.4.1", "inherits": "2.0.3", - "readable-stream": "2.3.3", + "readable-stream": "2.3.6", "stream-shift": "1.0.0" }, "dependencies": { @@ -1524,7 +1566,7 @@ "hash.js": "1.1.3", "hmac-drbg": "1.0.1", "inherits": "2.0.3", - "minimalistic-assert": "1.0.0", + "minimalistic-assert": "1.0.1", "minimalistic-crypto-utils": "1.0.1" } }, @@ -1683,9 +1725,9 @@ "define-property": "0.2.5", "extend-shallow": "2.0.1", "posix-character-classes": "0.1.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -1697,62 +1739,14 @@ "is-descriptor": "0.1.6" } }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } + "is-extendable": "0.1.1" } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", - "dev": true } } }, @@ -1772,18 +1766,30 @@ "dev": true }, "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } } }, "extglob": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.3.tgz", - "integrity": "sha1-VeAZ0Mlb+HOUnHN7flFy26hOuyk=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { "array-unique": "0.3.2", @@ -1791,9 +1797,58 @@ "expand-brackets": "2.1.4", "extend-shallow": "2.0.1", "fragment-cache": "0.2.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + } } }, "fancy-log": { @@ -1841,6 +1896,17 @@ "is-number": "3.0.0", "repeat-string": "1.6.1", "to-regex-range": "2.1.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } } }, "find-index": { @@ -1857,7 +1923,7 @@ "requires": { "detect-file": "1.0.0", "is-glob": "3.1.0", - "micromatch": "3.1.5", + "micromatch": "3.1.10", "resolve-dir": "1.0.1" } }, @@ -1893,7 +1959,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "readable-stream": "2.3.3" + "readable-stream": "2.3.6" } }, "for-in": { @@ -1926,24 +1992,6 @@ "map-cache": "0.2.2" } }, - "fs-mkdirp-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", - "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "through2": "2.0.3" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - } - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1980,7 +2028,7 @@ "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -1991,16 +2039,6 @@ "path-is-absolute": "1.0.1" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" - } - }, "glob-stream": { "version": "3.1.18", "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", @@ -2039,7 +2077,7 @@ "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", "dev": true, "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "1.1.11" } }, "readable-stream": { @@ -2093,11 +2131,11 @@ "global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { "global-prefix": "1.0.2", - "is-windows": "1.0.1", + "is-windows": "1.0.2", "resolve-dir": "1.0.1" } }, @@ -2110,7 +2148,7 @@ "expand-tilde": "2.0.2", "homedir-polyfill": "1.0.1", "ini": "1.3.5", - "is-windows": "1.0.1", + "is-windows": "1.0.2", "which": "1.3.0" } }, @@ -2182,9 +2220,9 @@ } }, "glogg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", - "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz", + "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", "dev": true, "requires": { "sparkles": "1.0.0" @@ -2196,7 +2234,7 @@ "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", "dev": true, "requires": { - "natives": "1.1.1" + "natives": "1.1.3" } }, "growl": { @@ -2243,6 +2281,26 @@ "has-ansi": "2.0.0", "strip-ansi": "3.0.1", "supports-color": "2.0.0" + }, + "dependencies": { + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + } } }, "supports-color": { @@ -2269,7 +2327,7 @@ "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", "dev": true, "requires": { - "concat-with-sourcemaps": "1.0.4", + "concat-with-sourcemaps": "1.0.5", "through2": "2.0.3", "vinyl": "2.1.0" } @@ -2429,10 +2487,49 @@ "is-negated-glob": "1.0.0", "ordered-read-streams": "1.0.1", "pumpify": "1.4.0", - "readable-stream": "2.3.3", + "readable-stream": "2.3.6", "remove-trailing-separator": "1.1.0", "to-absolute-glob": "2.0.2", "unique-stream": "2.2.1" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + } + }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "dev": true + }, + "pumpify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", + "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", + "dev": true, + "requires": { + "duplexify": "3.5.4", + "inherits": "2.0.3", + "pump": "2.0.1" + } + }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "dev": true, + "requires": { + "is-absolute": "1.0.0", + "is-negated-glob": "1.0.0" + } + } } }, "graceful-fs": { @@ -2456,7 +2553,7 @@ "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", "dev": true, "requires": { - "readable-stream": "2.3.3" + "readable-stream": "2.3.6" } }, "source-map": { @@ -2473,6 +2570,18 @@ "requires": { "json-stable-stringify": "1.0.1", "through2-filter": "2.0.0" + }, + "dependencies": { + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", + "dev": true, + "requires": { + "through2": "2.0.3", + "xtend": "4.0.1" + } + } } }, "vinyl-fs": { @@ -2489,7 +2598,7 @@ "lead": "1.0.0", "object.assign": "4.1.0", "pumpify": "1.4.0", - "readable-stream": "2.3.3", + "readable-stream": "2.3.6", "remove-bom-buffer": "3.0.0", "remove-bom-stream": "1.2.0", "resolve-options": "1.1.0", @@ -2498,6 +2607,125 @@ "value-or-function": "3.0.0", "vinyl": "2.1.0", "vinyl-sourcemap": "1.1.0" + }, + "dependencies": { + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "through2": "2.0.3" + } + }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", + "dev": true + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "2.3.6" + } + }, + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "dev": true, + "requires": { + "flush-write-stream": "1.0.3" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "function-bind": "1.1.1", + "has-symbols": "1.0.0", + "object-keys": "1.0.11" + } + }, + "pumpify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", + "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", + "dev": true, + "requires": { + "duplexify": "3.5.4", + "inherits": "2.0.3", + "pump": "2.0.1" + } + }, + "remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "requires": { + "is-buffer": "1.1.6", + "is-utf8": "0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "dev": true, + "requires": { + "remove-bom-buffer": "3.0.0", + "safe-buffer": "5.1.1", + "through2": "2.0.3" + } + }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "dev": true, + "requires": { + "value-or-function": "3.0.0" + } + }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "dev": true, + "requires": { + "through2": "2.0.3" + } + }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", + "dev": true + }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "dev": true, + "requires": { + "append-buffer": "1.0.2", + "convert-source-map": "1.5.1", + "graceful-fs": "4.1.11", + "normalize-path": "2.1.1", + "now-and-later": "2.0.0", + "remove-bom-buffer": "3.0.0", + "vinyl": "2.1.0" + } + } } } } @@ -2565,7 +2793,7 @@ "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", "dev": true, "requires": { - "clone": "1.0.3", + "clone": "1.0.4", "clone-stats": "0.0.1", "replace-ext": "0.0.1" } @@ -2578,7 +2806,7 @@ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "dev": true, "requires": { - "glogg": "1.0.0" + "glogg": "1.0.1" } }, "handlebars": { @@ -2622,12 +2850,6 @@ "ansi-regex": "2.1.1" } }, - "has-color": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", - "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", - "dev": true - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2682,12 +2904,13 @@ } }, "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { - "inherits": "2.0.3" + "inherits": "2.0.3", + "safe-buffer": "5.1.1" } }, "hash.js": { @@ -2697,7 +2920,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" + "minimalistic-assert": "1.0.1" } }, "he": { @@ -2713,7 +2936,7 @@ "dev": true, "requires": { "hash.js": "1.1.3", - "minimalistic-assert": "1.0.0", + "minimalistic-assert": "1.0.1", "minimalistic-crypto-utils": "1.0.1" } }, @@ -2744,12 +2967,6 @@ "integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==", "dev": true }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2769,7 +2986,7 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, "inline-source-map": { @@ -2807,46 +3024,76 @@ "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { "is-relative": "1.0.0", - "is-windows": "1.0.1" + "is-windows": "1.0.2" } }, "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } } }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } } }, "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } } }, "is-extendable": { @@ -2870,12 +3117,6 @@ "is-extglob": "2.1.1" } }, - "is-negated-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", - "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", - "dev": true - }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -2897,12 +3138,20 @@ } }, "is-odd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-1.0.0.tgz", - "integrity": "sha1-O4qTLrAos3dcObsJ6RdnrM22kIg=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", + "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", "dev": true, "requires": { - "is-number": "3.0.0" + "is-number": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } } }, "is-path-cwd": { @@ -2912,9 +3161,9 @@ "dev": true }, "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", "dev": true, "requires": { "is-path-inside": "1.0.1" @@ -2932,7 +3181,7 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "3.0.1" @@ -2947,7 +3196,7 @@ "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { "is-unc-path": "1.0.0" @@ -2956,7 +3205,7 @@ "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { "unc-path-regex": "0.1.2" @@ -2968,16 +3217,10 @@ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, - "is-valid-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", - "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", - "dev": true - }, "is-windows": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.1.tgz", - "integrity": "sha1-MQ23D3QtJZoWo2kgK1GvhCMzENk=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isarray": { @@ -3010,7 +3253,7 @@ "esprima": "2.7.3", "glob": "5.0.15", "handlebars": "4.0.11", - "js-yaml": "3.10.0", + "js-yaml": "3.11.0", "mkdirp": "0.5.1", "nopt": "3.0.6", "once": "1.4.0", @@ -3051,9 +3294,9 @@ } }, "jake": { - "version": "8.0.15", - "resolved": "https://registry.npmjs.org/jake/-/jake-8.0.15.tgz", - "integrity": "sha1-8Np9WOeQrBqPhubuDxk+XZIw6rs=", + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/jake/-/jake-8.0.16.tgz", + "integrity": "sha512-qUTOVCKFkiz3tHgV1WMy7HTxDZgo+sO4X9GxFLAU+Mks4WsDGe9+ONHK6tPsSp8I3x6sPl0TwGbXHwTOhTyzog==", "dev": true, "requires": { "async": "0.9.2", @@ -3084,6 +3327,14 @@ "ansi-styles": "1.0.0", "has-color": "0.1.7", "strip-ansi": "0.1.1" + }, + "dependencies": { + "has-color": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", + "dev": true + } } }, "strip-ansi": { @@ -3101,19 +3352,19 @@ "dev": true }, "js-yaml": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", - "integrity": "sha1-LnhEFka9RoLpY/IrbpKCPDCcYtw=", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", + "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", "dev": true, "requires": { - "argparse": "1.0.9", + "argparse": "1.0.10", "esprima": "4.0.0" }, "dependencies": { "esprima": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha1-RJnt3NERDgshi6zy+n9/WfVcqAQ=", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", "dev": true } } @@ -3148,7 +3399,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, "labeled-stream-splicer": { @@ -3171,31 +3422,11 @@ } }, "lazy-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", - "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "dev": true, - "requires": { - "set-getter": "0.1.0" - } - }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "dev": true, - "requires": { - "readable-stream": "2.3.3" - } - }, - "lead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", - "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", - "dev": true, - "requires": { - "flush-write-stream": "1.0.3" - } + "optional": true }, "levn": { "version": "0.3.0", @@ -3391,23 +3622,12 @@ "dev": true }, "make-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.0.tgz", - "integrity": "sha1-V7713IXSOSO6I3ZzJNjo+PPZaUs=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } + "kind-of": "6.0.2" } }, "map-cache": { @@ -3433,18 +3653,6 @@ "requires": { "hash-base": "3.0.4", "inherits": "2.0.3" - }, - "dependencies": { - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - } } }, "memoizee": { @@ -3470,24 +3678,24 @@ "dev": true }, "micromatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.5.tgz", - "integrity": "sha512-ykttrLPQrz1PUJcXjwsTUjGoPJ64StIGNE2lGVD1c9CuguJ+L7/navsE8IcDNndOoCMvYV0qc/exfVbMHkUhvA==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { "arr-diff": "4.0.0", "array-unique": "0.3.2", - "braces": "2.3.0", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "extglob": "2.0.3", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", "fragment-cache": "0.2.1", "kind-of": "6.0.2", - "nanomatch": "1.2.7", + "nanomatch": "1.2.9", "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "miller-rabin": { @@ -3501,9 +3709,9 @@ } }, "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true }, "minimalistic-crypto-utils": { @@ -3515,10 +3723,10 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -3528,9 +3736,9 @@ "dev": true }, "mixin-deep": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.0.tgz", - "integrity": "sha1-R6hzK6l3mUV8jB7KKPlRMtfoFQo=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { "for-in": "1.0.2", @@ -3540,7 +3748,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { "is-plain-object": "2.0.4" @@ -3566,9 +3774,9 @@ } }, "mocha": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.5.tgz", - "integrity": "sha512-3MM3UjZ5p8EJrYpG7s+29HAI9G7sTzKEe4+w37Dg0QP7qL4XGsV+Q2xet2cE37AqdgN1OtYQB6Vl98YiPV3PgA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.0.tgz", + "integrity": "sha512-d6RWgYPILd+AoWVOxiD0UwUqRicnE1inTxMr40CXOgqYve1MvnKntoLAtLIcxjEeVjEoYYTe5QAq3mUc6/ySjQ==", "dev": true, "requires": { "browser-stdout": "1.3.1", @@ -3579,6 +3787,7 @@ "glob": "7.1.2", "growl": "1.10.3", "he": "1.1.1", + "minimatch": "3.0.4", "mkdirp": "0.5.1", "supports-color": "4.4.0" }, @@ -3630,8 +3839,8 @@ "duplexer2": "0.1.4", "inherits": "2.0.3", "parents": "1.0.1", - "readable-stream": "2.3.3", - "resolve": "1.6.0", + "readable-stream": "2.3.6", + "resolve": "1.7.1", "stream-combiner2": "1.1.1", "subarg": "1.0.0", "through2": "2.0.3", @@ -3639,9 +3848,9 @@ }, "dependencies": { "resolve": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.6.0.tgz", - "integrity": "sha512-mw7JQNu5ExIkcw4LPih0owX/TZXjD/ZUF/ZQ/pDnkw3ZKhDcZZw5klmBlj6gVMwjQ3Pz5Jgu7F3d0jcDVuEWdw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", "dev": true, "requires": { "path-parse": "1.0.5" @@ -3700,36 +3909,29 @@ } }, "nanomatch": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.7.tgz", - "integrity": "sha512-/5ldsnyurvEw7wNpxLFgjVvBLMta43niEYOy0CJ4ntcYSbx6bugRUTQeFb4BR/WanEL1o3aQgHuVLHQaB6tOqg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", + "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", "dev": true, "requires": { "arr-diff": "4.0.0", "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", "fragment-cache": "0.2.1", - "is-odd": "1.0.0", - "kind-of": "5.1.0", + "is-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "natives": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.1.tgz", - "integrity": "sha1-ARrM4ffL2H97prMJPWzZOSvhxXQ=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.3.tgz", + "integrity": "sha512-BZGSYV4YOLxzoTK73l0/s/0sH9l8SHs2ocReMH1f8JYSh5FUWu4ZrKCpJdRkWXV6HFR/pZDz7bwWOVAY07q77g==", "dev": true }, "next-tick": { @@ -3791,43 +3993,6 @@ "is-descriptor": "0.1.6" } }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", - "dev": true - } - } - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -3854,18 +4019,6 @@ "isobject": "3.0.1" } }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "function-bind": "1.1.1", - "has-symbols": "1.0.0", - "object-keys": "1.0.11" - } - }, "object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", @@ -3885,7 +4038,7 @@ "dev": true, "requires": { "for-own": "1.0.0", - "make-iterator": "1.0.0" + "make-iterator": "1.0.1" } }, "object.pick": { @@ -3952,7 +4105,7 @@ "requires": { "end-of-stream": "0.1.5", "sequencify": "0.0.7", - "stream-consume": "0.1.0" + "stream-consume": "0.1.1" } }, "ordered-read-streams": { @@ -3976,7 +4129,7 @@ "p-map": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha1-5OlPMR6rvIYzoeeZCBZfyiYkG2s=", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", "dev": true }, "pako": { @@ -3995,14 +4148,14 @@ } }, "parse-asn1": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", - "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { "asn1.js": "4.10.1", "browserify-aes": "1.2.0", - "create-hash": "1.1.3", + "create-hash": "1.2.0", "evp_bytestokey": "1.0.3", "pbkdf2": "3.0.14" } @@ -4093,8 +4246,8 @@ "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", "dev": true, "requires": { - "create-hash": "1.1.3", - "create-hmac": "1.1.6", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", "ripemd160": "2.0.1", "safe-buffer": "5.1.1", "sha.js": "2.4.11" @@ -4198,21 +4351,21 @@ "dev": true }, "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, "public-encrypt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", + "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", "dev": true, "requires": { "bn.js": "4.11.8", "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "parse-asn1": "5.1.0", + "create-hash": "1.2.0", + "parse-asn1": "5.1.1", "randombytes": "2.0.6" } }, @@ -4237,17 +4390,6 @@ } } }, - "pumpify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", - "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", - "dev": true, - "requires": { - "duplexify": "3.5.4", - "inherits": "2.0.3", - "pump": "2.0.1" - } - }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -4297,21 +4439,21 @@ "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", "dev": true, "requires": { - "readable-stream": "2.3.3" + "readable-stream": "2.3.6" } }, "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", "isarray": "1.0.0", - "process-nextick-args": "1.0.7", + "process-nextick-args": "2.0.0", "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", + "string_decoder": "1.1.1", "util-deprecate": "1.0.2" } }, @@ -4325,33 +4467,13 @@ } }, "regex-not": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", - "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "2.0.1" - } - }, - "remove-bom-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", - "dev": true, - "requires": { - "is-buffer": "1.1.6", - "is-utf8": "0.2.1" - } - }, - "remove-bom-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", - "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", - "dev": true, - "requires": { - "remove-bom-buffer": "3.0.0", - "safe-buffer": "5.1.1", - "through2": "2.0.3" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" } }, "remove-trailing-separator": { @@ -4394,21 +4516,18 @@ "global-modules": "1.0.0" } }, - "resolve-options": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", - "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", - "dev": true, - "requires": { - "value-or-function": "3.0.0" - } - }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -4422,7 +4541,7 @@ "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { "glob": "7.1.2" @@ -4436,12 +4555,23 @@ "requires": { "hash-base": "2.0.2", "inherits": "2.0.3" + }, + "dependencies": { + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + } } }, "run-sequence": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz", - "integrity": "sha1-HOZD2jb9jH6n4akynaM/wriJhJU=", + "integrity": "sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw==", "dev": true, "requires": { "chalk": "1.1.3", @@ -4479,9 +4609,18 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "0.1.15" + } + }, "sander": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", @@ -4505,7 +4644,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, "semver": { @@ -4520,25 +4659,27 @@ "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=", "dev": true }, - "set-getter": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", - "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", - "dev": true, - "requires": { - "to-object-path": "0.3.0" - } - }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { "extend-shallow": "2.0.1", "is-extendable": "0.1.1", "is-plain-object": "2.0.4", "split-string": "3.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } } }, "sha.js": { @@ -4580,9 +4721,9 @@ "dev": true }, "snapdragon": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", - "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { "base": "0.11.2", @@ -4592,7 +4733,7 @@ "map-cache": "0.2.2", "source-map": "0.5.7", "source-map-resolve": "0.5.1", - "use": "2.0.2" + "use": "3.1.0" }, "dependencies": { "define-property": { @@ -4604,80 +4745,72 @@ "is-descriptor": "0.1.6" } }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } + "is-extendable": "0.1.1" } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", - "dev": true } } }, "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { "define-property": "1.0.0", "isobject": "3.0.1", "snapdragon-util": "3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + } } }, "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { "kind-of": "3.2.2" @@ -4703,7 +4836,7 @@ "buffer-crc32": "0.2.13", "minimist": "1.2.0", "sander": "0.5.1", - "sourcemap-codec": "1.3.1" + "sourcemap-codec": "1.4.1" } }, "source-map": { @@ -4715,10 +4848,10 @@ "source-map-resolve": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha1-etD1k/IoFZjoVN+A8ZquS5LXoRo=", + "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", "dev": true, "requires": { - "atob": "2.0.3", + "atob": "2.1.0", "decode-uri-component": "0.2.0", "resolve-url": "0.2.1", "source-map-url": "0.4.0", @@ -4749,13 +4882,10 @@ "dev": true }, "sourcemap-codec": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.3.1.tgz", - "integrity": "sha1-mtb5vb1pGTEBbjCTnbyGhnMyMUY=", - "dev": true, - "requires": { - "vlq": "0.2.3" - } + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz", + "integrity": "sha512-hX1eNBNuilj8yfFnECh0DzLgwKpBLMIvmhgEhixXNui8lMLBInTI8Kyxt++RwJnMNu7cAUo635L2+N1TxMJCzA==", + "dev": true }, "sparkles": { "version": "1.0.0", @@ -4766,31 +4896,10 @@ "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { "extend-shallow": "3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", - "dev": true, - "requires": { - "is-plain-object": "2.0.4" - } - } } }, "sprintf-js": { @@ -4817,63 +4926,6 @@ "requires": { "is-descriptor": "0.1.6" } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", - "dev": true } } }, @@ -4884,7 +4936,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "readable-stream": "2.3.3" + "readable-stream": "2.3.6" } }, "stream-combiner2": { @@ -4894,13 +4946,13 @@ "dev": true, "requires": { "duplexer2": "0.1.4", - "readable-stream": "2.3.3" + "readable-stream": "2.3.6" } }, "stream-consume": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", - "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8=", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", + "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==", "dev": true }, "stream-http": { @@ -4911,7 +4963,7 @@ "requires": { "builtin-status-codes": "3.0.0", "inherits": "2.0.3", - "readable-stream": "2.3.3", + "readable-stream": "2.3.6", "to-arraybuffer": "1.0.1", "xtend": "4.0.1" } @@ -4929,7 +4981,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "readable-stream": "2.3.3" + "readable-stream": "2.3.6" } }, "streamqueue": { @@ -4968,9 +5020,9 @@ } }, "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "5.1.1" @@ -5011,9 +5063,9 @@ } }, "supports-color": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", - "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { "has-flag": "3.0.0" @@ -5040,17 +5092,7 @@ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" - } - }, - "through2-filter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", - "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", - "dev": true, - "requires": { - "through2": "2.0.3", + "readable-stream": "2.3.6", "xtend": "4.0.1" } }, @@ -5088,16 +5130,6 @@ "next-tick": "1.0.0" } }, - "to-absolute-glob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", - "dev": true, - "requires": { - "is-absolute": "1.0.0", - "is-negated-glob": "1.0.0" - } - }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -5125,82 +5157,15 @@ } }, "to-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", - "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "regex-not": "1.0.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", - "dev": true - } + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -5213,15 +5178,6 @@ "repeat-string": "1.6.1" } }, - "to-through": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", - "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", - "dev": true, - "requires": { - "through2": "2.0.3" - } - }, "travis-fold": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/travis-fold/-/travis-fold-0.1.2.tgz", @@ -5229,13 +5185,13 @@ "dev": true }, "ts-node": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-5.0.1.tgz", - "integrity": "sha512-XK7QmDcNHVmZkVtkiwNDWiERRHPyU8nBqZB1+iv2UhOG0q3RQ9HsZ2CMqISlFbxjrYFGfG2mX7bW4dAyxBVzUw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-6.0.0.tgz", + "integrity": "sha512-+CQev+4J7BAUNUnW9piRzSfSZZWeFCjgUjMSgGs4+dJ2RZa86NVW9MOlP4e6/kEHTyOqdxHxcIMd7KgmY/ynVw==", "dev": true, "requires": { "arrify": "1.0.1", - "chalk": "2.3.2", + "chalk": "2.4.0", "diff": "3.5.0", "make-error": "1.3.4", "minimist": "1.2.0", @@ -5258,13 +5214,13 @@ "requires": { "babel-code-frame": "6.26.0", "builtin-modules": "1.1.1", - "chalk": "2.3.2", + "chalk": "2.4.0", "commander": "2.15.1", "diff": "3.5.0", "glob": "7.1.2", - "js-yaml": "3.10.0", + "js-yaml": "3.11.0", "minimatch": "3.0.4", - "resolve": "1.6.0", + "resolve": "1.7.1", "semver": "5.5.0", "tslib": "1.9.0", "tsutils": "2.26.1" @@ -5277,9 +5233,9 @@ "dev": true }, "resolve": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.6.0.tgz", - "integrity": "sha512-mw7JQNu5ExIkcw4LPih0owX/TZXjD/ZUF/ZQ/pDnkw3ZKhDcZZw5klmBlj6gVMwjQ3Pz5Jgu7F3d0jcDVuEWdw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", "dev": true, "requires": { "path-parse": "1.0.5" @@ -5318,9 +5274,9 @@ } }, "type-detect": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.5.tgz", - "integrity": "sha1-1w5byB223io4G8rKDG4MvcdjXeI=", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, "typedarray": { @@ -5330,9 +5286,9 @@ "dev": true }, "typescript": { - "version": "2.9.0-dev.20180407", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.0-dev.20180407.tgz", - "integrity": "sha512-Tg0/hU2hSz+4pb5Lj5+bj1uLldN7C8NO5Ik19WfftMlpeXRyZQJzglV0oncmsXOfN9gG+JC0xnO58YspE6sZ1w==", + "version": "2.9.0-dev.20180414", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.0-dev.20180414.tgz", + "integrity": "sha512-9wS/WxRJGXYs/EFNS4HrByWi6bGHPMhqEecAS9162kAZkTrwk75C9yU28ZjZ/rTgkXSqgvS3jWxgGhe3Nrkkvg==", "dev": true }, "uglify-js": { @@ -5378,6 +5334,15 @@ "set-value": "0.4.3" }, "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, "set-value": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", @@ -5463,82 +5428,12 @@ } }, "use": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", - "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", + "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", "dev": true, "requires": { - "define-property": "0.2.5", - "isobject": "3.0.1", - "lazy-cache": "2.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", - "dev": true - } + "kind-of": "6.0.2" } }, "user-home": { @@ -5585,12 +5480,6 @@ "user-home": "1.1.1" } }, - "value-or-function": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", - "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", - "dev": true - }, "vinyl": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", @@ -5600,7 +5489,7 @@ "clone": "2.1.2", "clone-buffer": "1.0.0", "clone-stats": "1.0.0", - "cloneable-readable": "1.0.0", + "cloneable-readable": "1.1.2", "remove-trailing-separator": "1.1.0", "replace-ext": "1.0.0" }, @@ -5693,48 +5582,16 @@ } } }, - "vinyl-sourcemap": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", - "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", - "dev": true, - "requires": { - "append-buffer": "1.0.2", - "convert-source-map": "1.5.1", - "graceful-fs": "4.1.11", - "normalize-path": "2.1.1", - "now-and-later": "2.0.0", - "remove-bom-buffer": "3.0.0", - "vinyl": "2.1.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - } - } - }, - "vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha1-jz5DKM9jsVQMDWfhsneDhviXWyY=", - "dev": true - }, "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.0.1.tgz", + "integrity": "sha512-EqzLchIMYLBjRPoqVsEkZOa/4Vr2RfOWbd58F+I/Gj79AYTrsseMunxbbSkbYfrqZaXSuPBBXNSOhtJgg0PpmA==", + "dev": true }, "which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { "isexe": "2.0.0" @@ -5762,17 +5619,17 @@ "xml2js": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha1-aGwg8hMgnpSr8NG88e+qKRx4J6c=", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", "dev": true, "requires": { "sax": "1.2.4", - "xmlbuilder": "9.0.4" + "xmlbuilder": "9.0.7" } }, "xmlbuilder": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz", - "integrity": "sha1-UZy0ymhtAFqEINNJbz8MruzKWA8=", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", "dev": true }, "xtend": { diff --git a/package.json b/package.json index c9dd26fd424..dff8abf71f5 100644 --- a/package.json +++ b/package.json @@ -51,15 +51,10 @@ "@types/source-map-support": "latest", "@types/through2": "latest", "@types/xml2js": "^0.4.0", - "@typescript/vfs-core": "file:scripts/vfs-core", - "@typescript/vfs-errors": "file:scripts/vfs-errors", - "@typescript/vfs-path": "file:scripts/vfs-path", - "@typescript/vfs": "file:scripts/vfs", - "@typescript/harness-core": "file:scripts/harness-core", + "xml2js": "^0.4.19", "browser-resolve": "^1.11.2", "browserify": "latest", "chai": "latest", - "chalk": "latest", "convert-source-map": "latest", "del": "latest", "gulp": "3.X", @@ -85,9 +80,9 @@ "travis-fold": "latest", "ts-node": "latest", "tslint": "latest", - "typescript": "next", "vinyl": "latest", - "xml2js": "^0.4.19" + "chalk": "latest", + "typescript": "next" }, "scripts": { "pretest": "jake tests", diff --git a/scripts/harness-core/gulpfile.js b/scripts/harness-core/gulpfile.js deleted file mode 100644 index 31c28436446..00000000000 --- a/scripts/harness-core/gulpfile.js +++ /dev/null @@ -1,18 +0,0 @@ -const gulp = require("gulp"); -const sourcemaps = require("gulp-sourcemaps"); -const tsb = require("gulp-tsb"); -const del = require("del"); - -const project = tsb.create("tsconfig.json") - -gulp.task("clean", () => del(["dist/**/*"])); - -gulp.task("build", () => gulp.src(["src/**/*.ts"]) - .pipe(sourcemaps.init()) - .pipe(project()) - .pipe(sourcemaps.write(".", { sourceRoot: "../src", includeContent: false, destPath: "dist" })) - .pipe(gulp.dest("dist"))); - -gulp.task("watch", () => gulp.watch(["src/**/*", "tsconfig.json"], ["build"])); - -gulp.task("default", ["build"]); \ No newline at end of file diff --git a/scripts/harness-core/package.json b/scripts/harness-core/package.json deleted file mode 100644 index 6967fcdb792..00000000000 --- a/scripts/harness-core/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "private": true, - "name": "@typescript/harness-core", - "version": "0.0.0", - "description": "TypeScript test harness core", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "author": "Ron Buckton (ron.buckton@microsoft.com)", - "license": "Apache-2.0", - "dependencies": { - "@typescript/vfs-core": "file:../vfs-core" - }, - "devDependencies": { - "@types/node": "^8.0.20", - "@types/source-map-support": "^0.4.0", - "del": "^2.0.2", - "gulp": "^3.9.1", - "gulp-sourcemaps": "^2.6.1", - "gulp-tsb": "^2.0.5", - "source-map-support": "^0.5.0", - "typescript": "^2.6.1" - } -} diff --git a/scripts/harness-core/src/crypto.ts b/scripts/harness-core/src/crypto.ts deleted file mode 100644 index b74413b0f2b..00000000000 --- a/scripts/harness-core/src/crypto.ts +++ /dev/null @@ -1,67 +0,0 @@ -const H = new Uint32Array(5); -const W = new Uint8Array(80); -const B = new Uint8Array(64); -const BLOCK_SIZE = 64; - -export function sha1(message: string): string { - let buffer = B; - const textSize = message.length; - const messageSize = textSize * 2; - const finalBlockSize = messageSize % BLOCK_SIZE; - const padSize = (finalBlockSize < BLOCK_SIZE - 8 - 1 ? BLOCK_SIZE : BLOCK_SIZE * 2) - finalBlockSize; - const byteLength = messageSize + padSize; - if (byteLength > BLOCK_SIZE) { - buffer = new Uint8Array(byteLength); - } - - const bufferView = new DataView(buffer.buffer); - for (let i = 0; i < textSize; ++i) { - bufferView.setUint16(i * 2, message.charCodeAt(i)); - } - - buffer[messageSize] = 0x80; - bufferView.setUint32(byteLength - 4, messageSize * 8); - H[0] = 0x67452301, H[1] = 0xefcdab89, H[2] = 0x98badcfe, H[3] = 0x10325476, H[4] = 0xc3d2e1f0; - for (let offset = 0; offset < byteLength; offset += BLOCK_SIZE) { - let a = H[0], b = H[1], c = H[2], d = H[3], e = H[4]; - for (let i = 0; i < 80; ++i) { - if (i < 16) { - const x = offset + i * 4; - W[i] = buffer[x] << 24 | buffer[x + 1] << 16 | buffer[x + 2] << 8 | buffer[x + 3]; - } - else { - const x = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; - W[i] = (x << 1 | x >>> 31) >>> 0; - } - - let t = (a << 5 | a >>> 27) >>> 0 + e + W[i]; - if (i < 20) { - t += ((b & c) | (~b & d)) + 0x5A827999; - } - else if (i < 40) { - t += (b ^ c ^ d) + 0x6ED9EBA1; - } - else if (i < 60) { - t += ((b & c) | (b & d) | (c & d)) + 0x8F1BBCDC; - } - else { - t += (b ^ c ^ d) + 0xCA62C1D6; - } - - e = d, d = c, c = (b << 30 | b >>> 2) >>> 0, b = a, a = t; - } - - H[0] += a, H[1] += b, H[2] += c, H[3] += d, H[4] += e; - } - - for (let i = 0; i < 5; ++i) { - bufferView.setUint32(i * 4, H[i]); - } - - let result = ""; - for (let i = 0; i < 20; ++i) { - result += (buffer[i] < 16 ? "0" : "") + buffer[i].toString(16); - } - - return result; -} \ No newline at end of file diff --git a/scripts/harness-core/src/index.ts b/scripts/harness-core/src/index.ts deleted file mode 100644 index 6047bb0716e..00000000000 --- a/scripts/harness-core/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "@typescript/vfs-core"; -export * from "./strings"; -export * from "./crypto"; \ No newline at end of file diff --git a/scripts/harness-core/src/strings.ts b/scripts/harness-core/src/strings.ts deleted file mode 100644 index 23ae7ef4467..00000000000 --- a/scripts/harness-core/src/strings.ts +++ /dev/null @@ -1,132 +0,0 @@ -export function padLeft(text: string, size: number, ch = " "): string { - while (text.length < size) text = ch + text; - return text; -} - -export function padRight(text: string, size: number, ch = " "): string { - while (text.length < size) text += ch; - return text; -} - -export function getByteOrderMark(text: string): string { - const length = getByteOrderMarkLength(text); - return length > 0 ? text.slice(0, length) : ""; -} - -export function getByteOrderMarkLength(text: string): number { - if (text.length >= 2) { - const ch0 = text.charCodeAt(0); - const ch1 = text.charCodeAt(1); - if ((ch0 === 0xff && ch1 === 0xfe) || - (ch0 === 0xfe && ch1 === 0xff)) { - return 2; - } - if (text.length >= 3 && ch0 === 0xef && ch1 === 0xbb && text.charCodeAt(2) === 0xbf) { - return 3; - } - } - return 0; -} - -export function removeByteOrderMark(text: string): string { - const length = getByteOrderMarkLength(text); - return length ? text.slice(length) : text; -} - -export function addUTF8ByteOrderMark(text: string) { - return getByteOrderMarkLength(text) === 0 ? "\u00EF\u00BB\u00BF" + text : text; -} - -function splitLinesWorker(text: string, lineStarts: number[] | undefined, lines: string[] | undefined, removeEmptyElements: boolean) { - let pos = 0; - let end = 0; - let lineStart = 0; - let nonWhiteSpace = false; - while (pos < text.length) { - const ch = text.charCodeAt(pos); - end = pos; - pos++; - switch (ch) { - // LineTerminator - case 0x000d: // carriage return - if (pos < text.length && text.charCodeAt(pos) === 0x000a) { - pos++; - } - // falls through - - case 0x000a: // line feed - case 0x2028: // line separator - case 0x2029: // paragraph separator - if (lineStarts) { - lineStarts.push(lineStart); - } - if (lines && (!removeEmptyElements || nonWhiteSpace)) { - lines.push(text.slice(lineStart, end)); - } - lineStart = pos; - nonWhiteSpace = false; - break; - - // WhiteSpace - case 0x0009: // tab - case 0x000b: // vertical tab - case 0x000c: // form feed - case 0x0020: // space - case 0x00a0: // no-break space - case 0xfeff: // zero width no-break space - case 0x1680: // ogham space mark - case 0x2000: // en quad - case 0x2001: // em quad - case 0x2002: // en space - case 0x2003: // em space - case 0x2004: // three-per-em space - case 0x2005: // four-per-em space - case 0x2006: // six-per-em space - case 0x2007: // figure space - case 0x2008: // punctuation space - case 0x2009: // thin space - case 0x200a: // hair space - case 0x202f: // narrow no-break space - case 0x205f: // medium mathematical space - case 0x3000: // ideographic space - case 0x0085: // next-line (not strictly per spec, but used by the compiler) - break; - - default: - nonWhiteSpace = true; - break; - } - } - if (lineStarts) { - lineStarts.push(lineStart); - } - if (lines && (!removeEmptyElements || nonWhiteSpace)) { - lines.push(text.slice(lineStart, text.length)); - } -} - -export type LineStarts = ReadonlyArray; - -export interface LinesAndLineStarts { - readonly lines: ReadonlyArray; - readonly lineStarts: LineStarts; -} - -export function getLinesAndLineStarts(text: string): LinesAndLineStarts { - const lines: string[] = []; - const lineStarts: number[] = []; - splitLinesWorker(text, lineStarts, lines, /*removeEmptyElements*/ false); - return { lines, lineStarts }; -} - -export function splitLines(text: string, removeEmptyElements = false): string[] { - const lines: string[] = []; - splitLinesWorker(text, /*lineStarts*/ undefined, lines, removeEmptyElements); - return lines; -} - -export function computeLineStarts(text: string): LineStarts { - const lineStarts: number[] = []; - splitLinesWorker(text, lineStarts, /*lines*/ undefined, /*removeEmptyElements*/ false); - return lineStarts; -} \ No newline at end of file diff --git a/scripts/harness-core/tsconfig.json b/scripts/harness-core/tsconfig.json deleted file mode 100644 index 637d5c89c31..00000000000 --- a/scripts/harness-core/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es2015", - "module": "commonjs", - "outDir": "dist", - "types": ["node"], - "declaration": true, - "sourceMap": true, - "strict": true, - "preserveConstEnums": true - }, - "include": [ - "src/**/*" - ] -} \ No newline at end of file diff --git a/scripts/vfs-core/gulpfile.js b/scripts/vfs-core/gulpfile.js deleted file mode 100644 index 31c28436446..00000000000 --- a/scripts/vfs-core/gulpfile.js +++ /dev/null @@ -1,18 +0,0 @@ -const gulp = require("gulp"); -const sourcemaps = require("gulp-sourcemaps"); -const tsb = require("gulp-tsb"); -const del = require("del"); - -const project = tsb.create("tsconfig.json") - -gulp.task("clean", () => del(["dist/**/*"])); - -gulp.task("build", () => gulp.src(["src/**/*.ts"]) - .pipe(sourcemaps.init()) - .pipe(project()) - .pipe(sourcemaps.write(".", { sourceRoot: "../src", includeContent: false, destPath: "dist" })) - .pipe(gulp.dest("dist"))); - -gulp.task("watch", () => gulp.watch(["src/**/*", "tsconfig.json"], ["build"])); - -gulp.task("default", ["build"]); \ No newline at end of file diff --git a/scripts/vfs-core/package.json b/scripts/vfs-core/package.json deleted file mode 100644 index 21133f4999c..00000000000 --- a/scripts/vfs-core/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "private": true, - "name": "@typescript/vfs-core", - "version": "0.0.0", - "description": "JavaScript Virtual File System core", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "author": "Ron Buckton (ron.buckton@microsoft.com)", - "license": "Apache-2.0", - "devDependencies": { - "@types/node": "^8.0.20", - "@types/source-map-support": "^0.4.0", - "del": "^2.0.2", - "gulp": "^3.9.1", - "gulp-sourcemaps": "^2.6.1", - "gulp-tsb": "^2.0.5", - "source-map-support": "^0.5.0", - "typescript": "^2.6.1" - } -} diff --git a/scripts/vfs-core/src/collections.ts b/scripts/vfs-core/src/collections.ts deleted file mode 100644 index f73ae6c4fd4..00000000000 --- a/scripts/vfs-core/src/collections.ts +++ /dev/null @@ -1,517 +0,0 @@ -import { identity } from "./functions"; - -export interface SortOptions { - comparer: (a: T, b: T) => number; - sort: "insertion" | "comparison"; -} - -export class SortedMap { - private _comparer: (a: K, b: K) => number; - private _keys: K[] = []; - private _values: V[] = []; - private _order: number[] | undefined; - private _version = 0; - private _copyOnWrite = false; - - constructor(comparer: ((a: K, b: K) => number) | SortOptions, iterable?: Iterable<[K, V]>) { - this._comparer = typeof comparer === "object" ? comparer.comparer : comparer; - this._order = typeof comparer === "object" && comparer.sort === "insertion" ? [] : undefined; - if (iterable) { - for (const [key, value] of iterable) { - this.set(key, value); - } - } - } - - public get size() { - return this._keys.length; - } - - public get comparer() { - return this._comparer; - } - - public get [Symbol.toStringTag]() { - return "SortedMap"; - } - - public has(key: K) { - return binarySearch(this._keys, key, identity, this._comparer) >= 0; - } - - public get(key: K) { - const index = binarySearch(this._keys, key, identity, this._comparer); - return index >= 0 ? this._values[index] : undefined; - } - - public set(key: K, value: V) { - const index = binarySearch(this._keys, key, identity, this._comparer); - if (index >= 0) { - this._values[index] = value; - } - else { - this.writePreamble(); - insertAt(this._keys, ~index, key); - insertAt(this._values, ~index, value); - if (this._order) insertAt(this._order, ~index, this._version); - this.writePostScript(); - } - return this; - } - - public delete(key: K) { - const index = binarySearch(this._keys, key, identity, this._comparer); - if (index >= 0) { - this.writePreamble(); - removeAt(this._keys, index); - removeAt(this._values, index); - if (this._order) removeAt(this._order, index); - this.writePostScript(); - return true; - } - return false; - } - - public clear() { - if (this.size > 0) { - this.writePreamble(); - this._keys.length = 0; - this._values.length = 0; - if (this._order) this._order.length = 0; - this.writePostScript(); - } - } - - public forEach(callback: (value: V, key: K, collection: this) => void, thisArg?: any) { - const keys = this._keys; - const values = this._values; - const indices = this.getIterationOrder(); - const version = this._version; - this._copyOnWrite = true; - try { - if (indices) { - for (const i of indices) { - callback.call(thisArg, values[i], keys[i], this); - } - } - else { - for (let i = 0; i < keys.length; i++) { - callback.call(thisArg, values[i], keys[i], this); - } - } - } - finally { - if (version === this._version) { - this._copyOnWrite = false; - } - } - } - - public * keys() { - const keys = this._keys; - const indices = this.getIterationOrder(); - const version = this._version; - this._copyOnWrite = true; - try { - if (indices) { - for (const i of indices) { - yield keys[i]; - } - } - else { - for (let i = 0; i < keys.length; i++) { - yield keys[i]; - } - } - } - finally { - if (version === this._version) { - this._copyOnWrite = false; - } - } - } - - public * values() { - const values = this._values; - const indices = this.getIterationOrder(); - const version = this._version; - this._copyOnWrite = true; - try { - if (indices) { - for (const i of indices) { - yield values[i]; - } - } - else { - for (let i = 0; i < values.length; i++) { - yield values[i]; - } - } - } - finally { - if (version === this._version) { - this._copyOnWrite = false; - } - } - } - - public * entries() { - const keys = this._keys; - const values = this._values; - const indices = this.getIterationOrder(); - const version = this._version; - this._copyOnWrite = true; - try { - if (indices) { - for (const i of indices) { - yield [keys[i], values[i]] as [K, V]; - } - } - else { - for (let i = 0; i < keys.length; i++) { - yield [keys[i], values[i]] as [K, V]; - } - } - } - finally { - if (version === this._version) { - this._copyOnWrite = false; - } - } - } - - public [Symbol.iterator]() { - return this.entries(); - } - - private writePreamble() { - if (this._copyOnWrite) { - this._keys = this._keys.slice(); - this._values = this._values.slice(); - if (this._order) this._order = this._order.slice(); - this._copyOnWrite = false; - } - } - - private writePostScript() { - this._version++; - } - - private getIterationOrder() { - if (this._order) { - const order = this._order; - return this._order - .map((_, i) => i) - .sort((x, y) => order[x] - order[y]); - } - return undefined; - } -} - -export class SortedSet { - private _comparer: (a: T, b: T) => number; - private _values: T[] = []; - private _order: number[] | undefined; - private _version = 0; - private _copyOnWrite = false; - - constructor(comparer: ((a: T, b: T) => number) | SortOptions, iterable?: Iterable) { - this._comparer = typeof comparer === "object" ? comparer.comparer : comparer; - this._order = typeof comparer === "object" && comparer.sort === "insertion" ? [] : undefined; - - if (iterable) { - for (const value of iterable) { - this.add(value); - } - } - } - - public get size() { - return this._values.length; - } - - public get comparer() { - return this._comparer; - } - - public get [Symbol.toStringTag]() { - return "SortedSet"; - } - - public has(value: T) { - return binarySearch(this._values, value, identity, this._comparer) >= 0; - } - - public add(value: T) { - const index = binarySearch(this._values, value, identity, this._comparer); - if (index < 0) { - this.writePreamble(); - insertAt(this._values, ~index, value); - if (this._order) insertAt(this._order, ~index, this._version); - this.writePostScript(); - } - return this; - } - - public delete(value: T) { - const index = binarySearch(this._values, value, identity, this._comparer); - if (index >= 0) { - this.writePreamble(); - removeAt(this._values, index); - if (this._order) removeAt(this._order, index); - this.writePostScript(); - return true; - } - return false; - } - - public clear() { - if (this.size > 0) { - this.writePreamble(); - this._values.length = 0; - if (this._order) this._order.length = 0; - this.writePostScript(); - } - } - - public forEach(callback: (value: T, key: T, collection: this) => void, thisArg?: any) { - const values = this._values; - const indices = this.getIterationOrder(); - const version = this._version; - this._copyOnWrite = true; - try { - if (indices) { - for (const i of indices) { - callback.call(thisArg, values[i], values[i], this); - } - } - else { - for (const value of values) { - callback.call(thisArg, value, value, this); - } - } - } - finally { - if (version === this._version) { - this._copyOnWrite = false; - } - } - } - - public keys() { - return this.values(); - } - - public * values() { - const values = this._values; - const indices = this.getIterationOrder(); - const version = this._version; - this._copyOnWrite = true; - try { - if (indices) { - for (const i of indices) { - yield values[i]; - } - } - else { - for (const value of values) { - yield value; - } - } - } - finally { - if (version === this._version) { - this._copyOnWrite = false; - } - } - } - - public * entries() { - const values = this._values; - const indices = this.getIterationOrder(); - const version = this._version; - this._copyOnWrite = true; - try { - if (indices) { - for (const i of indices) { - yield [values[i], values[i]] as [T, T]; - } - } - else { - for (const value of values) { - yield [value, value] as [T, T]; - } - } - } - finally { - if (version === this._version) { - this._copyOnWrite = false; - } - } - } - - public [Symbol.iterator]() { - return this.values(); - } - - private writePreamble() { - if (this._copyOnWrite) { - this._values = this._values.slice(); - if (this._order) this._order = this._order.slice(); - this._copyOnWrite = false; - } - } - - private writePostScript() { - this._version++; - } - - private getIterationOrder() { - if (this._order) { - const order = this._order; - return this._order - .map((_, i) => i) - .sort((x, y) => order[x] - order[y]); - } - return undefined; - } -} - -export function binarySearch(array: ReadonlyArray, value: T, keySelector: (v: T) => U, keyComparer: (a: U, b: U) => number, offset?: number): number { - if (!array || array.length === 0) { - return -1; - } - - let low = offset || 0; - let high = array.length - 1; - const key = keySelector(value); - while (low <= high) { - const middle = low + ((high - low) >> 1); - const midKey = keySelector(array[middle]); - const result = keyComparer(midKey, key); - if (result < 0) { - low = middle + 1; - } - else if (result > 0) { - high = middle - 1; - } - else { - return middle; - } - } - - return ~low; -} - -export function removeAt(array: T[], index: number): void { - if (index < 0 || index >= array.length) { - return; - } - else if (index === 0) { - array.shift(); - } - else if (index === array.length - 1) { - array.pop(); - } - else { - for (let i = index; i < array.length - 1; i++) { - array[i] = array[i + 1]; - } - array.length--; - } -} - -export function insertAt(array: T[], index: number, value: T): void { - if (index === 0) { - array.unshift(value); - } - else if (index === array.length) { - array.push(value); - } - else { - for (let i = array.length; i > index; i--) { - array[i] = array[i - 1]; - } - array[index] = value; - } -} - -/** - * A collection of metadata that supports inheritance. - */ -export class Metadata { - private static readonly _undefinedValue = {}; - private _parent: Metadata | undefined; - private _map: { [key: string]: any }; - private _version = 0; - private _size = -1; - private _parentVersion: number | undefined; - - constructor(parent?: Metadata) { - this._parent = parent; - this._map = Object.create(parent ? parent._map : null); // tslint:disable-line:no-null-keyword - } - - public get size(): number { - if (this._size === -1 || (this._parent && this._parent._version !== this._parentVersion)) { - let size = 0; - for (const _ in this._map) size++; - this._size = size; - if (this._parent) { - this._parentVersion = this._parent._version; - } - } - return this._size; - } - - public get parent() { - return this._parent; - } - - public has(key: string): boolean { - return this._map[Metadata._escapeKey(key)] !== undefined; - } - - public get(key: string): any { - const value = this._map[Metadata._escapeKey(key)]; - return value === Metadata._undefinedValue ? undefined : value; - } - - public set(key: string, value: any): this { - this._map[Metadata._escapeKey(key)] = value === undefined ? Metadata._undefinedValue : value; - this._size = -1; - this._version++; - return this; - } - - public delete(key: string): boolean { - const escapedKey = Metadata._escapeKey(key); - if (this._map[escapedKey] !== undefined) { - delete this._map[escapedKey]; - this._size = -1; - this._version++; - return true; - } - return false; - } - - public clear(): void { - this._map = Object.create(this._parent ? this._parent._map : null); // tslint:disable-line:no-null-keyword - this._size = -1; - this._version++; - } - - public forEach(callback: (value: any, key: string, map: this) => void) { - for (const key in this._map) { - callback(this._map[key], Metadata._unescapeKey(key), this); - } - } - - private static _escapeKey(text: string) { - return (text.length >= 2 && text.charAt(0) === "_" && text.charAt(1) === "_" ? "_" + text : text); - } - - private static _unescapeKey(text: string) { - return (text.length >= 3 && text.charAt(0) === "_" && text.charAt(1) === "_" && text.charAt(2) === "_" ? text.slice(1) : text); - } -} \ No newline at end of file diff --git a/scripts/vfs-core/src/comparers.ts b/scripts/vfs-core/src/comparers.ts deleted file mode 100644 index 589a631c7c7..00000000000 --- a/scripts/vfs-core/src/comparers.ts +++ /dev/null @@ -1,41 +0,0 @@ -export function compareNumbers(a: number, b: number): number { - if (a === b) return 0; - if (a === undefined) return -1; - if (b === undefined) return +1; - return a < b ? -1 : +1; -} - -export function compareStrings(a: string, b: string, ignoreCase: boolean): number { - return ignoreCase - ? compareStringsCaseInsensitive(a, b) - : compareStringsCaseSensitive(a, b); -} - -// NOTE: This is a duplicate of `compareNumbers` above, but is intended to be used only with -// strings to reduce polymorphism. -export function compareStringsCaseSensitive(a: string, b: string): number { - if (a === b) return 0; - if (a === undefined) return -1; - if (b === undefined) return +1; - return a < b ? -1 : +1; -} - -export function compareStringsCaseInsensitive(a: string, b: string): number { - if (a === b) return 0; - if (a === undefined) return -1; - if (b === undefined) return +1; - a = a.toUpperCase(); - b = b.toUpperCase(); - return a < b ? -1 : a > b ? +1 : 0; -} - -export function equateStringsCaseSensitive(a: string, b: string): boolean { - return a === b; -} - -export function equateStringsCaseInsensitive(a: string, b: string): boolean { - return a === b - || a !== undefined - && b !== undefined - && a.toUpperCase() === b.toUpperCase(); -} diff --git a/scripts/vfs-core/src/functions.ts b/scripts/vfs-core/src/functions.ts deleted file mode 100644 index c8fd7fb1305..00000000000 --- a/scripts/vfs-core/src/functions.ts +++ /dev/null @@ -1 +0,0 @@ -export function identity(v: T): T { return v; } diff --git a/scripts/vfs-core/src/index.ts b/scripts/vfs-core/src/index.ts deleted file mode 100644 index 7d7770a5a3d..00000000000 --- a/scripts/vfs-core/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./functions"; -export * from "./comparers"; -export * from "./collections"; \ No newline at end of file diff --git a/scripts/vfs-core/tsconfig.json b/scripts/vfs-core/tsconfig.json deleted file mode 100644 index 637d5c89c31..00000000000 --- a/scripts/vfs-core/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es2015", - "module": "commonjs", - "outDir": "dist", - "types": ["node"], - "declaration": true, - "sourceMap": true, - "strict": true, - "preserveConstEnums": true - }, - "include": [ - "src/**/*" - ] -} \ No newline at end of file diff --git a/scripts/vfs-errors/gulpfile.js b/scripts/vfs-errors/gulpfile.js deleted file mode 100644 index 31c28436446..00000000000 --- a/scripts/vfs-errors/gulpfile.js +++ /dev/null @@ -1,18 +0,0 @@ -const gulp = require("gulp"); -const sourcemaps = require("gulp-sourcemaps"); -const tsb = require("gulp-tsb"); -const del = require("del"); - -const project = tsb.create("tsconfig.json") - -gulp.task("clean", () => del(["dist/**/*"])); - -gulp.task("build", () => gulp.src(["src/**/*.ts"]) - .pipe(sourcemaps.init()) - .pipe(project()) - .pipe(sourcemaps.write(".", { sourceRoot: "../src", includeContent: false, destPath: "dist" })) - .pipe(gulp.dest("dist"))); - -gulp.task("watch", () => gulp.watch(["src/**/*", "tsconfig.json"], ["build"])); - -gulp.task("default", ["build"]); \ No newline at end of file diff --git a/scripts/vfs-errors/package.json b/scripts/vfs-errors/package.json deleted file mode 100644 index 220423d9d7c..00000000000 --- a/scripts/vfs-errors/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "private": true, - "name": "@typescript/vfs-errors", - "version": "0.0.0", - "description": "JavaScript Virtual File System Errors", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "author": "Ron Buckton (ron.buckton@microsoft.com)", - "license": "Apache-2.0", - "devDependencies": { - "@types/node": "^8.0.20", - "@types/source-map-support": "^0.4.0", - "del": "^2.0.2", - "gulp": "^3.9.1", - "gulp-sourcemaps": "^2.6.1", - "gulp-tsb": "^2.0.5", - "source-map-support": "^0.5.0", - "typescript": "^2.6.1" - } -} diff --git a/scripts/vfs-errors/src/index.ts b/scripts/vfs-errors/src/index.ts deleted file mode 100644 index 9aee22a6551..00000000000 --- a/scripts/vfs-errors/src/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -export const IOErrorMessages = Object.freeze({ - "EACCES": "access denied", - "EIO": "an I/O error occurred", - "ENOENT": "no such file or directory", - "EEXIST": "file already exists", - "ELOOP": "too many symbolic links encountered", - "ENOTDIR": "no such directory", - "EISDIR": "path is a directory", - "EBADF": "invalid file descriptor", - "EINVAL": "invalid value", - "ENOTEMPTY": "directory not empty", - "EPERM": "operation not permitted", - "EROFS": "file system is read-only" -}); - -export class IOError extends Error { - public readonly code: string; - public readonly syscall: string | undefined; - public readonly path: string | undefined; - public readonly dest: string | undefined; - - constructor(code: keyof typeof IOErrorMessages, syscall?: string); - constructor(code: keyof typeof IOErrorMessages, syscall: string, path?: string); - constructor(code: keyof typeof IOErrorMessages, syscall: string, path: string, dest?: string); - constructor(code: keyof typeof IOErrorMessages, syscall?: string, path?: string, dest?: string) { - let message = `${code}: ${IOErrorMessages[code]}`; - if (syscall !== undefined) { - message += `, ${syscall}`; - if (path !== undefined) { - message += ` '${path}'`; - if (dest !== undefined) { - message += ` -> '${dest}'`; - } - } - } - super(message); - this.name = "Error"; - this.code = code; - this.syscall = syscall; - this.path = path; - this.dest = dest; - } -} - diff --git a/scripts/vfs-errors/tsconfig.json b/scripts/vfs-errors/tsconfig.json deleted file mode 100644 index 637d5c89c31..00000000000 --- a/scripts/vfs-errors/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es2015", - "module": "commonjs", - "outDir": "dist", - "types": ["node"], - "declaration": true, - "sourceMap": true, - "strict": true, - "preserveConstEnums": true - }, - "include": [ - "src/**/*" - ] -} \ No newline at end of file diff --git a/scripts/vfs-path/gulpfile.js b/scripts/vfs-path/gulpfile.js deleted file mode 100644 index 4ed6e3cf4be..00000000000 --- a/scripts/vfs-path/gulpfile.js +++ /dev/null @@ -1,19 +0,0 @@ -const gulp = require("gulp"); -const gutil = require("gulp-util"); -const sourcemaps = require("gulp-sourcemaps"); -const tsb = require("gulp-tsb"); -const del = require("del"); - -const project = tsb.create("tsconfig.json") - -gulp.task("clean", () => del(["dist/**/*"])); - -gulp.task("build", () => gulp.src(["src/**/*.ts"]) - .pipe(sourcemaps.init()) - .pipe(project()) - .pipe(sourcemaps.write(".", { sourceRoot: "../src", includeContent: false, destPath: "dist" })) - .pipe(gulp.dest("dist"))); - -gulp.task("watch", () => gulp.watch(["src/**/*", "tsconfig.json"], ["build"])); - -gulp.task("default", ["build"]); \ No newline at end of file diff --git a/scripts/vfs-path/package.json b/scripts/vfs-path/package.json deleted file mode 100644 index 146f09f094f..00000000000 --- a/scripts/vfs-path/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "private": true, - "name": "@typescript/vfs-path", - "version": "0.0.0", - "description": "JavaScript Virtual File System Paths", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "author": "Ron Buckton (ron.buckton@microsoft.com)", - "license": "Apache-2.0", - "dependencies": { - "@typescript/vfs-core": "file:../vfs-core", - "@typescript/vfs-errors": "file:../vfs-errors" - }, - "devDependencies": { - "@types/node": "^8.0.20", - "@types/source-map-support": "^0.4.0", - "del": "^2.0.2", - "gulp": "^3.9.1", - "gulp-sourcemaps": "^2.6.1", - "gulp-tsb": "^2.0.5", - "source-map-support": "^0.5.0", - "typescript": "^2.6.1" - } -} diff --git a/scripts/vfs-path/src/index.ts b/scripts/vfs-path/src/index.ts deleted file mode 100644 index 3b88aaf4cd2..00000000000 --- a/scripts/vfs-path/src/index.ts +++ /dev/null @@ -1,436 +0,0 @@ -import { IOError } from "@typescript/vfs-errors"; -import * as core from "@typescript/vfs-core"; - -/** - * Virtual path separator. - */ -export const sep = "/"; - -/** - * Normalize path separators. - */ -export function normalizeSeparators(path: string): string { - return path.replace(/\s*[\\/]\s*/g, sep).trim(); -} - -const invalidRootComponentRegExp = /^(?!(\/|\/\/\w+\/|[a-zA-Z]:\/?|)$)/; -const invalidNavigableComponentRegExp = /[:*?"<>|]/; -const invalidNonNavigableComponentRegExp = /^\.{1,2}$|[:*?"<>|]/; - -export const enum ValidationFlags { - None = 0, - - RequireRoot = 1 << 0, - RequireDirname = 1 << 1, - RequireBasename = 1 << 2, - RequireExtname = 1 << 3, - RequireTrailingSeparator = 1 << 4, - - AllowRoot = 1 << 5, - AllowDirname = 1 << 6, - AllowBasename = 1 << 7, - AllowExtname = 1 << 8, - AllowTrailingSeparator = 1 << 9, - AllowNavigation = 1 << 10, - - /** Path must be a valid directory root */ - Root = RequireRoot | AllowRoot | AllowTrailingSeparator, - - /** Path must be a absolute */ - Absolute = RequireRoot | AllowRoot | AllowDirname | AllowBasename | AllowExtname | AllowTrailingSeparator | AllowNavigation, - - /** Path may be relative or absolute */ - RelativeOrAbsolute = AllowRoot | AllowDirname | AllowBasename | AllowExtname | AllowTrailingSeparator | AllowNavigation, - - /** Path may only be a filename */ - Basename = RequireBasename | AllowExtname, -} - -export function valid(path: string, flags: ValidationFlags = ValidationFlags.RelativeOrAbsolute) { - return validateComponents(parse(path), flags, hasTrailingSeparator(path)); -} - -export function validate(path: string, flags: ValidationFlags = ValidationFlags.RelativeOrAbsolute) { - const components = parse(path); - const trailing = hasTrailingSeparator(path); - if (!validateComponents(components, flags, trailing)) throw new IOError("ENOENT", "scandir", path); - return components.length > 1 && trailing ? format(reduce(components)) + sep : format(reduce(components)); -} - -function validateComponents(components: string[], flags: ValidationFlags, hasTrailingSeparator: boolean) { - const hasRoot = !!components[0]; - const hasDirname = components.length > 2; - const hasBasename = components.length > 1; - const hasExtname = hasBasename && extRegExp.test(components[components.length - 1]); - const invalidComponentRegExp = flags & ValidationFlags.AllowNavigation ? invalidNavigableComponentRegExp : invalidNonNavigableComponentRegExp; - - // Validate required components - if (flags & ValidationFlags.RequireRoot && !hasRoot) return false; - if (flags & ValidationFlags.RequireDirname && !hasDirname) return false; - if (flags & ValidationFlags.RequireBasename && !hasBasename) return false; - if (flags & ValidationFlags.RequireExtname && !hasExtname) return false; - if (flags & ValidationFlags.RequireTrailingSeparator && !hasTrailingSeparator) return false; - - // Required components indicate allowed components - if (flags & ValidationFlags.RequireRoot) flags |= ValidationFlags.AllowRoot; - if (flags & ValidationFlags.RequireDirname) flags |= ValidationFlags.AllowDirname; - if (flags & ValidationFlags.RequireBasename) flags |= ValidationFlags.AllowBasename; - if (flags & ValidationFlags.RequireExtname) flags |= ValidationFlags.AllowExtname; - if (flags & ValidationFlags.RequireTrailingSeparator) flags |= ValidationFlags.AllowTrailingSeparator; - - // Validate disallowed components - if (~flags & ValidationFlags.AllowRoot && hasRoot) return false; - if (~flags & ValidationFlags.AllowDirname && hasDirname) return false; - if (~flags & ValidationFlags.AllowBasename && hasBasename) return false; - if (~flags & ValidationFlags.AllowExtname && hasExtname) return false; - if (~flags & ValidationFlags.AllowTrailingSeparator && hasTrailingSeparator) return false; - - // Validate component strings - if (invalidRootComponentRegExp.test(components[0])) return false; - for (let i = 1; i < components.length; i++) { - if (invalidComponentRegExp.test(components[i])) return false; - } - - return true; -} - -const absolutePathRegExp = /^[\\/]([\\/](.*?[\\/](.*?[\\/])?)?)?|^[a-zA-Z]:[\\/]?|^\w+:\/{2}[^\\/]*\/?/; - -function getRootLength(path: string) { - const match = absolutePathRegExp.exec(path); - return match ? match[0].length : 0; -} - -/** - * Determines whether a path is an absolute path (e.g. starts with `/`, `\\`, or a dos path - * like `c:`). - */ -export function isAbsolute(path: string) { - return absolutePathRegExp.test(path); -} - -/** - * Determines whether a path consists only of a path root. - */ -export function isRoot(path: string) { - const rootLength = getRootLength(path); - return rootLength > 0 && rootLength === path.length; -} - -const trailingSeperatorRegExp = /[\\/]$/; - -/** - * Determines whether a path has a trailing separator (`/`). - */ -export function hasTrailingSeparator(path: string) { - return trailingSeperatorRegExp.test(path) && !isRoot(path); -} - -/** - * Adds a trailing separator (`/`) to a path if it doesn't have one. - */ -export function addTrailingSeparator(path: string) { - return !trailingSeperatorRegExp.test(path) && path ? path + "/" : path; -} - -/** - * Removes a trailing separator (`/`) from a path if it has one. - */ -export function removeTrailingSeparator(path: string) { - return trailingSeperatorRegExp.test(path) && !isRoot(path) ? path.slice(0, -1) : path; -} - -function reduce(components: ReadonlyArray) { - const normalized = [components[0]]; - for (let i = 1; i < components.length; i++) { - const component = components[i]; - if (component === ".") continue; - if (component === "..") { - if (normalized.length > 1) { - if (normalized[normalized.length - 1] !== "..") { - normalized.pop(); - continue; - } - } - else if (normalized[0]) continue; - } - normalized.push(component); - } - return normalized; -} - -/** - * Normalize a path containing path traversal components (`.` or `..`). - */ -export function normalize(path: string): string { - const components = reduce(parse(path)); - return components.length > 1 && hasTrailingSeparator(path) ? format(components) + sep : format(components); -} - -/** - * Combines two or more paths. If a path is absolute, it replaces any previous path. - */ -export function combine(path: string, ...paths: string[]) { - path = normalizeSeparators(path); - for (let name of paths) { - name = normalizeSeparators(name); - if (name.length === 0) continue; - path = path.length === 0 || isAbsolute(name) ? name : - addTrailingSeparator(path) + name; - } - return path; -} - -/** - * Combines and normalizes two or more paths. - */ -export function resolve(path: string, ...paths: string[]) { - return normalize(combine(path, ...paths)); -} - -function relativeWorker(from: string, to: string, stringEqualityComparer: (a: string, b: string) => boolean) { - if (!isAbsolute(from)) throw new Error("Path not absolute"); - if (!isAbsolute(to)) throw new Error("Path not absolute"); - - const fromComponents = reduce(parse(from)); - const toComponents = reduce(parse(to)); - - let start: number; - for (start = 0; start < fromComponents.length && start < toComponents.length; start++) { - if (!stringEqualityComparer(fromComponents[start], toComponents[start])) { - break; - } - } - - if (start === 0 || (start === 1 && fromComponents[0] === "/")) { - return format(toComponents); - } - - const components = toComponents.slice(start); - for (; start < fromComponents.length; start++) { - components.unshift(".."); - } - - return format(["", ...components]); -} - -function relativeCaseSensitive(from: string, to: string) { - return relativeWorker(from, to, core.equateStringsCaseSensitive); -} - -function relativeCaseInsensitive(from: string, to: string) { - return relativeWorker(from, to, core.equateStringsCaseInsensitive); -} - -/** - * Gets a relative path that can be used to traverse between `from` and `to`. - */ -export function relative(from: string, to: string, ignoreCase: boolean) { - return ignoreCase ? relativeCaseInsensitive(from, to) : relativeCaseSensitive(from, to); -} - -function compareWorker(a: string, b: string, stringComparer: (a: string, b: string) => number) { - if (a === b) return 0; - a = removeTrailingSeparator(a); - b = removeTrailingSeparator(b); - if (a === b) return 0; - const aComponents = reduce(parse(a)); - const bComponents = reduce(parse(b)); - const len = Math.min(aComponents.length, bComponents.length); - for (let i = 0; i < len; i++) { - const result = stringComparer(aComponents[i], bComponents[i]); - if (result !== 0) return result; - } - return core.compareNumbers(aComponents.length, bComponents.length); -} - -/** - * Performs a case-sensitive comparison of two paths. - */ -export function compareCaseSensitive(a: string, b: string) { - return compareWorker(a, b, core.compareStringsCaseSensitive); -} - -/** - * Performs a case-insensitive comparison of two paths. - */ -export function compareCaseInsensitive(a: string, b: string) { - return compareWorker(a, b, core.compareStringsCaseInsensitive); -} - -/** - * Compare two paths. - */ -export function compare(a: string, b: string, ignoreCase: boolean) { - return ignoreCase ? compareCaseInsensitive(a, b) : compareCaseSensitive(a, b); -} - -/** - * Determines whether two strings are equal. - */ -export function equals(a: string, b: string, ignoreCase: boolean) { - if (!isAbsolute(a)) throw new Error("Path not absolute"); - if (!isAbsolute(b)) throw new Error("Path not absolute"); - if (a === b) return true; - a = removeTrailingSeparator(a); - b = removeTrailingSeparator(b); - if (a === b) return true; - a = normalize(a); - b = normalize(b); - if (a === b) return true; - return ignoreCase && a.toUpperCase() === b.toUpperCase(); -} - -function beneathWorker(ancestor: string, descendant: string, stringEqualityComparer: (a: string, b: string) => boolean) { - if (!isAbsolute(ancestor)) throw new Error("Path not absolute"); - if (!isAbsolute(descendant)) throw new Error("Path not absolute"); - const ancestorComponents = reduce(parse(ancestor)); - const descendantComponents = reduce(parse(descendant)); - if (descendantComponents.length < ancestorComponents.length) return false; - for (let i = 0; i < ancestorComponents.length; i++) { - if (!stringEqualityComparer(ancestorComponents[i], descendantComponents[i])) { - return false; - } - } - return true; -} - -function beneathCaseSensitive(ancestor: string, descendant: string) { - return beneathWorker(ancestor, descendant, core.equateStringsCaseSensitive); -} - -function beneathCaseInsensitive(ancestor: string, descendant: string) { - return beneathWorker(ancestor, descendant, core.equateStringsCaseInsensitive); -} - -/** - * Determines whether the path `descendant` is beneath the path `ancestor`. - */ -export function beneath(ancestor: string, descendant: string, ignoreCase: boolean) { - return ignoreCase ? beneathCaseInsensitive(ancestor, descendant) : beneathCaseSensitive(ancestor, descendant); -} - -/** - * Parse a path into a root component and zero or more path segments. - * If the path is relative, the root component is `""`. - * If the path is absolute, the root component includes the first path separator (`/`). - */ -export function parse(path: string) { - path = normalizeSeparators(path); - const rootLength = getRootLength(path); - const root = path.substring(0, rootLength); - const rest = path.substring(rootLength).split(/\/+/g); - if (rest.length && !rest[rest.length - 1]) rest.pop(); - return [root, ...rest.map(component => component.trim())]; -} - -/** - * Formats a parsed path consisting of a root component and zero or more path segments. - */ -export function format(components: ReadonlyArray) { - return components.length ? components[0] + components.slice(1).join(sep) : ""; -} - -/** - * Gets the parent directory name of a path. - */ -export function dirname(path: string) { - path = normalizeSeparators(path); - path = removeTrailingSeparator(path); - return path.substr(0, Math.max(getRootLength(path), path.lastIndexOf(sep))); -} - -/** - * Gets the portion of a path following the last separator (`/`). - */ -export function basename(path: string): string; -/** - * Gets the portion of a path following the last separator (`/`). - * If the base name has any one of the provided extensions, it is removed. - */ -export function basename(path: string, extensions: string | ReadonlyArray, ignoreCase: boolean): string; -export function basename(path: string, extensions?: string | ReadonlyArray, ignoreCase?: boolean) { - path = normalizeSeparators(path); - path = removeTrailingSeparator(path); - const name = path.substr(Math.max(getRootLength(path), path.lastIndexOf(sep) + 1)); - const extension = extensions !== undefined && ignoreCase !== undefined ? extname(path, extensions, ignoreCase) : undefined; - return extension ? name.slice(0, name.length - extension.length) : name; -} - -function extnameWorker(path: string, extensions: string | ReadonlyArray, stringEqualityComparer: (a: string, b: string) => boolean) { - const manyExtensions = Array.isArray(extensions) ? extensions : undefined; - const singleExtension = Array.isArray(extensions) ? undefined : extensions; - const length = manyExtensions ? manyExtensions.length : 1; - for (let i = 0; i < length; i++) { - let extension = manyExtensions ? manyExtensions[i] : singleExtension; - if (!extension.startsWith(".")) extension = "." + extension; - if (path.length >= extension.length && path.charAt(path.length - extension.length) === ".") { - const pathExtension = path.slice(path.length - extension.length); - if (stringEqualityComparer(pathExtension, extension)) { - return pathExtension; - } - } - } - return ""; -} - -const extRegExp = /\.\w+$/; - -/** - * Gets the file extension for a path. - */ -export function extname(path: string): string; -/** - * Gets the file extension for a path, provided it is one of the provided extensions. - */ -export function extname(path: string, extensions: string | ReadonlyArray, ignoreCase: boolean): string; -export function extname(path: string, extensions?: string | ReadonlyArray, ignoreCase?: boolean) { - if (extensions) { - return extnameWorker(path, extensions, ignoreCase ? core.equateStringsCaseInsensitive : core.equateStringsCaseSensitive); - } - - const match = extRegExp.exec(path); - return match ? match[0] : ""; -} - -export function changeExtension(path: string, ext: string): string; -export function changeExtension(path: string, ext: string, extensions: string | ReadonlyArray, ignoreCase: boolean): string; -export function changeExtension(path: string, ext: string, extensions?: string | ReadonlyArray, ignoreCase?: boolean) { - const pathext = extensions !== undefined && ignoreCase !== undefined ? extname(path, extensions, ignoreCase) : extname(path); - return pathext ? path.slice(0, path.length - pathext.length) + (ext.startsWith(".") ? ext : "." + ext) : path; -} - -const typeScriptExtensions: ReadonlyArray = [".ts", ".tsx"]; - -export function isTypeScript(path: string) { - return extname(path, typeScriptExtensions, /*ignoreCase*/ false).length > 0; -} - -const javaScriptExtensions: ReadonlyArray = [".js", ".jsx"]; - -export function isJavaScript(path: string) { - return extname(path, javaScriptExtensions, /*ignoreCase*/ false).length > 0; -} - -export function isDeclaration(path: string) { - return extname(path, ".d.ts", /*ignoreCase*/ false).length > 0; -} - -export function isSourceMap(path: string) { - return extname(path, ".map", /*ignoreCase*/ false).length > 0; -} - -const javaScriptSourceMapExtensions: ReadonlyArray = [".js.map", ".jsx.map"]; - -export function isJavaScriptSourceMap(path: string) { - return extname(path, javaScriptSourceMapExtensions, /*ignoreCase*/ false).length > 0; -} - -export function isJson(path: string) { - return extname(path, ".json", /*ignoreCase*/ false).length > 0; -} - -export function isDefaultLibrary(path: string) { - return isDeclaration(path) - && basename(path).startsWith("lib."); -} \ No newline at end of file diff --git a/scripts/vfs-path/tsconfig.json b/scripts/vfs-path/tsconfig.json deleted file mode 100644 index 637d5c89c31..00000000000 --- a/scripts/vfs-path/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es2015", - "module": "commonjs", - "outDir": "dist", - "types": ["node"], - "declaration": true, - "sourceMap": true, - "strict": true, - "preserveConstEnums": true - }, - "include": [ - "src/**/*" - ] -} \ No newline at end of file diff --git a/scripts/vfs/gulpfile.js b/scripts/vfs/gulpfile.js deleted file mode 100644 index 0e9bc8ea372..00000000000 --- a/scripts/vfs/gulpfile.js +++ /dev/null @@ -1,24 +0,0 @@ -const gulp = require("gulp"); -const gutil = require("gulp-util"); -const sourcemaps = require("gulp-sourcemaps"); -const tsb = require("gulp-tsb"); -const mocha = require("gulp-mocha"); -const del = require("del"); - -const project = tsb.create("tsconfig.json") - -gulp.task("clean", () => del(["dist/**/*"])); - -gulp.task("build", () => gulp.src(["src/**/*.ts"]) - .pipe(sourcemaps.init()) - .pipe(project()) - .pipe(sourcemaps.write(".", { sourceRoot: "../src", includeContent: false, destPath: "dist" })) - .pipe(gulp.dest("dist"))); - -gulp.task("test", ["build"], () => gulp - .src(["dist/tests/index.js"], { read: false }) - .pipe(mocha({ reporter: "min" }))); - -gulp.task("watch", () => gulp.watch(["src/**/*", "tsconfig.json"], ["test"])); - -gulp.task("default", ["test"]); \ No newline at end of file diff --git a/scripts/vfs/package.json b/scripts/vfs/package.json deleted file mode 100644 index 0ba2ebea236..00000000000 --- a/scripts/vfs/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "private": true, - "name": "@typescript/vfs", - "version": "0.0.0", - "description": "JavaScript Virtual File System", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "author": "Ron Buckton (ron.buckton@microsoft.com)", - "license": "Apache-2.0", - "dependencies": { - "@typescript/vfs-core": "file:../vfs-core", - "@typescript/vfs-errors": "file:../vfs-errors", - "@typescript/vfs-path": "file:../vfs-path" - }, - "devDependencies": { - "@types/chai": "^4.0.10", - "@types/mocha": "^2.2.44", - "@types/node": "^8.0.20", - "@types/source-map-support": "^0.4.0", - "chai": "^4.1.2", - "del": "^2.0.2", - "gulp": "^3.9.1", - "gulp-mocha": "^4.3.1", - "gulp-sourcemaps": "^2.6.1", - "gulp-tsb": "^2.0.5", - "mocha": "^4.0.1", - "source-map-support": "^0.5.0", - "typescript": "^2.6.1" - } -} diff --git a/scripts/vfs/src/constants.ts b/scripts/vfs/src/constants.ts deleted file mode 100644 index 7edcd6349e6..00000000000 --- a/scripts/vfs/src/constants.ts +++ /dev/null @@ -1,58 +0,0 @@ -// file type -export const S_IFMT = 0o170000; // file type -export const S_IFSOCK = 0o140000; // socket -export const S_IFLNK = 0o120000; // symbolic link -export const S_IFREG = 0o100000; // regular file -export const S_IFBLK = 0o060000; // block device -export const S_IFDIR = 0o040000; // directory -export const S_IFCHR = 0o020000; // character device -export const S_IFIFO = 0o010000; // FIFO - -// file mode bits -export const S_ISUID = 0o004000; // set-user-ID bit -export const S_ISGID = 0o002000; // set-group-ID bit -export const S_ISVTX = 0o001000; // sticky bit - -// file permission bits -export const S_IRUSR = 0o000400; // read by owner -export const S_IWUSR = 0o000200; // write by owner -export const S_IXUSR = 0o000100; // execute by owner -export const S_IRWXU = 0o000700; // read/write/execute by owner -export const S_IRGRP = 0o000040; // read by group -export const S_IWGRP = 0o000020; // write by group -export const S_IXGRP = 0o000010; // execute by group -export const S_IRWXG = 0o000070; // read/write/execute by group -export const S_IROTH = 0o000004; // read by others -export const S_IWOTH = 0o000002; // write by others -export const S_IXOTH = 0o000001; // execute by others -export const S_IRWXO = 0o000007; // read/write/execute by others - -export const SEEK_SET = 0; -export const SEEK_CUR = 1; -export const SEEK_END = 2; - -export const O_ACCMODE = 0o00000003; -export const O_RDONLY = 0o00000000; -export const O_WRONLY = 0o00000001; -export const O_RDWR = 0o00000002; - -export const O_CREAT = 0o00000100; -export const O_EXCL = 0o00000200; -export const O_TRUNC = 0o00001000; -export const O_APPEND = 0o00002000; -export const O_SYNC = 0o00010000; // explicit fsync -export const O_DIRECTORY = 0o00200000; -export const O_NOFOLLOW = 0o00400000; -export const O_PATH = 0o10000000; - - -export const F_OK = 0o00000000; // path is visible to the current process -export const X_OK = 0o00000001; // path can be executed or searched by the current process -export const W_OK = 0o00000002; // path can be written to by the current process -export const R_OK = 0o00000004; // path can be read by the current process - -export const CAP_CHOWN = 0; // can change file ownership -export const CAP_FOWNER = 3; // overrdies file ownership restrictions -export const CAP_FSETID = 4; // allows S_ISGID and S_ISUID flags -export const CAP_FSETGID = 6; // allows use of setgid and setgroups -export const CAP_FSETUID = 7; // allows use of setuid \ No newline at end of file diff --git a/scripts/vfs/src/fileSet.ts b/scripts/vfs/src/fileSet.ts deleted file mode 100644 index 3e0b35f0c6f..00000000000 --- a/scripts/vfs/src/fileSet.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { FileSystemResolver } from "./fileSystem"; - -export interface FileSet { - [name: string]: DirectoryLike | FileLike | Link | Symlink | Mount | null | undefined; -} - -export type DirectoryLike = FileSet | Directory; -export type FileLike = File | Buffer | string; - -/** Extended options for a directory in a `FileMap` */ -export class Directory { - public readonly files: FileSet; - public readonly uid: number | undefined; - public readonly gid: number | undefined; - public readonly mode: number | undefined; - public readonly meta: Record | undefined; - constructor(files: FileSet, { uid, gid, mode, meta }: { uid?: number, gid?: number, mode?: number, meta?: Record } = {}) { - this.files = files; - this.uid = uid; - this.gid = gid; - this.mode = mode; - this.meta = meta; - } -} - -/** Extended options for a file in a `FileMap` */ -export class File { - public readonly data: Buffer | string; - public readonly encoding: string | undefined; - public readonly uid: number | undefined; - public readonly gid: number | undefined; - public readonly mode: number | undefined | undefined; - public readonly meta: Record | undefined; - constructor(data: Buffer | string, { uid, gid, mode, meta, encoding }: { encoding?: string, uid?: number, gid?: number, mode?: number, meta?: Record } = {}) { - this.data = data; - this.encoding = encoding; - this.uid = uid; - this.gid = gid; - this.mode = mode; - this.meta = meta; - } -} - -/** Extended options for a hard link in a `FileMap` */ -export class Link { - public readonly path: string; - constructor(path: string) { - this.path = path; - } -} - -/** Extended options for a symbolic link in a `FileMap` */ -export class Symlink { - public readonly symlink: string; - public readonly uid: number | undefined; - public readonly gid: number | undefined; - public readonly mode: number | undefined; - public readonly meta: Record | undefined; - constructor(symlink: string, { uid, gid, mode, meta }: { uid?: number, gid?: number, mode?: number, meta?: Record } = {}) { - this.symlink = symlink; - this.uid = uid; - this.gid = gid; - this.mode = mode; - this.meta = meta; - } -} - -/** Extended options for mounting a virtual copy of an external file system via a `FileMap` */ -export class Mount { - public readonly source: string; - public readonly resolver: FileSystemResolver; - public readonly uid: number | undefined; - public readonly gid: number | undefined; - public readonly mode: number | undefined; - public readonly meta: Record | undefined; - constructor(source: string, resolver: FileSystemResolver, { uid, gid, mode, meta }: { uid?: number, gid?: number, mode?: number, meta?: Record } = {}) { - this.source = source; - this.resolver = resolver; - this.uid = uid; - this.gid = gid; - this.mode = mode; - this.meta = meta; - } -} \ No newline at end of file diff --git a/scripts/vfs/src/fileSystem.ts b/scripts/vfs/src/fileSystem.ts deleted file mode 100644 index 6b7ab4dd6b1..00000000000 --- a/scripts/vfs/src/fileSystem.ts +++ /dev/null @@ -1,2531 +0,0 @@ -import * as events from "events"; -import * as constants from "./constants"; -import * as vpath from "@typescript/vfs-path"; -import * as core from "@typescript/vfs-core"; -import { IOError } from "@typescript/vfs-errors"; -import { Inode, FileInode, isFileInode, DirectoryInode, isDirectoryInode, SymlinkInode, isSymlinkInode } from "./inode"; -import { FileSet, File, Directory, Link, Symlink, Mount } from "./fileSet"; -import { Stats } from "./stats"; -import { FSWatcher, FSWatcherEntry, FSWatcherEntrySet, WatchedFile } from "./watcher"; - -const _throw = (e: any) => { throw e; }; - -const RWX_OK = constants.R_OK | constants.W_OK | constants.X_OK; - -let devCount = 0; // A monotonically increasing count of device ids -let inoCount = 0; // A monotonically increasing count of inodes -let fdCount = 0; // A monotonically increasing count of file descriptors -let wdCount = 0; // A monotonically increasing count of watch descriptors -let cookieCount = 0; // A monotonically increasing count of watch event cookies - -/** - * Represents a virtual POSIX-like file system. - */ -export class FileSystem { - public readonly ignoreCase: boolean; - public readonly stringComparer: (a: string, b: string) => number; - - private static _portableFilenameCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"; - - // lazy-initialized state that should be mutable even if the FileSystem is frozen. - private _lazy: { - links?: core.SortedMap; - shadows?: Map; - watchedFiles?: core.SortedMap>; - meta?: core.Metadata; - watchers?: core.SortedMap; - } = {}; - - private _cwd: string; - private _uid: number; - private _gid: number; - private _umask: number; - private _time: number | Date | (() => number | Date); - private _timers: FileSystemTimers; - private _openFiles = new Map(); - private _shadowRoot: FileSystem | undefined; - private _dirStack: string[] | undefined; - private _noRecursiveWatchers: boolean; - - constructor(ignoreCase: boolean, options: FileSystemOptions = {}) { - const { uid = 0, gid = 0, umask = 0o022, time = -1, timers, files, meta, noRecursiveWatchers = false } = options; - this.ignoreCase = ignoreCase; - this.stringComparer = this.ignoreCase ? vpath.compareCaseInsensitive : vpath.compareCaseSensitive; - this._uid = uid; - this._gid = gid; - this._umask = umask; - this._time = time; - this._timers = timers || { setInterval, clearInterval }; - this._noRecursiveWatchers = noRecursiveWatchers; - - if (meta) { - for (const key of Object.keys(meta)) { - this.meta.set(key, meta[key]); - } - } - - if (files) { - this._applyFiles(files, /*dirname*/ ""); - } - - let cwd = options.cwd; - if ((!cwd || !vpath.isRoot(cwd)) && this._lazy.links) { - for (const name of this._lazy.links.keys()) { - cwd = cwd ? vpath.resolve(name, cwd) : name; - break; - } - } - - if (cwd) { - vpath.validate(cwd, vpath.ValidationFlags.Absolute); - this.mkdirpSync(cwd, /*mode*/ 0o777); - } - - this._cwd = cwd || ""; - } - - /** - * Gets metadata for this `FileSystem`. - */ - public get meta(): core.Metadata { - if (!this._lazy.meta) { - this._lazy.meta = new core.Metadata(this._shadowRoot ? this._shadowRoot.meta : undefined); - } - return this._lazy.meta; - } - - /** - * Gets a value indicating whether the file system is read-only. - */ - public get isReadonly() { - return Object.isFrozen(this); - } - - /** - * Makes the file system read-only. - */ - public makeReadonly() { - Object.freeze(this); - return this; - } - - /** - * Gets the file system shadowed by this file system. - */ - public get shadowRoot() { - return this._shadowRoot; - } - - /** - * Gets a shadow of this file system. - */ - public shadow(ignoreCase = this.ignoreCase) { - if (!this.isReadonly) throw new Error("Cannot shadow a mutable file system."); - if (ignoreCase && !this.ignoreCase) throw new Error("Cannot create a case-insensitive file system from a case-sensitive one."); - const fs = new FileSystem(ignoreCase, { - uid: this._uid, - gid: this._gid, - umask: this._umask, - time: this._time, - }); - fs._shadowRoot = this; - fs._cwd = this._cwd; - return fs; - } - - /** - * Gets the metadata object for a path. - * @param path - */ - public filemeta(path: string): core.Metadata { - path = this._resolve(path); - const { node } = this._find(path) || _throw(new IOError("ENOENT", "scandir", path)); - return this._filemeta(node); - } - - private _filemeta(node: Inode): core.Metadata { - if (!node.meta) { - const parentMeta = node.shadowRoot && this._shadowRoot && this._shadowRoot._filemeta(node.shadowRoot); - node.meta = new core.Metadata(parentMeta); - } - return node.meta; - } - - /** - * Gets the user ID to use for file system access. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/geteuid.html - */ - public getuid() { - return this._uid; - } - - /** - * Sets the user ID to use for file system access. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/setuid.html - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/seteuid.html - */ - public setuid(value: number) { - if (this.isReadonly) throw new IOError("EPERM", "setuid"); - this._uid = value; - } - - /** - * Gets the group ID to use for file system access. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html - */ - public getgid() { - return this._gid; - } - - /** - * Sets the group ID to use for file system access. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/setgid.html - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html - */ - public setgid(value: number) { - if (this.isReadonly) throw new IOError("EPERM", "setgid"); - this._gid = value; - } - - /** - * Gets or sets the virtual process's file mode creation mask (umask) - * to `mask & 0o777` and returns the previous value of the mask. - * - * @link http://man7.org/linux/man-pages/man2/umask.2.html - */ - public umask(value?: number): number { - if (value !== undefined && this.isReadonly) throw new IOError("EPERM", "umask"); - const result = this._umask; - if (value !== undefined) { - this._umask = value; - } - return result; - } - - /** - * Gets or sets the timestamp (in milliseconds) used for file status, returning the previous timestamp. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/time.html - */ - public time(value?: number | Date | (() => number | Date)): number { - if (value !== undefined && this.isReadonly) throw new IOError("EPERM"); - let result = this._time; - if (typeof result === "function") result = result(); - if (typeof result === "object") result = result.getTime(); - if (result === -1) result = Date.now(); - if (value !== undefined) { - this._time = value; - } - return result; - } - - /** - * Get the pathname of the current working directory. - * - * @link - http://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html - */ - public cwd() { - if (!this._cwd) throw new Error("The current working directory has not been set."); - const { node } = this._find(this._cwd) || _throw(new IOError("ENOENT", "getcwd")); - if (!isDirectoryInode(node)) throw new IOError("ENOTDIR", "getcwd"); - if (!this._access(node, constants.X_OK)) throw new IOError("EPERM", "getcwd"); - return this._cwd; - } - - /** - * Changes the current working directory. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html - */ - public chdir(path: string) { - if (this.isReadonly) throw new IOError("EPERM", "chdir", path); - path = this._resolve(path); - const { node } = this._find(path) || _throw(new IOError("ENOENT", "chdir", path)); - if (!isDirectoryInode(node)) throw new IOError("ENOTDIR", "chdir", path); - if (this._cwd !== path) { - this._cwd = path; - } - } - - /** - * Pushes the current directory onto the directory stack and changes the current working directory to the supplied path. - */ - public pushd(path?: string) { - if (this.isReadonly) throw new IOError("EPERM", "chdir", path); - if (path) path = this._resolve(path); - if (this._cwd) { - if (!this._dirStack) this._dirStack = []; - this._dirStack.push(this._cwd); - } - if (path && path !== this._cwd) { - this.chdir(path); - } - } - - /** - * Pops the previous directory from the location stack and changes the current directory to that directory. - */ - public popd() { - if (this.isReadonly) throw new IOError("EPERM", "popd"); - const path = this._dirStack && this._dirStack.pop(); - if (path) { - this.chdir(path); - } - } - - /** - * Apply a file map to the file system. - */ - public apply(files: FileSet) { - this._applyFiles(files, this._cwd); - } - - /** - * Recursively remove all files and directories underneath the provided path. - */ - public rimrafSync(path: string) { - try { - const stats = this.lstatSync(path); - if (stats.isFile() || stats.isSymbolicLink()) { - this.unlinkSync(path); - } - else if (stats.isDirectory()) { - for (const file of this.readdirSync(path)) { - this.rimrafSync(vpath.combine(path, file)); - } - this.rmdirSync(path); - } - } - catch (e) { - if (e.code === "ENOENT") return; - throw e; - } - } - - /** - * Checks whether the calling process can access the file `path`. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html - */ - public access(path: string, callback: (err: Error | null) => void): void; - /** - * Checks whether the calling process can access the file `path`. If `path` is a symbolic link, it is dereferenced. - * @param mode One or more of the constants `F_OK`, `R_OK`, `W_OK`, or `X_OK`. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html - */ - public access(path: string, mode: number | undefined, callback: (err: Error | null) => void): void; - public access(path: string, mode: number | typeof callback, callback?: (err: Error | null) => void) { - if (typeof mode === "function") callback = mode, mode = undefined; - if (!callback) throw new IOError("EINVAL"); - try { - this.accessSync(path, mode); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Checks whether the calling process can access the file `path`. If `path` is a symbolic link, it is dereferenced. - * @param mode One or more of the constants `F_OK`, `R_OK`, `W_OK`, or `X_OK`. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html - */ - public accessSync(path: string, mode: number = constants.F_OK) { - path = this._resolve(path); - if (!isFinite(mode) || (mode & ~RWX_OK)) throw new IOError("EINVAL", "access", path); - const { node } = this._find(path) || _throw(new IOError("ENOENT", "access", path)); - if (!this._access(node, mode)) throw new IOError("EACCES", "access", path); - } - - private _access(node: Inode, mode: number) { - let flags = (node.mode & constants.S_IRWXO) >> 0; - if (this.getgid() === node.gid) flags |= (node.mode & constants.S_IRWXG) >> 3; - if (this.getuid() === node.uid) flags |= (node.mode & constants.S_IRWXU) >> 6; - return (flags & mode) === mode; - } - - /** - * Changes the permissions of a file. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html - */ - public chmod(path: string, mode: number, callback: (err: Error | null) => void) { - try { - this.chmodSync(path, mode); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Changes the permissions of a file. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html - */ - public chmodSync(path: string, mode: number) { - path = this._resolve(path); - this._chmod("chmod", this._find(path) || _throw(new IOError("ENOENT", "chmod", path)), mode, path); - } - - /** - * Changes the permissions of a file. Like `chmod`, except symbolic links are not dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html - */ - public lchmod(path: string, mode: number, callback: (err: Error | null) => void) { - try { - this.lchmodSync(path, mode); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Changes the permissions of a file. Like `chmod`, except symbolic links are not dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html - */ - public lchmodSync(path: string, mode: number) { - path = this._resolve(path); - this._chmod("lchmod", this._lfind(path) || _throw(new IOError("ENOENT", "lchmod", path)), mode, path); - } - - /** - * Changes the permissions of an open file - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html - */ - public fchmod(fd: number, mode: number, callback: (err: Error | null) => void) { - try { - this.fchmodSync(fd, mode); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Changes the permissions of an open file - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html - */ - public fchmodSync(fd: number, mode: number) { - this._chmod("fchmod", this._file("fchmod", fd), mode); - } - - private _chmod(syscall: string, entry: FileDescription, mode: number, path?: string) { - if (!isFinite(mode)) throw new IOError("EINVAL", syscall, path); - if (this._uid !== 0 && this._uid !== entry.node.uid) throw new IOError("EPERM", syscall, path); - if (this.isReadonly) throw new IOError("EROFS", syscall, path); - - entry.node.mode = (entry.node.mode & constants.S_IFMT) | (mode & 0o7777); - entry.node.ctimeMs = this.time(); - - this._notifySelf(entry.node, "rename"); - } - - /** - * Changes the ownership of a file. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html - */ - public chown(path: string, uid: number, gid: number, callback: (err: Error | null) => void) { - try { - this.chownSync(path, uid, gid); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Changes the ownership of a file. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html - */ - public chownSync(path: string, uid: number, gid: number) { - path = this._resolve(path); - this._chown("chown", this._find(path) || _throw(new IOError("ENOENT", "chown", path)), uid, gid, path); - } - - /** - * Changes the ownership of a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lchown.html - */ - public lchown(path: string, uid: number, gid: number, callback: (err: Error | null) => void) { - try { - this.lchownSync(path, uid, gid); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Changes the ownership of a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lchown.html - */ - public lchownSync(path: string, uid: number, gid: number) { - path = this._resolve(path); - this._chown("lchown", this._lfind(path) || _throw(new IOError("ENOENT", "lchown", path)), uid, gid, path); - } - - /** - * Changes the ownership of an open file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html - */ - public fchown(fd: number, uid: number, gid: number, callback: (err: Error | null) => void) { - try { - this.fchownSync(fd, uid, gid); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Changes the ownership of an open file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html - */ - public fchownSync(fd: number, uid: number, gid: number) { - this._chown("fchown", this._file("fchown", fd), uid, gid); - } - - private _chown(syscall: string, entry: FileDescription, uid: number, gid: number, path?: string) { - if (!isFinite(uid) || !isFinite(gid)) throw new IOError("EINVAL", syscall, path); - if (uid === entry.node.uid) uid = -1; - if (gid === entry.node.gid) gid = -1; - if (uid === -1 && gid === -1) return; - if (uid !== -1 && this._uid !== 0) throw new IOError("EPERM", syscall, path); - if (gid !== -1 && this._uid !== 0 && this._uid !== entry.node.uid) throw new IOError("EPERM", syscall, path); - if (this.isReadonly) throw new IOError("EROFS", syscall, path); - - if (uid !== -1) entry.node.uid = uid; - if (gid !== -1) entry.node.gid = gid - entry.node.mode &= ~(constants.S_ISGID | constants.S_ISUID); - entry.node.ctimeMs = this.time(); - - this._notifySelf(entry.node, "rename"); - } - - /** - * Sets file access and modification times. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html - */ - public utimes(path: string, atime: number | Date, mtime: number | Date, callback: (err: Error | null) => void) { - try { - this.utimesSync(path, atime, mtime); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Sets file access and modification times. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html - */ - public utimesSync(path: string, atime: number | Date, mtime: number | Date) { - path = this._resolve(path); - this._utimes("utimes", this._find(path) || _throw(new IOError("ENOENT", "utimes", path)), atime, mtime, path); - } - - /** - * Sets file access and modification times. If `path` is a symbolic link, it is not dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html - */ - public lutimes(path: string, atime: number | Date, mtime: number | Date, callback: (err: Error | null) => void) { - try { - this.lutimesSync(path, atime, mtime); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Sets file access and modification times. If `path` is a symbolic link, it is not dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html - */ - public lutimesSync(path: string, atime: number | Date, mtime: number | Date) { - path = this._resolve(path); - this._utimes("lutimes", this._lfind(path) || _throw(new IOError("ENOENT", "lutimes", path)), atime, mtime, path); - } - - /** - * Sets file access and modification times. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/futimes.html - */ - public futimes(fd: number, atime: number | Date, mtime: number | Date, callback: (err: Error | null) => void) { - try { - this.futimesSync(fd, atime, mtime); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Sets file access and modification times. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/futimes.html - */ - public futimesSync(fd: number, atime: number | Date, mtime: number | Date) { - this._utimes("futimes", this._file("futimes", fd), atime, mtime); - } - - private _utimes(syscall: string, entry: FileDescription, atime: number | Date, mtime: number | Date, path?: string) { - if (this.isReadonly) throw new IOError("EROFS", syscall, path); - const atimeMs = typeof atime === "number" ? atime : atime.getTime(); - const mtimeMs = typeof mtime === "number" ? mtime : mtime.getTime(); - if (!isFinite(atimeMs) || !isFinite(mtimeMs)) throw new IOError("EINVAL", syscall, path); - - entry.node.atimeMs = atimeMs; - entry.node.mtimeMs = mtimeMs; - entry.node.ctimeMs = this.time(); - - this._notifySelf(entry.node, "rename"); - } - - public fsync(fd: number, callback: (err: Error | null) => void): void { - try { - this.fsyncSync(fd); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - public fsyncSync(fd: number): void { - this._fsync(this._file("fsync", fd, "file"), /*metadata*/ true); - } - - public fdatasync(fd: number, callback: (err: Error | null) => void): void { - try { - this.fdatasyncSync(fd); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - public fdatasyncSync(fd: number): void { - this._fsync(this._file("fsyncdata", fd, "file"), /*metadata*/ false); - } - - private _fsync(entry: OpenFileDescription, metadata: boolean) { - if (isFileInode(entry.node) && entry.buffer && entry.buffer !== entry.node.buffer) { - const time = this.time(); - entry.node.buffer = entry.buffer; - entry.node.mtimeMs = time; - entry.node.ctimeMs = time; - if (metadata) { - entry.node.size = entry.node.buffer.byteLength; - } - - this._notifySelf(entry.node, "change"); - } - } - - /** - * Get file status. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html - */ - public stat(path: string, callback: (err: Error | null, stats: Stats | null) => void) { - try { - process.nextTick(callback, null, this.statSync(path)); - } - catch (e) { - process.nextTick(callback, e, null); - } - } - - /** - * Get file status. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html - */ - public statSync(path: string) { - path = this._resolve(path); - return this._stat("stat", this._find(path) || _throw(new IOError("ENOENT", "stat", path)), path); - } - - /** - * Get file status. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html - */ - public lstat(path: string, callback: (err: Error | null, stats: Stats | null) => void) { - try { - process.nextTick(callback, null, this.lstatSync(path)); - } - catch (e) { - process.nextTick(callback, e, null); - } - } - - /** - * Get file status. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html - */ - public lstatSync(path: string) { - path = this._resolve(path); - return this._stat("lstat", this._lfind(path) || _throw(new IOError("ENOENT", "lstat", path)), path); - } - - /** - * Get file status. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fstat.html - */ - public fstat(fd: number, callback: (err: Error | null, stats: Stats | null) => void) { - try { - process.nextTick(callback, null, this.fstatSync(fd)); - } - catch (e) { - process.nextTick(callback, e, null); - } - } - - /** - * Get file status. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fstat.html - */ - public fstatSync(fd: number) { - return this._stat("fstat", this._file("fstat", fd)); - } - - private _stat(syscall: string, entry: FileDescription, path?: string) { - const node = entry.node; - return new Stats( - node.dev, - node.ino, - node.mode, - node.nlink, - node.uid, - node.gid, - /*rdev*/ 0, - /*size*/ isFileInode(node) ? this._getSize(node) : isSymlinkInode(node) ? node.symlink.length : 0, - /*blksize*/ 4096, - /*blocks*/ 0, - node.atimeMs, - node.mtimeMs, - node.ctimeMs, - node.birthtimeMs, - ); - } - - public scanSync(path: string, axis: Axis, traversal: Traversal) { - path = this._resolve(path); - const results: string[] = []; - this._scan(path, this.statSync(path), axis, traversal, /*noFollow*/ false, results); - return results; - } - - public lscanSync(path: string, axis: Axis, traversal: Traversal) { - path = this._resolve(path); - const results: string[] = []; - this._scan(path, this.statSync(path), axis, traversal, /*noFollow*/ true, results); - return results; - } - - private _scan(path: string, stats: Stats, axis: Axis, traversal: Traversal, noFollow: boolean, results: string[]) { - if (axis === "ancestors-or-self" || axis === "self" || axis === "descendants-or-self") { - if (!traversal.accept || traversal.accept(path, stats)) { - results.push(path); - } - } - if (axis === "ancestors-or-self" || axis === "ancestors") { - const dirname = vpath.dirname(path); - if (dirname !== path) { - try { - const stats = this._stat("scandir", this._walk(dirname, noFollow) || _throw(new IOError("ENOENT", "scandir", dirname))); - if (!traversal.traverse || traversal.traverse(dirname, stats)) { - this._scan(dirname, stats, "ancestors-or-self", traversal, noFollow, results); - } - } - catch { /*ignored*/ } - } - } - if (axis === "descendants-or-self" || axis === "descendants") { - if (stats.isDirectory() && (!traversal.traverse || traversal.traverse(path, stats))) { - for (const file of this.readdirSync(path)) { - try { - const childpath = vpath.combine(path, file); - const stats = this._stat("scandir", this._walk(childpath, noFollow) || _throw(new IOError("ENOENT", "scandir", childpath))); - this._scan(childpath, stats, "descendants-or-self", traversal, noFollow, results); - } - catch { /*ignored*/ } - } - } - } - } - - /** - * Read a directory. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html - */ - public readdir(path: string, callback: (err: Error | null, files: string[] | null) => void) { - try { - process.nextTick(callback, null, this.readdirSync(path)); - } - catch (e) { - process.nextTick(callback, e, null); - } - } - - /** - * Read a directory. If `path` is a symbolic link, it is dereferenced. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html - */ - public readdirSync(path: string) { - path = this._resolve(path); - const { node } = this._find(path) || _throw(new IOError("ENOENT", "readdir", path)); - if (!isDirectoryInode(node)) throw new IOError("ENOTDIR", "readdir", path); - if (!this._access(node, constants.R_OK)) throw new IOError("EACCES", "readdir", path); - return Array.from(this._getLinks(node).keys()); - } - - /** - * Mounts a physical or virtual file system at a location in this virtual file system. - * - * @param source The path in the physical (or other virtual) file system. - * @param target The path in this virtual file system. - * @param resolver An object used to resolve files in `source`. - */ - public mount(source: string, target: string, resolver: FileSystemResolver, callback: (err: Error | null) => void): void; - /** - * Mounts a physical or virtual file system at a location in this virtual file system. - * - * @param source The path in the physical (or other virtual) file system. - * @param target The path in this virtual file system. - * @param resolver An object used to resolve files in `source`. - */ - public mount(source: string, target: string, resolver: FileSystemResolver, mode: number | undefined, callback: (err: Error | null) => void): void; - public mount(source: string, target: string, resolver: FileSystemResolver, mode: number | typeof callback, callback?: (err: Error | null) => void) { - if (typeof mode === "function") callback = mode, mode = undefined; - if (typeof callback !== "function") throw new IOError("EINVAL"); - try { - this.mountSync(source, target, resolver, mode); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Mounts a physical or virtual file system at a location in this virtual file system. - * - * @param source The path in the physical (or other virtual) file system. - * @param target The path in this virtual file system. - * @param resolver An object used to resolve files in `source`. - */ - public mountSync(source: string, target: string, resolver: FileSystemResolver, mode = 0o777) { - source = vpath.validate(source, vpath.ValidationFlags.Absolute); - target = this._resolve(target); - mode = mode & 0o1777; // allows S_ISVTX bit - - let parent: DirectoryInode | undefined; - let name: string; - - // special case for FS root - if (this.stringComparer(vpath.dirname(target), target) === 0) { - if (this.getuid() !== 0) throw new IOError("EPERM", "mount", source, target); - name = target; - } - else { - const entry = this._find(vpath.dirname(target)) || _throw(new IOError("ENOENT", "mount", source, target)); - if (!isDirectoryInode(entry.node)) throw new IOError("ENOTDIR", "mount", source, target); - if (!this._access(entry.node, constants.W_OK)) throw new IOError("EACCES", "mount", source, target); - parent = entry.node; - name = vpath.basename(target); - } - - const links = this._getLinks(parent); - if (links.has(name)) throw new IOError("EEXIST", "mount", source, target); - if (this.isReadonly) throw new IOError("EROFS", "mount", source, target); - - const node = parent - ? this._mknod(parent.dev, constants.S_IFDIR, mode) - : this._mknod(++devCount, constants.S_IFDIR, mode, /*uid*/ 0, /*gid*/ 0, /*umask*/ 0o000); - - node.source = source; - node.resolver = resolver; - - this._addLink(parent, links, name, node); - - if (parent) { - parent.mtimeMs = this.time(); - this._notifyChild(parent, "rename", name); - this._notifyAncestors(parent, "change"); - } - else if (!this._cwd) { - this._cwd = name; - } - } - - /** - * Make a directory. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html - */ - public mkdir(path: string, callback: (error: Error | null) => void): void; - /** - * Make a directory. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html - */ - public mkdir(path: string, mode: number | undefined, callback: (error: Error | null) => void): void; - public mkdir(path: string, mode: number | typeof callback, callback?: (error: Error | null) => void) { - if (typeof mode === "function") callback = mode, mode = undefined; - if (typeof callback !== "function") throw new IOError("EINVAL"); - try { - this.mkdirSync(path, mode); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Make a directory. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html - */ - public mkdirSync(path: string, mode: number = 0o777) { - path = this._resolve(path); - if (!isFinite(mode)) throw new IOError("EINVAL", "mkdir", path); - mode = mode & 0o1777; // allows S_ISVTX bit - - let parent: DirectoryInode | undefined; - let name: string; - - // special case for FS root - if (this.stringComparer(vpath.dirname(path), path) === 0) { - if (this.getuid() !== 0) throw new IOError("EPERM", "mkdir", path); - name = path; - } - else { - const parentEntry = this._find(vpath.dirname(path)) || _throw(new IOError("ENOENT", "mkdir", path)); - if (!isDirectoryInode(parentEntry.node)) throw new IOError("ENOTDIR", "mkdir", path); - if (!this._access(parentEntry.node, constants.W_OK)) throw new IOError("EACCES", "mkdir", path); - parent = parentEntry.node; - name = vpath.basename(path); - } - - const links = this._getLinks(parent); - if (links.has(name)) throw new IOError("EEXIST", "mkdir", path); - if (this.isReadonly) throw new IOError("EROFS", "mkdir", path); - - const node = parent - ? this._mknod(parent.dev, constants.S_IFDIR, mode) - : this._mknod(++devCount, constants.S_IFDIR, mode, /*uid*/ 0, /*gid*/ 0, /*umask*/ 0o000); - - if (parent && parent.mode & constants.S_ISGID) { - node.mode |= constants.S_ISGID; - node.gid = parent.gid; - } - - this._addLink(parent, links, name, node); - - if (parent) { - parent.mtimeMs = this.time(); - this._notifyChild(parent, "rename", name); - this._notifyAncestors(parent, "change"); - } - else if (!this._cwd) { - this._cwd = name; - } - } - - /** - * Make a directory and all of its parent paths (if they don't exist). - */ - public mkdirp(path: string, callback: (error: Error | null) => void): void; - /** - * Make a directory and all of its parent paths (if they don't exist). - */ - public mkdirp(path: string, mode: number | undefined, callback: (error: Error | null) => void): void; - public mkdirp(path: string, mode: number | typeof callback, callback?: (error: Error | null) => void) { - if (typeof mode === "function") callback = mode, mode = undefined; - if (typeof callback !== "function") throw new IOError("EINVAL"); - try { - this.mkdirpSync(path, mode); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Make a directory and all of its parent paths (if they don't exist). - */ - public mkdirpSync(path: string, mode: number = 0o777) { - path = this._resolve(path); - try { - this.mkdirSync(path, mode); - } - catch (e) { - if (e.code === "ENOENT") { - this.mkdirpSync(vpath.dirname(path), mode); - this.mkdirSync(path, mode); - } - else if (e.code !== "EEXIST") { - throw e; - } - } - } - - /** - * Remove a directory. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html - */ - public rmdir(path: string, callback: (err: Error | null) => void) { - try { - this.rmdirSync(path); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Remove a directory. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html - */ - public rmdirSync(path: string) { - path = this._resolve(path); - - const { parent, node, basename } = this._find(path) || _throw(new IOError("ENOENT", "rmdir", path)); - if (!isDirectoryInode(node)) throw new IOError("ENOTDIR", "rmdir", path); - if (this._getLinks(node).size !== 0) throw new IOError("ENOTEMPTY", "rmdir", path); - if (!this._access(parent, constants.W_OK)) throw new IOError("EACCES", "mkdir", path); - if (this.isReadonly) throw new IOError("EROFS", "rmdir", path); - - const name = vpath.basename(path); - const links = this._getLinks(parent); - this._removeLink(parent, links, name, node); - - const time = this.time(); - parent.mtimeMs = time; - node.ctimeMs = time; - this._invalidatePaths(node); - - this._notifyChild(parent, "rename", basename); - this._notifyAncestors(parent, "change"); - this._removeWatchers(parent, node, basename); - } - - /** - * Link one file to another file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html - */ - public link(oldpath: string, newpath: string, callback: (err: Error | null) => void) { - try { - this.linkSync(oldpath, newpath); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Link one file to another file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html - */ - public linkSync(oldpath: string, newpath: string) { - oldpath = this._resolve(oldpath); - newpath = this._resolve(newpath); - - const { node, basename: oldBasename } = this._find(oldpath) || _throw(new IOError("ENOENT", "link", oldpath, newpath)); - if (isDirectoryInode(node)) throw new IOError("EPERM", "link", oldpath, newpath); - - const { node: newParent } = this._find(vpath.dirname(newpath)) || _throw(new IOError("ENOENT", "link", oldpath, newpath)); - if (!isDirectoryInode(newParent)) throw new IOError("ENOTDIR", "link", oldpath, newpath); - - const newParentLinks = this._getLinks(newParent); - const newBasename = vpath.basename(newpath); - - if (newParentLinks.has(newBasename)) throw new IOError("EEXIST", "link", oldpath, newpath); - if (!this._access(newParent, constants.W_OK)) throw new IOError("EACCES", "link", oldpath, newpath); - if (this.isReadonly) throw new IOError("EROFS", "link", oldpath, newpath); - - this._addLink(newParent, newParentLinks, newBasename, node); - - const time = this.time(); - newParent.mtimeMs = time; - node.ctimeMs = time; - this._invalidatePaths(node); - - this._notifyChild(newParent, "rename", newBasename); - this._notifyAncestors(newParent, "change"); - } - - /** - * Remove a directory entry. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html - */ - public unlink(path: string, callback: (err: Error | null) => void) { - try { - this.unlinkSync(path); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Remove a directory entry. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html - */ - public unlinkSync(path: string) { - path = this._resolve(path); - - const { parent, node, basename } = this._lfind(path) || _throw(new IOError("ENOENT", "unlink", path)); - if (isDirectoryInode(node)) throw new IOError("EISDIR", "unlink", path); - if (!this._access(parent, constants.W_OK)) throw new IOError("EACCES", "unlink", path); - if (this.isReadonly) throw new IOError("EROFS", "unlink", path); - - const links = this._getLinks(parent); - this._removeLink(parent, links, basename, node); - - const time = this.time(); - parent.mtimeMs = time; - node.ctimeMs = time; - this._invalidatePaths(node); - this._notifyChild(parent, "rename", basename); - this._notifyAncestors(parent, "change"); - this._removeWatchers(parent, node, basename); - } - - /** - * Rename a file - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html - */ - public rename(oldpath: string, newpath: string, callback: (err: Error | null) => void) { - try { - this.renameSync(oldpath, newpath); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Rename a file - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html - */ - public renameSync(oldpath: string, newpath: string) { - oldpath = this._resolve(oldpath); - newpath = this._resolve(newpath); - - const { parent: oldParent, node, basename: oldBasename } = this._lfind(oldpath) || _throw(new IOError("ENOENT", "rename", oldpath, newpath)); - const { node: newParent } = this._find(vpath.dirname(newpath)) || _throw(new IOError("ENOENT", "rename", oldpath, newpath)); - if (!isDirectoryInode(newParent)) throw new IOError("ENOTDIR", "rename", oldpath, newpath); - - const newBasename = vpath.basename(newpath); - const newParentLinks = this._getLinks(newParent); - const existingNode = newParentLinks.get(newBasename); - if (existingNode) { - if (isDirectoryInode(node)) { - if (!isDirectoryInode(existingNode)) throw new IOError("ENOTDIR", "rename", oldpath, newpath); - if (this._getLinks(existingNode).size > 0) throw new IOError("ENOTEMPTY", "rename", oldpath, newpath); - } - else { - if (isDirectoryInode(existingNode)) throw new IOError("EISDIR", "rename", oldpath, newpath); - } - } - - if (!this._access(oldParent, constants.W_OK)) throw new IOError("EACCES", "rename", oldpath, newpath); - if (!this._access(newParent, constants.W_OK)) throw new IOError("EACCES", "rename", oldpath, newpath); - if (this.isReadonly) throw new IOError("EROFS", "rename", oldpath, newpath); - - const time = this.time(); - if (existingNode) { - this._removeLink(newParent, newParentLinks, newBasename, existingNode); - this._invalidatePaths(existingNode); - existingNode.ctimeMs = time; - this._notifyChild(newParent, "rename", newBasename); - this._removeWatchers(newParent, existingNode, newBasename); - } - - const oldParentLinks = this._getLinks(oldParent); - this._replaceLink(oldParent, oldParentLinks, oldBasename, newParent, newParentLinks, newBasename, node); - - oldParent.mtimeMs = time; - newParent.mtimeMs = time; - this._invalidatePaths(node); - - const cookie = ++cookieCount; - this._notifyChild(oldParent, "rename", oldBasename); - this._notifyChild(newParent, "rename", newBasename); - this._notifyAncestors(newParent, "change"); - if (newParent !== oldParent) { - this._notifyAncestors(oldParent, "change"); - } - } - - /** - * Make a symbolic link - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html - */ - public symlink(target: string, linkpath: string, callback: (err: Error | null) => void) { - try { - this.symlinkSync(target, linkpath); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Make a symbolic link - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html - */ - public symlinkSync(target: string, linkpath: string) { - target = vpath.validate(target, vpath.ValidationFlags.RelativeOrAbsolute); - linkpath = this._resolve(linkpath); - - const dirname = vpath.dirname(linkpath); - const { node: parent } = this._find(dirname) || _throw(new IOError("ENOENT", "symlink", target, linkpath)); - if (!isDirectoryInode(parent)) throw new IOError("ENOTDIR", "symlink", target, linkpath); - - const basename = vpath.basename(linkpath); - const parentLinks = this._getLinks(parent); - - if (parentLinks.has(basename)) throw new IOError("EEXIST", "symlink", target, linkpath); - if (!this._access(parent, constants.W_OK)) throw new IOError("EACCES", "symlink", target, linkpath); - if (this.isReadonly) throw new IOError("EROFS", "symlink", target, linkpath); - - const node = this._mknod(parent.dev, constants.S_IFLNK, 0o666); - node.symlink = target; - - this._addLink(parent, parentLinks, basename, node); - - const time = this.time(); - parent.mtimeMs = time; - - this._notifyChild(parent, "rename", basename); - this._notifyAncestors(parent, "change"); - } - - /** - * Read the contents of a symbolic link - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html - */ - public readlink(path: string, callback: (err: Error | null, path: string | null) => void) { - try { - process.nextTick(callback, null, this.readlinkSync(path)); - } - catch (e) { - process.nextTick(callback, e, null); - } - } - - /** - * Read the contents of a symbolic link - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html - */ - public readlinkSync(path: string) { - path = this._resolve(path); - - const { node } = this._lfind(path) || _throw(new IOError("ENOENT", "readlink", path)); - if (!isSymlinkInode(node)) throw new IOError("EINVAL", "readlink", path); - - return node.symlink; - } - - /** - * Resolve a pathname - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html - */ - public realpath(path: string, callback: (err: Error | null, path: string | null) => void) { - try { - process.nextTick(callback, null, this.realpathSync(path)); - } - catch (e) { - process.nextTick(callback, e, null); - } - } - - /** - * Resolve a pathname - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html - */ - public realpathSync(path: string) { - path = this._resolve(path); - const entry = this._find(path) || _throw(new IOError("ENOENT", "realpath", path)); - return entry.path; - } - - /** - * Open a file - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html - */ - public open(path: string, flags: string | number, callback: (err: Error | null, fd: number | null) => void): void; - /** - * Open a file - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html - */ - public open(path: string, flags: string | number, mode: number, callback: (err: Error | null, fd: number | null) => void): void; - public open(path: string, flags: string | number, mode: number | typeof callback, callback?: (err: Error | null, fd: number | null) => void) { - if (typeof mode === "function") callback = mode, mode = undefined; - if (typeof callback !== "function") throw new IOError("EINVAL"); - try { - process.nextTick(callback, null, this.openSync(path, flags, mode)); - } - catch (e) { - process.nextTick(callback, e, null); - } - } - - /** - * Open a file - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html - */ - public openSync(path: string, flags: string | number, mode = 0o666): number { - path = this._resolve(path); - flags = parseFlags(flags); - if (!isFinite(flags) || !isFinite(mode)) throw new IOError("EINVAL", "open", path); - - const read = (flags & constants.O_ACCMODE) !== constants.O_WRONLY; - const write = flags & constants.O_ACCMODE && flags & constants.O_CREAT | constants.O_TRUNC; - if (write && this.isReadonly) throw new IOError("EROFS", "open", path); - - const basename = vpath.basename(path); - const entry = this._walk(path, /*noFollow*/ !!(flags & constants.O_NOFOLLOW)); - - let node: Inode; - let parent: Inode; - if (!entry) { - if (~flags & constants.O_CREAT) throw new IOError("ENOENT", "open", path); - if (flags & constants.O_DIRECTORY) throw new IOError("ENOTDIR", "open", path); - flags |= constants.O_TRUNC; - - const entry = this._walk(vpath.dirname(path), /*noFollow*/ false) || _throw(new IOError("ENOENT", "open", path)); - parent = entry.node; - - if (!isDirectoryInode(parent)) throw new IOError("ENOTDIR", "open", path); - if (!this._access(parent, constants.W_OK)) throw new IOError("EACCES", "open", path); - - node = this._mknod(parent.dev, constants.S_IFREG, mode); - node.buffer = Buffer.allocUnsafe(0); - if (parent.mode & constants.S_ISGID) { - node.mode |= constants.S_ISGID; - node.gid = parent.gid; - } - - const links = this._getLinks(parent); - this._addLink(parent, links, basename, node); - - const time = this.time(); - parent.mtimeMs = time; - - this._notifyChild(parent, "rename", basename); - this._notifyAncestors(parent, "change"); - } - else { - if (flags & constants.O_EXCL) throw new IOError("EEXIST", "open", path); - node = entry.node; - parent = entry.parent; - } - - if (flags & constants.O_DIRECTORY && isFileInode(node)) throw new IOError("ENOTDIR", "open", path); - if (write && isDirectoryInode(node)) throw new IOError("EISDIR", "open", path); - if (write && !this._access(node, constants.W_OK)) throw new IOError("EACCES", "open", path); - if (read && !this._access(node, constants.R_OK)) throw new IOError("EACCES", "open", path); - - const file: OpenFileDescription = { - fd: ++fdCount, - path, - basename, - parent, - node, - flags, - written: false, - offset: isFileInode(node) && (flags & (constants.O_APPEND | constants.O_TRUNC)) === constants.O_APPEND ? this._getSize(node) : 0, - buffer: undefined - }; - - this._openFiles.set(file.fd, file); - - if (flags & constants.O_TRUNC) { - file.buffer = Buffer.allocUnsafe(0); - if (flags & constants.O_SYNC) { - this._fsync(file, /*metadata*/ true); - } - } - - return file.fd; - } - - /** - * Close a file descriptor. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html# - */ - public close(fd: number, callback: (err: Error | null) => void) { - try { - this.closeSync(fd); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Close a file descriptor. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html# - */ - public closeSync(fd: number) { - const entry = this._file("close", fd); - this._openFiles.delete(entry.fd); - this._fsync(entry, /*metadata*/ true); - } - - /** - * Read from a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html - */ - public read(fd: number, buffer: Buffer, offset: number, length: number, position: number | undefined, callback: (err: Error | null, bytesRead: number | null, buffer: Buffer) => void) { - try { - process.nextTick(callback, null, this.readSync(fd, buffer, offset, length, position), buffer); - } - catch (e) { - process.nextTick(callback, e, null, buffer); - } - } - - /** - * Read from a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html - */ - public readSync(fd: number, buffer: Buffer, offset: number, length: number, position?: number | null) { - if (typeof position !== "number") position = -1; - if (!isFinite(offset) || !isFinite(length) || !isFinite(position)) throw new IOError("EINVAL", "read"); - if (offset < 0 || length < 0 || position < -1 || offset > buffer.byteLength - length) throw new IOError("EINVAL", "read"); - if (length === 0) return 0; - - const entry = this._file("read", fd, "file", constants.R_OK); - const parent = entry.parent; - const node = entry.node; - if (position !== -1) entry.offset = position; - if (!entry.buffer) entry.buffer = this._getBuffer(node); - const bytesRead = entry.buffer.copy(buffer, offset, entry.offset, entry.offset + length); - entry.offset += bytesRead; - return bytesRead; - } - - /** - * Read from a file. - */ - public readFile(path: string | number, callback: (error: Error | null, data: string | Buffer | null) => void): void; - /** - * Read from a file. - */ - public readFile(path: string | number, options: { encoding?: null, flag?: string | number } | null | undefined, callback: (error: Error | null, data: Buffer | null) => void): void; - /** - * Read from a file. - */ - public readFile(path: string | number, options: { encoding: string, flag?: string | number } | string, callback: (error: Error | null, data: string | null) => void): void; - /** - * Read from a file. - */ - public readFile(path: string | number, options: { encoding?: string | null, flag?: string | number } | string | null | undefined, callback: (error: Error | null, data: string | Buffer | null) => void): void; - public readFile(path: string | number, options: { encoding?: string | null, flag?: string | number } | string | null | undefined | typeof callback, callback?: ((error: Error | null, data: string | null) => void) | ((error: Error | null, data: Buffer | null) => void)) { - if (typeof options === "function") callback = options, options = undefined; - if (typeof callback !== "function") throw new IOError("EINVAL"); - try { - process.nextTick(callback, null, this.readFileSync(path, options)); - } - catch (e) { - process.nextTick(callback, e, null); - } - } - - /** - * Read from a file. - */ - public readFileSync(path: string | number, options?: { encoding?: null, flag?: string | number } | null): Buffer; - /** - * Read from a file. - */ - public readFileSync(path: string | number, options: { encoding: string, flag?: string | number } | string): string; - /** - * Read from a file. - */ - public readFileSync(path: string | number, options?: { encoding?: string | null, flag?: string | number } | string | null): string | Buffer; - public readFileSync(path: string | number, options: { encoding?: string | null, flag?: string | number } | string | null = {}) { - if (options === null || typeof options === "string") options = { encoding: options }; - const { encoding, flag = "r" } = options; - const fd = typeof path === "number" ? path : this.openSync(path, flag, /*mode*/ 0o666); - const size = this.fstatSync(fd).size; - let buffer = Buffer.allocUnsafe(size); - try { - let offset = 0; - let lastOffset: number | undefined; - while (lastOffset !== offset && offset < size) { - lastOffset = offset; - offset += this.readSync(fd, buffer, offset, size - offset, offset); - } - if (offset < size) buffer = buffer.slice(0, offset); - } - finally { - if (typeof path !== "number") { - this.closeSync(fd); - } - } - return encoding ? buffer.toString(encoding) : buffer; - } - - /** - * Write to a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - */ - public write(fd: number, buffer: Buffer, callback: (error: Error | null, bytesWritten: number | null, buffer: Buffer) => void): void; - /** - * Write to a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - */ - public write(fd: number, buffer: Buffer, offset: number | undefined, callback: (error: Error | null, bytesWritten: number | null, buffer: Buffer) => void): void; - /** - * Write to a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - */ - public write(fd: number, buffer: Buffer, offset: number | undefined, length: number | undefined, callback: (error: Error | null, bytesWritten: number | null, buffer: Buffer) => void): void; - /** - * Write to a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - */ - public write(fd: number, buffer: Buffer, offset: number | undefined, length: number | undefined, position: number | undefined, callback: (error: Error | null, bytesWritten: number | null, buffer: Buffer) => void): void; - /** - * Write to a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - */ - public write(fd: number, string: string, callback: (error: Error | null, bytesWritten: number | null, string: string) => void): void; - /** - * Write to a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - */ - public write(fd: number, string: string, position: number | null | undefined, callback: (error: Error | null, bytesWritten: number | null, string: string) => void): void; - /** - * Write to a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - */ - public write(fd: number, string: string, position: number | null | undefined, encoding: string | undefined, callback: (error: Error | null, bytesWritten: number | null, string: string) => void): void; - public write(fd: number, buffer: Buffer | string, offset?: number | null | typeof callback, length?: number | string | typeof callback, position?: number | typeof callback, callback?: ((error: Error | null, bytesWritten: number | null, buffer: Buffer) => void) | ((error: Error | null, bytesWritten: number | null, buffer: string) => void)) { - if (typeof offset === "function") length = offset, offset = undefined; - if (typeof length === "function") position = length, length = undefined; - if (typeof position === "function") callback = position, position = undefined; - if (typeof callback !== "function") throw new IOError("EINVAL"); - try { - if (Buffer.isBuffer(buffer)) { - if (typeof offset !== "number") offset = undefined; - if (typeof length !== "number") length = undefined; - process.nextTick(callback, null, this.writeSync(fd, buffer, offset, length, position), buffer); - } - else { - if (typeof length !== "string") length = undefined; - process.nextTick(callback, null, this.writeSync(fd, buffer, offset, length)); - } - } - catch (e) { - process.nextTick(callback, e, null, buffer); - } - } - - /** - * Write to a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - */ - public writeSync(fd: number, buffer: Buffer, offset?: number, length?: number, position?: number | null): number; - /** - * Write to a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - */ - public writeSync(fd: number, string: string, position?: number | null, encoding?: string): number; - public writeSync(fd: number, buffer: Buffer | string, offset?: number | null, length?: number | string, position?: number | null) { - if (Buffer.isBuffer(buffer)) { - if (typeof offset !== "number") offset = 0; - if (typeof length !== "number") length = buffer.byteLength - offset; - } - else { - buffer = Buffer.from(buffer, typeof length === "string" ? length : "utf8"); - position = offset; - offset = 0; - length = buffer.byteLength; - } - - if (typeof position !== "number") position = -1; - if (!isFinite(offset) || !isFinite(length) || !isFinite(position)) throw new IOError("EINVAL", "write"); - if (offset < 0 || length < 0 || position < -1 || offset > buffer.byteLength - length) throw new IOError("EINVAL", "write"); - if (length === 0) return; - - const entry = this._file("write", fd, "file", constants.W_OK); - const parent = entry.parent; - const node = entry.node; - if (position !== -1) entry.offset = position; - if (!entry.buffer) { - // if we haven't yet started writing, get a copy of the storage buffer - const buffer = this._getBuffer(node); - entry.buffer = Buffer.allocUnsafe(buffer.byteLength); - buffer.copy(entry.buffer, 0, 0, buffer.byteLength); - } - if (entry.offset >= entry.buffer.byteLength - length) { - // if we are writing to a point outside of the size of the buffer, resize it - entry.buffer = resizeBuffer(entry.buffer, entry.offset + length); - } - const bytesWritten = buffer.copy(entry.buffer, entry.offset, offset, offset + length); - entry.offset += bytesWritten; - if (entry.flags & constants.O_SYNC) { - this._fsync(entry, /*metadata*/ true); - } - return bytesWritten; - } - - /** - * Append to a file. - */ - public appendFile(path: string | number, data: string | Buffer, callback: (error: Error | null) => void): void; - /** - * Append to a file. - */ - public appendFile(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null | undefined, callback: (error: Error | null) => void): void; - public appendFile(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null | typeof callback | undefined, callback?: (error: Error | null) => void) { - if (typeof options === "function") callback = options, options = undefined; - if (typeof callback !== "function") throw new IOError("EINVAL"); - try { - this.appendFileSync(path, data, options); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Append to a file. - */ - public appendFileSync(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null = {}) { - if (options === null || typeof options === "string") options = { encoding: options }; - const { encoding, mode = 0o666, flag = "a" } = options; - this.writeFileSync(path, data, { encoding, mode, flag }); - } - - /** - * Write to a file. - */ - public writeFile(path: string | number, data: string | Buffer, callback: (error: Error | null) => void): void; - /** - * Write to a file. - */ - public writeFile(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null | undefined, callback: (error: Error | null) => void): void; - public writeFile(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null | typeof callback | undefined, callback?: (error: Error | null) => void) { - if (typeof options === "function") callback = options, options = undefined; - if (typeof callback !== "function") throw new IOError("EINVAL"); - try { - this.writeFileSync(path, data, options); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Write to a file. - */ - public writeFileSync(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null = {}) { - if (options === null || typeof options === "string") options = { encoding: options }; - const { encoding, mode = 0o666, flag = "w" } = options; - const flags = parseFlags(flag); - const fd = typeof path === "number" ? path : this.openSync(path, flags, mode); - const buffer = Buffer.isBuffer(data) ? data : Buffer.from("" + data, encoding || "utf8"); - try { - let offset = 0; - while (offset < buffer.byteLength) { - offset += this.writeSync(fd, buffer, offset, buffer.byteLength - offset, flags & constants.O_APPEND ? null : offset); - } - } - finally { - if (typeof path !== "number") { - this.closeSync(fd); - } - } - } - - /** - * Truncate a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html - */ - public truncate(path: string, callback: (error: Error | null) => void): void; - /** - * Truncate a file to a specified length. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html - */ - public truncate(path: string, length: number | undefined, callback: (error: Error | null) => void): void; - public truncate(path: string, length: number | Function | undefined, callback?: Function) { - if (typeof length === "function") callback = length, length = undefined; - if (typeof callback !== "function") throw new IOError("EINVAL"); - try { - this.truncateSync(path, length); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Truncate a file to a specified length. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html - */ - public truncateSync(path: string, length: number = 0) { - path = this._resolve(path); - this._truncate("truncate", this._find(path) || _throw(new IOError("ENOENT", "truncate", path)), length); - } - - /** - * Truncate a file. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html - */ - public ftruncate(fd: number, callback: (error: Error | null) => void): void; - /** - * Truncate a file to a specified length. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html - */ - public ftruncate(fd: number, length: number | undefined, callback: (error: Error | null) => void): void; - public ftruncate(fd: number, length: number | Function | undefined, callback?: Function) { - if (typeof length === "function") callback = length, length = undefined; - if (typeof callback !== "function") throw new IOError("EINVAL"); - try { - this.ftruncateSync(fd, length); - process.nextTick(callback, null); - } - catch (e) { - process.nextTick(callback, e); - } - } - - /** - * Truncate a file to a specified length. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html - */ - public ftruncateSync(fd: number, length: number = 0) { - this._truncate("ftruncate", this._file("ftruncate", fd, "file", constants.W_OK), length); - } - - private _truncate(syscall: string, entry: FileDescription, length: number, path?: string) { - if (!isFinite(length)) throw new IOError("EINVAL", syscall, path); - if (!isFileInode(entry.node)) throw new IOError("ENOENT", syscall, path); - if (this.isReadonly) throw new IOError("EROFS", syscall, path); - - if (this._getSize(entry.node) !== length) { - this._resize(entry.node, entry.node, length); - } - - entry.node.mtimeMs = this.time(); - this._notifySelf(entry.node, "change"); - } - - /** - * Makes a temporary directory. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdtemp.html - */ - public mkdtemp(template: string, callback: (error: Error | null, folder: string | null) => void) { - try { - process.nextTick(callback, null, this.mkdtempSync(template)); - } - catch (e) { - process.nextTick(callback, e, null); - } - } - - /** - * Makes a temporary directory. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdtemp.html - */ - public mkdtempSync(template: string) { - this.mkdirSync(this._mktemp("mkdtemp", template)); - } - - /** - * Makes a temporary file, returning a file descriptor. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html - */ - public mkstemp(template: string, callback: (error: Error | null, fd: number | null) => void) { - try { - process.nextTick(callback, null, this.mkstempSync(template)); - } - catch (e) { - process.nextTick(callback, e, null); - } - } - - /** - * Makes a temporary file, returning a file descriptor. - * - * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html - */ - public mkstempSync(template: string) { - return this.openSync(this._mktemp("mkstemp", template), constants.O_RDWR | constants.O_CREAT | constants.O_EXCL, constants.S_IRUSR | constants.S_IWUSR); - } - - private _mktemp(syscall: string, template: string) { - if (this.isReadonly) throw new IOError("EROFS", syscall, template); - - template = this._resolve(template); - if (vpath.hasTrailingSeparator(template)) throw new IOError("EINVAL", syscall, template); - - const basename = vpath.basename(template); - let count = 0; - for (let i = basename.length - 1; i >= 0; i--) { - if (basename.charAt(i) !== "X") break; - count++; - } - if (count < 6) throw new IOError("EINVAL", syscall, template); - - const { node: parent, path } = this._find(vpath.dirname(template)) || _throw(new IOError("ENOENT", syscall, template)); - if (!isDirectoryInode(parent)) throw new IOError("ENOTDIR", syscall, template); - if (!this._access(parent, constants.W_OK)) throw new IOError("EACCES", syscall, template); - - const parentLinks = this._getLinks(parent); - const prefix = basename.slice(0, basename.length - count); - while (true) { - let suffix = ""; - while (suffix.length < count) { - suffix += FileSystem._portableFilenameCharSet.charAt(Math.floor(Math.random() * FileSystem._portableFilenameCharSet.length)); - } - const name = prefix + suffix; - if (!parentLinks.has(name)) return vpath.combine(path, name); - } - } - - /** - * Watch a path for changes. - */ - public watch(path: string, callback?: (eventType: string, filename: string) => void): FSWatcher; - public watch(path: string, options?: { recursive?: boolean }, callback?: (eventType: string, filename: string) => void): FSWatcher; - public watch(path: string, options?: { recursive?: boolean } | typeof callback, callback?: (eventType: string, filename: string) => void) { - path = this._resolve(path); - if (typeof options === "function") callback = options, options = undefined; - if (typeof options === "undefined") options = {}; - - const watcher = new FSWatcher(this); - const realpath = this.realpathSync(path); - const recursive = this._noRecursiveWatchers ? false : !!options.recursive; - const watchers = this._lazy.watchers || (this._lazy.watchers = new core.SortedMap(this.stringComparer)); - - let pathWatchers = watchers.get(realpath); - if (!pathWatchers) { - pathWatchers = new FSWatcherEntrySet(realpath); - watchers.set(realpath, pathWatchers); - } - - pathWatchers.add(watcher["_entry"] = { watcher, path, recursive, container: pathWatchers }); - return typeof callback === "function" ? watcher.on("change", callback) : watcher; - } - - private _removeWatcher(entry: FSWatcherEntry) { - entry.watcher["_entry"] = undefined; - entry.container.delete(entry); - - if (entry.container.size === 0) { - const watchers = this._lazy.watchers; - if (!watchers) return; - - if (entry.container === watchers.get(entry.container.path)) { - watchers.delete(entry.container.path); - } - } - } - - private _removeWatchers(parent: DirectoryInode | undefined, node: Inode, name: string) { - if (!this._lazy.watchers) return; - const paths = parent ? this._getPaths(parent).map(path => vpath.combine(path, name)) : [name]; - for (const path of paths) { - const watchers = this._lazy.watchers.get(path); - if (!watchers) continue; - for (const watcher of watchers) { - this._removeWatcher(watcher); - } - } - } - - private _notifyChild(parent: DirectoryInode, eventType: "change" | "rename", name: string): void { - this._notify(parent, eventType, name, /*noExactMatch*/ false); - } - - private _notifySelf(node: Inode, eventType: "change" | "rename") { - this._notify(node, eventType, /*childPath*/ undefined, /*noExactMatch*/ false); - } - - private _notifyAncestors(node: Inode, eventType: "change" | "rename") { - this._notify(node, eventType, /*childPath*/ undefined, /*noExactMatch*/ true); - } - - private _notify(node: Inode, eventType: "change" | "rename", childPath: string | undefined, noExactMatch: boolean) { - if (typeof childPath === "boolean") noExactMatch = childPath, childPath = undefined; - if (!this._lazy.watchers) return; - for (const path of this._getPaths(node)) { - const fullPath = childPath ? vpath.combine(path, childPath) : path; - const dirname = vpath.dirname(fullPath); - for (const [watchedPath, watchers] of this._lazy.watchers) { - const exactMatch = !noExactMatch && vpath.equals(watchedPath, fullPath, this.ignoreCase); - const nonRecursiveMatch = watchers.nonRecursiveCount > 0 && vpath.equals(watchedPath, dirname, this.ignoreCase); - const recursiveMatch = watchers.recursiveCount > 0 && vpath.beneath(watchedPath, dirname, this.ignoreCase); - if (exactMatch || nonRecursiveMatch || recursiveMatch) { - for (const { watcher, recursive } of watchers) { - if (exactMatch || (recursive ? recursiveMatch : nonRecursiveMatch)) { - const entry = watcher["_entry"]; - const name = exactMatch ? vpath.basename(entry ? entry.path : fullPath) : vpath.relative(watchedPath, fullPath, this.ignoreCase); - watcher.emit("change", eventType, name); - } - } - } - } - } - } - - /** - * Watch a path for changes using polling. - */ - public watchFile(path: string, callback: (current: Stats, previous: Stats) => void): void; - /** - * Watch a path for changes using polling. - */ - public watchFile(path: string, options: { interval?: number } | undefined, callback: (current: Stats, previous: Stats) => void): void; - public watchFile(path: string, options: { interval?: number } | typeof callback | undefined, callback?: (current: Stats, previous: Stats) => void) { - if (typeof options === "function") callback = options, options = undefined; - if (typeof options === "undefined") options = {}; - if (typeof callback !== "function") throw new IOError("EINVAL"); - path = this._resolve(path); - const entry = this._find(path); - const { interval = 5007 } = options; - const watchedFiles = this._lazy.watchedFiles || (this._lazy.watchedFiles = new core.SortedMap(this.stringComparer)); - let watchedFileSet = watchedFiles.get(path); - if (!watchedFileSet) watchedFiles.set(path, watchedFileSet = new Set()); - const watchedFile: WatchedFile = { - path, - handle: this._timers.setInterval(() => this._onWatchInterval(watchedFile), interval), - previous: entry ? this._stat("stat", entry, path) : new Stats(), - listener: callback - }; - watchedFileSet.add(watchedFile); - if (!entry) { - callback(watchedFile.previous, watchedFile.previous); - } - } - - private _onWatchInterval(watchedFile: WatchedFile) { - if (watchedFile.handle === undefined) return; - const entry = this._find(watchedFile.path); - const previous = watchedFile.previous; - const current = entry ? this._stat("stat", entry, watchedFile.path) : new Stats(); - if (current.dev !== previous.dev || - current.ino !== previous.ino || - current.mode !== previous.mode || - current.nlink !== previous.nlink || - current.uid !== previous.uid || - current.gid !== previous.gid || - current.rdev !== previous.rdev || - current.size !== previous.size || - current.blksize !== previous.blksize || - current.blocks !== previous.blocks || - current.atimeMs !== previous.atimeMs || - current.mtimeMs !== previous.mtimeMs || - current.ctimeMs !== previous.ctimeMs || - current.birthtimeMs !== previous.birthtimeMs) { - watchedFile.previous = current; - const callback = watchedFile.listener; - callback(current, previous); - } - } - - /** - * Stop watching a path for changes. - */ - public unwatchFile(path: string, callback?: (current: Stats, previous: Stats) => void) { - path = this._resolve(path); - - const watchedFiles = this._lazy.watchedFiles; - if (!watchedFiles) return; - - const watchedFileSet = watchedFiles.get(path); - if (!watchedFileSet) return; - - for (const watchedFile of watchedFileSet) { - if (!callback || watchedFile.listener === callback) { - this._timers.clearInterval(watchedFile.handle); - watchedFileSet.delete(watchedFile); - watchedFile.handle = undefined; - } - } - - if (watchedFileSet.size === 0) { - watchedFiles.delete(path); - } - } - - public debugPrint(): void { - let result = ""; - const printLinks = (dirname: string | undefined, links: core.SortedMap) => { - for (const [name, node] of links) { - const path = dirname ? vpath.combine(dirname, name) : name; - const marker = vpath.compare(this._cwd, path, this.ignoreCase) === 0 ? "*" : " "; - if (result) result += "\n"; - result += marker; - if (isDirectoryInode(node)) { - result += vpath.addTrailingSeparator(path); - printLinks(path, this._getLinks(node)); - } - else if (isFileInode(node)) { - result += path; - } - else if (isSymlinkInode(node)) { - result += path + " -> " + node.symlink; - } - } - } - printLinks(/*dirname*/ undefined, this._getRootLinks()); - console.log(result); - } - - private _mknod(dev: number, type: typeof constants.S_IFREG, mode: number, uid?: number, gid?: number, umask?: number): FileInode; - private _mknod(dev: number, type: typeof constants.S_IFDIR, mode: number, uid?: number, gid?: number, umask?: number): DirectoryInode; - private _mknod(dev: number, type: typeof constants.S_IFLNK, mode: number, uid?: number, gid?: number, umask?: number): SymlinkInode; - private _mknod(dev: number, type: number, mode: number, uid = this.getuid(), gid = this.getgid(), umask = this.umask()) { - const timestamp = this.time(); - return { - dev, - ino: ++inoCount, - mode: (mode & ~constants.S_IFMT & ~umask & 0o7777) | (type & constants.S_IFMT), - uid, - gid, - atimeMs: timestamp, - mtimeMs: timestamp, - ctimeMs: timestamp, - birthtimeMs: timestamp, - nlink: 0, - incomingLinks: new Map>(), - }; - } - - private _addLink(parent: DirectoryInode | undefined, links: core.SortedMap, name: string, node: Inode) { - links.set(name, node); - node.nlink++; - - let set = node.incomingLinks.get(parent); - if (!set) node.incomingLinks.set(parent, set = new core.SortedSet(this.stringComparer)); - set.add(name); - } - - private _removeLink(parent: DirectoryInode | undefined, links: core.SortedMap, name: string, node: Inode) { - links.delete(name); - node.nlink--; - - const set = node.incomingLinks.get(parent); - if (set) { - set.delete(name); - if (set.size === 0) node.incomingLinks.delete(parent); - } - } - - private _replaceLink(oldParent: DirectoryInode, oldLinks: core.SortedMap, oldName: string, newParent: DirectoryInode, newLinks: core.SortedMap, newName: string, node: Inode) { - if (oldParent !== newParent) { - this._removeLink(oldParent, oldLinks, oldName, node); - this._addLink(newParent, newLinks, newName, node); - return; - } - - oldLinks.delete(oldName); - oldLinks.set(newName, node); - - const set = node.incomingLinks.get(oldParent); - if (set) { - set.delete(oldName); - set.add(newName); - } - } - - private _getRootLinks() { - if (!this._lazy.links) { - this._lazy.links = new core.SortedMap(this.stringComparer); - if (this._shadowRoot) { - this._copyShadowLinks(this._shadowRoot._getRootLinks(), this._lazy.links, /*parent*/ undefined); - } - this._lazy.links = this._lazy.links; - } - return this._lazy.links; - } - - private _getLinks(node: DirectoryInode | undefined) { - if (!node) return this._getRootLinks(); - if (!node.links) { - const links = new core.SortedMap(this.stringComparer); - const { source, resolver } = node; - if (source && resolver) { - node.source = undefined; - node.resolver = undefined; - for (const name of resolver.readdirSync(source)) { - const path = vpath.combine(source, name); - const stats = resolver.statSync(path); - switch (stats.mode & constants.S_IFMT) { - case constants.S_IFDIR: - const dir = this._mknod(node.dev, constants.S_IFDIR, 0o777); - dir.source = vpath.combine(source, name); - dir.resolver = resolver; - this._addLink(node, links, name, dir); - break; - case constants.S_IFREG: - const file = this._mknod(node.dev, constants.S_IFREG, 0o666); - file.source = vpath.combine(source, name); - file.resolver = resolver; - file.size = stats.size; - this._addLink(node, links, name, file); - break; - } - } - } - else if (this._shadowRoot && node.shadowRoot) { - this._copyShadowLinks(this._shadowRoot._getLinks(node.shadowRoot), links, node); - } - node.links = links; - } - return node.links; - } - - private _getShadow(root: DirectoryInode): DirectoryInode; - private _getShadow(root: Inode): Inode; - private _getShadow(root: Inode) { - const shadows = this._lazy.shadows || (this._lazy.shadows = new Map()); - - let shadow = shadows.get(root.ino); - if (!shadow) { - shadow = { - dev: root.dev, - ino: root.ino, - mode: root.mode, - uid: root.uid, - gid: root.gid, - atimeMs: root.atimeMs, - mtimeMs: root.mtimeMs, - ctimeMs: root.ctimeMs, - birthtimeMs: root.birthtimeMs, - nlink: root.nlink, - shadowRoot: root, - incomingLinks: new Map>(), - paths: root.paths - }; - - if (isSymlinkInode(root)) (shadow).symlink = root.symlink; - shadows.set(shadow.ino, shadow); - - for (const [rootParent, rootNames] of root.incomingLinks) { - shadow.incomingLinks.set(rootParent && this._getShadow(rootParent), new core.SortedSet(this.stringComparer, rootNames)); - } - } - - return shadow; - } - - private _copyShadowLinks(source: ReadonlyMap, target: core.SortedMap, parent: DirectoryInode | undefined) { - const shadows = this._lazy.shadows || (this._lazy.shadows = new Map()); - for (const [name, root] of source) { - target.set(name, this._getShadow(root)); - } - } - - private _invalidatePaths(node: Inode) { - node.paths = undefined; - if (isDirectoryInode(node)) { - for (const child of this._getLinks(node).values()) { - this._invalidatePaths(child); - } - } - } - - private _getSize(node: FileInode): number { - if (node.buffer) return node.buffer.byteLength; - if (node.size !== undefined) return node.size; - if (node.source && node.resolver) return node.size = node.resolver.statSync(node.source).size; - if (this._shadowRoot && node.shadowRoot) return node.size = this._shadowRoot._getSize(node.shadowRoot); - return 0; - } - - private _getBuffer(node: FileInode): Buffer { - if (!node.buffer) { - const { source, resolver } = node; - if (source && resolver) { - node.source = undefined; - node.resolver = undefined; - node.size = undefined; - node.buffer = resolver.readFileSync(source); - } - else if (this._shadowRoot && node.shadowRoot) { - node.buffer = this._shadowRoot._getBuffer(node.shadowRoot); - } - else { - node.buffer = Buffer.allocUnsafe(0); - } - } - return node.buffer; - } - - private _getPaths(node: Inode): ReadonlyArray { - if (!node.paths) { - const result: string[] = []; - for (const [parent, names] of node.incomingLinks) { - if (parent) { - for (const path of this._getPaths(parent)) { - for (const name of names) { - result.push(vpath.combine(path, name)); - } - } - } - else { - for (const name of names) { - result.push(name); - } - } - } - node.paths = result; - } - return node.paths; - } - - private _file(syscall: string, fd: number): OpenFileDescription; - private _file(syscall: string, fd: number, kind: "directory"): OpenFileDescription; - private _file(syscall: string, fd: number, kind: "file", mode?: number): OpenFileDescription; - private _file(syscall: string, fd: number, kind?: "file" | "directory", mode: number = constants.F_OK) { - const entry = this._openFiles.get(fd); - if (!entry) throw new IOError("EBADF", syscall); - if (kind === "file" && isDirectoryInode(entry.node)) throw new IOError("EISDIR", syscall); - if (kind === "file" && !isFileInode(entry.node)) throw new IOError("EBADF", syscall); - if (kind === "directory" && !isDirectoryInode(entry.node)) throw new IOError("EBADF", syscall); - if (mode & constants.W_OK && !isWritable(entry)) throw new IOError("EBADF", syscall); - if (mode & constants.R_OK && !isReadable(entry)) throw new IOError("EBADF", syscall); - return entry; - } - - private _find(path: string): FileDescription | undefined { - return this._walk(path, /*noFollow*/ false); - } - - private _lfind(path: string): FileDescription | undefined { - return this._walk(path, /*noFollow*/ true); - } - - // http://man7.org/linux/man-pages/man7/path_resolution.7.html - private _walk(path: string, noFollow: boolean): FileDescription | undefined { - let links: core.SortedMap = this._getRootLinks(); - let parent: DirectoryInode | undefined; - let components = vpath.parse(path); - let step = 0; - let depth = 0; - while (step < components.length) { - if (depth >= 40) throw new IOError("ELOOP", "scandir", vpath.format(components.slice(0, step))); - - const lastStep = step === components.length - 1; - const basename = components[step]; - const node = links.get(basename); - if (node === undefined) return undefined; - - if (isSymlinkInode(node) && !(noFollow && lastStep)) { - const dirname = vpath.format(components.slice(0, step)); - const symlink = vpath.resolve(dirname, node.symlink); - if (!vpath.isAbsolute(symlink)) throw new Error("Path not absolute"); - - links = this._getRootLinks(); - parent = undefined; - components = vpath.parse(symlink).concat(components.slice(step + 1)); - step = 0; - depth++; - continue; - } - - if (lastStep) { - const path = vpath.format(components); - if (!parent && isDirectoryInode(node)) parent = node; - if (!parent) throw new IOError("ENOENT", "scandir", path); - return { path, basename, parent, node }; - } - - if (isDirectoryInode(node)) { - const subpath = vpath.format(components.slice(0, step + 1)); - if (!this._access(node, constants.X_OK)) throw new IOError("EACCES", "scandir", path); - links = this._getLinks(node); - parent = node; - step++; - continue; - } - - throw new IOError("ENOTDIR", "scandir", vpath.format(components.slice(0, step + 1))); - } - - return undefined; - } - - private _resize(node: FileInode, entry: { buffer: Buffer | undefined }, size: number) { - if (!entry.buffer) { - entry.buffer = this._getBuffer(node); - } - const oldSize = entry.buffer.byteLength; - if (entry.buffer.byteLength !== size) { - const oldBuffer = entry.buffer; - entry.buffer = size < oldSize ? Buffer.allocUnsafe(size) : Buffer.alloc(size); - oldBuffer.copy(entry.buffer, 0, 0, Math.min(oldSize, size)); - } - } - - private _resolve(path: string) { - return this._cwd - ? vpath.resolve(this._cwd, vpath.validate(path, vpath.ValidationFlags.RelativeOrAbsolute)) - : vpath.validate(path, vpath.ValidationFlags.Absolute); - } - - private _applyFiles(files: FileSet, dirname: string) { - const deferred: [Symlink | Link | Mount, string][] = []; - this._applyFilesWorker(files, dirname, deferred); - for (const [entry, path] of deferred) { - this.mkdirpSync(vpath.dirname(path), 0o777); - this.pushd(vpath.dirname(path)); - if (entry instanceof Symlink) { - if (this.stringComparer(vpath.dirname(path), path) === 0) { - throw new TypeError("Roots cannot be symbolic links."); - } - this.symlinkSync(entry.symlink, path); - this._applyFileExtendedOptions(path, entry); - } - else if (entry instanceof Link) { - if (this.stringComparer(vpath.dirname(path), path) === 0) { - throw new TypeError("Roots cannot be hard links."); - } - this.linkSync(entry.path, path); - } - else { - this.mountSync(entry.source, path, entry.resolver); - this._applyFileExtendedOptions(path, entry); - } - this.popd(); - } - } - - private _applyFileExtendedOptions(path: string, entry: Directory | File | Symlink | Mount) { - const { uid = -1, gid = -1, mode, meta } = entry; - if (uid !== -1 || gid !== -1) this.chownSync(path, uid, gid); - if (mode !== undefined) this.chmodSync(path, mode); - if (meta !== undefined) { - const filemeta = this.filemeta(path); - for (const key of Object.keys(meta)) { - filemeta.set(key, meta[key]); - } - } - } - - private _applyFilesWorker(files: FileSet, dirname: string, deferred: [Symlink | Link | Mount, string][]) { - for (const key of Object.keys(files)) { - const value = this._normalizeFileMapEntry(files[key]); - const path = dirname ? vpath.resolve(dirname, key) : key; - vpath.validate(path, vpath.ValidationFlags.Absolute); - if (value === null || value === undefined) { - if (this.stringComparer(vpath.dirname(path), path) === 0) { - throw new TypeError("Roots cannot be deleted."); - } - this.rimrafSync(path); - } - else if (value instanceof File) { - if (this.stringComparer(vpath.dirname(path), path) === 0) { - throw new TypeError("Roots cannot be files."); - } - this.mkdirpSync(vpath.dirname(path), 0o777); - this.writeFileSync(path, value.data, value.encoding); - this._applyFileExtendedOptions(path, value); - } - else if (value instanceof Directory) { - this.mkdirpSync(path, 0o777); - this._applyFileExtendedOptions(path, value); - this._applyFilesWorker(value.files, path, deferred); - } - else { - deferred.push([value, path]); - } - } - } - - private _normalizeFileMapEntry(value: FileSet[string]) { - if (value === undefined || - value === null || - value instanceof Directory || - value instanceof File || - value instanceof Link || - value instanceof Symlink || - value instanceof Mount) { - return value; - } - return typeof value === "string" || Buffer.isBuffer(value) ? new File(value) : new Directory(value); - } -} - -export interface FileSystemOptions { - uid?: number; - gid?: number; - umask?: number; - time?: number | Date | (() => number | Date); - files?: FileSet; - timers?: FileSystemTimers; - cwd?: string; - meta?: Record; - noRecursiveWatchers?: boolean; -} - -export type Axis = "ancestors" | "ancestors-or-self" | "self" | "descendants-or-self" | "descendants"; - -export interface Traversal { - traverse?(path: string, stats: Stats): boolean; - accept?(path: string, stats: Stats): boolean; -} - -export interface FileSystemResolver { - statSync(path: string): { mode: number; size: number; }; - readdirSync(path: string): string[]; - readFileSync(path: string): Buffer; -} - -export interface FileSystemTimers { - setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; - clearInterval(handle: any): void; -} - -interface FileDescription { - path: string; - basename: string; - parent: DirectoryInode; - node: TInode; -} - -interface OpenFileDescription extends FileDescription { - fd: number; - flags: number; - offset: number; - written: boolean; - buffer: Buffer | undefined; -} - -function isReadable(file: OpenFileDescription) { - return (file.flags & constants.O_ACCMODE) !== constants.O_WRONLY; -} - -function isWritable(file: OpenFileDescription) { - return (file.flags & constants.O_ACCMODE) !== constants.O_RDONLY; -} - -function parseFlags(flags: string | number) { - if (typeof flags === "string") { - switch (flags) { - case "r": return constants.O_RDONLY; - case "r+": return constants.O_RDWR; - case "rs+": return constants.O_RDWR; - case "w": return constants.O_WRONLY | constants.O_TRUNC | constants.O_CREAT; - case "wx": return constants.O_WRONLY | constants.O_TRUNC | constants.O_CREAT | constants.O_EXCL; - case "w+": return constants.O_RDWR | constants.O_TRUNC | constants.O_CREAT; - case "wx+": return constants.O_RDWR | constants.O_TRUNC | constants.O_CREAT | constants.O_EXCL; - case "a": return constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT; - case "ax": return constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | constants.O_EXCL; - case "a+": return constants.O_RDWR | constants.O_APPEND | constants.O_CREAT; - case "ax+": return constants.O_RDWR | constants.O_APPEND | constants.O_CREAT | constants.O_EXCL; - default: throw new Error(`Unrecognized file open flag: ${flags}`); - } - } - return flags; -} - -function resizeBuffer(buffer: Buffer, size: number) { - if (buffer.byteLength === size) return buffer; - const newBuffer = Buffer.allocUnsafe(size); - newBuffer.fill(0, buffer.copy(newBuffer, 0, 0, size)); - return newBuffer; -} \ No newline at end of file diff --git a/scripts/vfs/src/index.ts b/scripts/vfs/src/index.ts deleted file mode 100644 index 9166b62459a..00000000000 --- a/scripts/vfs/src/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -export { - FileSystem, - FileSystemOptions, - FileSystemResolver, - FileSystemTimers, -} from "./fileSystem"; -export { - FileSet, - Directory, - DirectoryLike, - File, - FileLike, - Link, - Symlink, - Mount, -} from "./fileSet"; -export { Stats } from "./stats"; -export { FSWatcher } from "./watcher"; \ No newline at end of file diff --git a/scripts/vfs/src/inode.ts b/scripts/vfs/src/inode.ts deleted file mode 100644 index 41bc2b4daa7..00000000000 --- a/scripts/vfs/src/inode.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as core from "@typescript/vfs-core"; -import * as constants from "./constants"; -import { FileSystemResolver } from "./fileSystem"; - -// a generic POSIX inode -export type Inode = FileInode | DirectoryInode | SymlinkInode; - -export interface InodeBase { - // inode - dev: number; // device id - ino: number; // inode id - mode: number; // file mode - uid: number; // owner user id - gid: number; // owner group id - atimeMs: number; // access time - mtimeMs: number; // modified time - ctimeMs: number; // status change time - birthtimeMs: number; // creation time - nlink: number; // number of hard links - - // extra - shadowRoot: Inode | undefined; - incomingLinks: Map>; - paths?: ReadonlyArray; - meta?: core.Metadata; // metadata stored on the inode -} - -export interface FileInode extends InodeBase { - // file inode - size: number | undefined; - buffer: Buffer; - source: string | undefined; - resolver: FileSystemResolver | undefined; - shadowRoot: FileInode | undefined; -} - -export interface DirectoryInode extends InodeBase { - // directory inode - links: core.SortedMap | undefined; - source: string | undefined; - resolver: FileSystemResolver | undefined; - shadowRoot: DirectoryInode | undefined; -} - -export interface SymlinkInode extends InodeBase { - // symlink inode - symlink: string; - shadowRoot: SymlinkInode | undefined; -} - -export function isFileInode(node: Inode): node is FileInode { - return (node.mode & constants.S_IFMT) === constants.S_IFREG; -} - -export function isDirectoryInode(node: Inode): node is DirectoryInode { - return (node.mode & constants.S_IFMT) === constants.S_IFDIR; -} - -export function isSymlinkInode(node: Inode): node is SymlinkInode { - return (node.mode & constants.S_IFMT) === constants.S_IFLNK; -} diff --git a/scripts/vfs/src/stats.ts b/scripts/vfs/src/stats.ts deleted file mode 100644 index 4069a4fe2ed..00000000000 --- a/scripts/vfs/src/stats.ts +++ /dev/null @@ -1,53 +0,0 @@ -import * as constants from "./constants"; - -export class Stats { - public dev: number; - public ino: number; - public mode: number; - public nlink: number; - public uid: number; - public gid: number; - public rdev: number; - public size: number; - public blksize: number; - public blocks: number; - public atimeMs: number; - public mtimeMs: number; - public ctimeMs: number; - public birthtimeMs: number; - public atime: Date; - public mtime: Date; - public ctime: Date; - public birthtime: Date; - - constructor(); - constructor(dev: number, ino: number, mode: number, nlink: number, uid: number, gid: number, rdev: number, size: number, blksize: number, blocks: number, atimeMs: number, mtimeMs: number, ctimeMs: number, birthtimeMs: number); - constructor(dev: number = 0, ino: number = 0, mode: number = 0, nlink: number = 0, uid: number = 0, gid: number = 0, rdev: number = 0, size: number = 0, blksize: number = 0, blocks: number = 0, atimeMs: number = 0, mtimeMs: number = 0, ctimeMs: number = 0, birthtimeMs: number = 0) { - this.dev = dev; - this.ino = ino; - this.mode = mode; - this.nlink = nlink; - this.uid = uid; - this.gid = gid; - this.rdev = rdev; - this.size = size; - this.blksize = blksize; - this.blocks = blocks; - this.atimeMs = atimeMs; - this.mtimeMs = mtimeMs; - this.ctimeMs = ctimeMs; - this.birthtimeMs = birthtimeMs; - this.atime = new Date(this.atimeMs); - this.mtime = new Date(this.mtimeMs); - this.ctime = new Date(this.ctimeMs); - this.birthtime = new Date(this.birthtimeMs); - } - - public isFile() { return (this.mode & constants.S_IFMT) === constants.S_IFREG; } - public isDirectory() { return (this.mode & constants.S_IFMT) === constants.S_IFDIR; } - public isSymbolicLink() { return (this.mode & constants.S_IFMT) === constants.S_IFLNK; } - public isBlockDevice() { return (this.mode & constants.S_IFMT) === constants.S_IFBLK; } - public isCharacterDevice() { return (this.mode & constants.S_IFMT) === constants.S_IFCHR; } - public isFIFO() { return (this.mode & constants.S_IFMT) === constants.S_IFIFO; } - public isSocket() { return (this.mode & constants.S_IFMT) === constants.S_IFSOCK; } -} diff --git a/scripts/vfs/src/tests/fileSystemTests.ts b/scripts/vfs/src/tests/fileSystemTests.ts deleted file mode 100644 index 2d570edc996..00000000000 --- a/scripts/vfs/src/tests/fileSystemTests.ts +++ /dev/null @@ -1,1420 +0,0 @@ -import * as vpath from "@typescript/vfs-path"; -import * as constants from "../constants"; -import { createSpy } from "./utils"; -import { FileSystem } from "../fileSystem"; -import { Link, Symlink } from "../fileSet"; -import { assert } from "chai"; - -describe("fileSystem", () => { - function describeFileSystem(title: string, ignoreCase: boolean, root: string) { - describe(title, () => { - describe("accessSync", () => { - it("ok (user owner)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.umask(0o000); - fs.chmodSync("file", 0o700); - fs.chownSync("file", 1, 2); - fs.setuid(1); - fs.setgid(3); - fs.accessSync("file", constants.R_OK | constants.W_OK | constants.X_OK); - }); - - it("ok (group owner)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.umask(0o000); - fs.chmodSync("file", 0o070); - fs.chownSync("file", 1, 2); - fs.setuid(3); - fs.setgid(2); - fs.accessSync("file", constants.R_OK | constants.W_OK | constants.X_OK); - }); - - it("ok (other)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.umask(0o000); - fs.chmodSync("file", 0o007); - fs.setuid(3); - fs.setgid(4); - fs.accessSync("file", constants.R_OK | constants.W_OK | constants.X_OK); - }); - - it("ok (none)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.umask(0o000); - fs.chmodSync("file", 0o000); - fs.setuid(1); - fs.setgid(2); - fs.accessSync("file", constants.F_OK); - }); - - it("fail (self, EACCES)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.umask(0o000); - fs.chmodSync("file", 0o000); - fs.setuid(1); - fs.setgid(2); - assert.throws(() => { fs.accessSync("file", constants.R_OK); }, /EACCES/); - }); - - it("fail (parent, EACCES)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir/file": "" } } }); - fs.umask(0o000); - fs.chmodSync("dir", 0o666); - fs.setuid(1); - fs.setgid(2); - assert.throws(() => { fs.accessSync("dir/file", constants.R_OK); }, /EACCES/); - }); - }); - - describe("chmodSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.chmodSync("file", 0o654); - assert.strictEqual(fs.statSync("file").mode & 0o777, 0o654); - }); - - it("fail (EPERM)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.setuid(1); - assert.throws(() => fs.chmodSync("file", 0o654), /EPERM/); - }); - - it("fail (EROFS)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.makeReadonly(); - assert.throws(() => fs.chmodSync("file", 0o654), /EROFS/); - }); - }); - - describe("fchmodSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - const fd = fs.openSync("file", constants.O_RDONLY); - fs.fchmodSync(fd, 0o654); - fs.closeSync(fd); - assert.strictEqual(fs.statSync("file").mode & 0o777, 0o654); - }); - - it("fail (EPERM)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.setuid(1); - const fd = fs.openSync("file", constants.O_RDONLY); - assert.throws(() => fs.fchmodSync(fd, 0o654), /EPERM/); - }); - - it("fail (EROFS)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.makeReadonly(); - const fd = fs.openSync("file", constants.O_RDONLY); - assert.throws(() => fs.fchmodSync(fd, 0o654), /EROFS/); - }); - }); - - describe("chownSync", () => { - it("ok (uid)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.chownSync("file", 1, -1); - const stats = fs.statSync("file"); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 0); - }); - - it("ok (gid)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.chownSync("file", 1, -1); - fs.setuid(1); - fs.chownSync("file", -1, 2); - const stats = fs.statSync("file"); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - }); - - it("fail (uid, EPERM)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.setuid(1); - assert.throws(() => fs.chownSync("file", 1, -1), /EPERM/); - }); - - it("fail (gid, EPERM)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.setuid(1); - assert.throws(() => fs.chownSync("file", -1, 1), /EPERM/); - }); - - it("fail (EROFS)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.makeReadonly(); - assert.throws(() => fs.chownSync("file", 1, 2), /EROFS/); - }); - }); - - describe("fchownSync", () => { - it("ok (uid)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - const fd = fs.openSync("file", constants.O_RDONLY); - fs.fchownSync(fd, 1, -1); - fs.closeSync(fd); - const stats = fs.statSync("file"); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 0); - }); - - it("ok (gid)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - const fd = fs.openSync("file", constants.O_RDONLY); - fs.chownSync("file", 1, -1); - fs.setuid(1); - fs.chownSync("file", -1, 2); - const stats = fs.statSync("file"); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - }); - - it("fail (uid, EPERM)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.setuid(1); - const fd = fs.openSync("file", constants.O_RDONLY); - assert.throws(() => fs.fchownSync(fd, 1, 2), /EPERM/); - }); - - it("fail (gid, EPERM)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.setuid(1); - const fd = fs.openSync("file", constants.O_RDONLY); - assert.throws(() => fs.fchownSync(fd, -1, 2), /EPERM/); - }); - - it("fail (EROFS)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.makeReadonly(); - const fd = fs.openSync("file", constants.O_RDONLY); - assert.throws(() => fs.fchownSync(fd, 1, 2), /EROFS/); - }); - }); - - describe("utimesSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "" } } }); - fs.time(3); - fs.utimesSync("file", 1, 2); - const stats = fs.statSync("file"); - assert.strictEqual(stats.atimeMs, 1); - assert.strictEqual(stats.mtimeMs, 2); - assert.strictEqual(stats.ctimeMs, 3); - }); - }); - - describe("futimesSync", () => { - it("ok", async () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "" } } }); - const fd = fs.openSync("file", constants.O_RDONLY); - fs.time(3); - fs.futimesSync(fd, 1, 2); - fs.closeSync(fd); - const stats2 = fs.statSync("file"); - assert.strictEqual(stats2.atimeMs, 1); - assert.strictEqual(stats2.mtimeMs, 2); - assert.strictEqual(stats2.ctimeMs, 3); - }); - }); - - describe("statSync", () => { - it("ok (file)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "testing" } } }); - fs.chmodSync("file", 0o654); - fs.chownSync("file", 1, 2); - fs.time(1); - const stats = fs.statSync("file"); - assert.isTrue(stats.isFile()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 7); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 0); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (file, hardlink)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "testing" } } }); - fs.chmodSync("file", 0o654); - fs.chownSync("file", 1, 2); - fs.time(1); - fs.linkSync("file", "link"); - const stats = fs.statSync("link"); - assert.isTrue(stats.isFile()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 2); - assert.strictEqual(stats.size, 7); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 1); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (file, symlink)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "testing" } } }); - fs.chmodSync("file", 0o654); - fs.chownSync("file", 1, 2); - fs.time(1); - fs.symlinkSync("file", "symlink"); - fs.lchmodSync("symlink", 0o456); - fs.lchownSync("symlink", 3, 4); - const stats = fs.statSync("symlink"); - assert.isTrue(stats.isFile()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 7); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 0); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (directory)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "dir": {} } } }); - fs.chmodSync("dir", 0o654); - fs.chownSync("dir", 1, 2); - fs.time(1); - const stats = fs.statSync("dir"); - assert.isTrue(stats.isDirectory()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 0); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 0); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (directory, symlink)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "dir": {} } } }); - fs.chmodSync("dir", 0o654); - fs.chownSync("dir", 1, 2); - fs.time(1); - fs.symlinkSync("dir", "symlink"); - fs.lchmodSync("symlink", 0o456); - fs.lchownSync("symlink", 3, 4); - const stats = fs.statSync("symlink"); - assert.isTrue(stats.isDirectory()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 0); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 0); - assert.strictEqual(stats.birthtimeMs, 0); - }); - }); - - describe("lstatSync", () => { - it("ok (file)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "testing" } } }); - fs.chmodSync("file", 0o654); - fs.chownSync("file", 1, 2); - fs.time(1); - const stats = fs.lstatSync("file"); - assert.isTrue(stats.isFile()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 7); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 0); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (file, hardlink)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "testing" } } }); - fs.chmodSync("file", 0o654); - fs.chownSync("file", 1, 2); - fs.time(1); - fs.linkSync("file", "link"); - const stats = fs.lstatSync("link"); - assert.isTrue(stats.isFile()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 2); - assert.strictEqual(stats.size, 7); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 1); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (file, symlink)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "testing" } } }); - fs.chmodSync("file", 0o654); - fs.chownSync("file", 1, 2); - fs.time(1); - fs.symlinkSync("file", "symlink"); - fs.lchmodSync("symlink", 0o456); - fs.lchownSync("symlink", 3, 4); - const stats = fs.lstatSync("symlink"); - assert.isTrue(stats.isSymbolicLink()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o456); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 4); - assert.strictEqual(stats.uid, 3); - assert.strictEqual(stats.gid, 4); - assert.strictEqual(stats.atimeMs, 1); - assert.strictEqual(stats.mtimeMs, 1); - assert.strictEqual(stats.ctimeMs, 1); - assert.strictEqual(stats.birthtimeMs, 1); - }); - - it("ok (directory)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "dir": {} } } }); - fs.chmodSync("dir", 0o654); - fs.chownSync("dir", 1, 2); - fs.time(1); - const stats = fs.lstatSync("dir"); - assert.isTrue(stats.isDirectory()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 0); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 0); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (directory, symlink)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "dir": {} } } }); - fs.chmodSync("dir", 0o654); - fs.chownSync("dir", 1, 2); - fs.time(1); - fs.symlinkSync("dir", "symlink"); - fs.lchmodSync("symlink", 0o456); - fs.lchownSync("symlink", 3, 4); - const stats = fs.lstatSync("symlink"); - assert.isTrue(stats.isSymbolicLink()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o456); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 3); - assert.strictEqual(stats.uid, 3); - assert.strictEqual(stats.gid, 4); - assert.strictEqual(stats.atimeMs, 1); - assert.strictEqual(stats.mtimeMs, 1); - assert.strictEqual(stats.ctimeMs, 1); - assert.strictEqual(stats.birthtimeMs, 1); - }); - }); - - describe("fstatSync", () => { - it("ok (file)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "testing" } } }); - fs.chmodSync("file", 0o654); - fs.chownSync("file", 1, 2); - fs.time(1); - const fd = fs.openSync("file", constants.O_RDONLY); - const stats = fs.fstatSync(fd); - fs.closeSync(fd); - assert.isTrue(stats.isFile()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 7); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 0); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (file hardlink)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "testing" } } }); - fs.chmodSync("file", 0o654); - fs.chownSync("file", 1, 2); - fs.time(1); - fs.linkSync("file", "link"); - const fd = fs.openSync("link", constants.O_RDONLY); - const stats = fs.fstatSync(fd); - fs.closeSync(fd); - assert.isTrue(stats.isFile()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 2); - assert.strictEqual(stats.size, 7); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 1); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (file symlink)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "file": "testing" } } }); - fs.chmodSync("file", 0o654); - fs.chownSync("file", 1, 2); - fs.time(1); - fs.symlinkSync("file", "symlink"); - fs.lchmodSync("symlink", 0o456); - fs.lchownSync("symlink", 3, 4); - const fd = fs.openSync("symlink", constants.O_RDONLY); - const stats = fs.fstatSync(fd); - fs.closeSync(fd); - assert.isTrue(stats.isFile()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 7); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 0); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (directory)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "dir": {} } } }); - fs.chmodSync("dir", 0o654); - fs.chownSync("dir", 1, 2); - fs.time(1); - const fd = fs.openSync("dir", constants.O_RDONLY | constants.O_DIRECTORY); - const stats = fs.fstatSync(fd); - fs.closeSync(fd); - assert.isTrue(stats.isDirectory()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 0); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 0); - assert.strictEqual(stats.birthtimeMs, 0); - }); - - it("ok (directory symlink)", () => { - const fs = new FileSystem(ignoreCase, { time: 0, files: { [root]: { "dir": {} } } }); - fs.chmodSync("dir", 0o654); - fs.chownSync("dir", 1, 2); - fs.time(1); - fs.symlinkSync("dir", "symlink"); - fs.lchmodSync("symlink", 0o456); - fs.lchownSync("symlink", 3, 4); - const fd = fs.openSync("symlink", constants.O_RDONLY | constants.O_DIRECTORY); - const stats = fs.fstatSync(fd); - fs.closeSync(fd); - assert.isTrue(stats.isDirectory()); - assert.strictEqual(stats.mode & ~constants.S_IFMT, 0o654); - assert.strictEqual(stats.nlink, 1); - assert.strictEqual(stats.size, 0); - assert.strictEqual(stats.uid, 1); - assert.strictEqual(stats.gid, 2); - assert.strictEqual(stats.atimeMs, 0); - assert.strictEqual(stats.mtimeMs, 0); - assert.strictEqual(stats.ctimeMs, 0); - assert.strictEqual(stats.birthtimeMs, 0); - }); - }); - - describe("readdirSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "a": {} } } }); - const actual = fs.readdirSync(root); - assert.deepEqual(actual, ["a"]); - }); - }); - - describe("mkdirSync", () => { - it("ok", () => { - const path = vpath.combine(root, "a"); - const fs = new FileSystem(ignoreCase, { files: { [root]: {} } }); - fs.mkdirSync(path); - assert.isTrue(fs.statSync(path).isDirectory()); - }); - }); - - describe("rmdirSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "a": {} } } }); - fs.rmdirSync(vpath.combine(root, "a")); - const actual = fs.readdirSync(root); - assert.deepEqual(actual.length, 0); - }); - }); - - describe("linkSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.linkSync("file", "link"); - const stats1 = fs.statSync("file"); - const stats2 = fs.statSync("link"); - assert.deepEqual(stats2, stats1); - assert.strictEqual(stats1.nlink, 2); - }); - }); - - describe("unlinkSync", () => { - it("ok (nlink = 1)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.unlinkSync("file"); - assert.throws(() => fs.statSync("file"), /ENOENT/); - }); - - it("ok (nlink > 1)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.linkSync("file", "link"); - fs.unlinkSync("file"); - assert.throws(() => fs.statSync("file"), /ENOENT/); - const stats = fs.statSync("link"); - assert.strictEqual(stats.nlink, 1); - }); - }); - - describe("renameSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - const stats1 = fs.statSync("file"); - fs.renameSync("file", "renamed"); - assert.throws(() => fs.statSync("file"), /ENOENT/); - const stats2 = fs.statSync("renamed"); - assert.strictEqual(stats2.ino, stats1.ino); - }); - }); - - describe("truncateSync", () => { - it("length 0", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.truncateSync("file", 0); - assert.strictEqual(fs.readFileSync("file", "utf8"), ""); - }); - - it("length non-zero and less than size", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.truncateSync("file", 2); - assert.strictEqual(fs.readFileSync("file", "utf8"), "te"); - }); - - it("length greater than size", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.truncateSync("file", 5); - assert.strictEqual(fs.readFileSync("file", "utf8"), "test\u0000"); - }); - }); - - describe("ftruncateSync", () => { - it("length 0", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - const fd = fs.openSync("file", constants.O_WRONLY); - fs.ftruncateSync(fd, 0); - fs.closeSync(fd); - assert.strictEqual(fs.readFileSync("file", "utf8"), ""); - }); - - it("length non-zero and less than size", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - const fd = fs.openSync("file", constants.O_WRONLY); - fs.ftruncateSync(fd, 2); - fs.closeSync(fd); - assert.strictEqual(fs.readFileSync("file", "utf8"), "te"); - }); - - it("length greater than size", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - const fd = fs.openSync("file", constants.O_WRONLY); - fs.ftruncateSync(fd, 5); - fs.closeSync(fd); - assert.strictEqual(fs.readFileSync("file", "utf8"), "test\u0000"); - }); - }); - - describe("symlinkSync", () => { - it("ok (absolute target)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.symlinkSync(vpath.combine(root, "file"), "symlink"); - assert.strictEqual(fs.readFileSync("symlink", "utf8"), "test"); - }); - - it("ok (relative target)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.symlinkSync("file", "symlink"); - assert.strictEqual(fs.readFileSync("symlink", "utf8"), "test"); - }); - - it("ok (indirect target)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.symlinkSync("file", "symlink1"); - fs.symlinkSync("symlink1", "symlink2"); - assert.strictEqual(fs.readFileSync("symlink2", "utf8"), "test"); - }); - }); - - describe("readlinkSync", () => { - it("ok (absolute target)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.symlinkSync(vpath.combine(root, "file"), "symlink"); - assert.strictEqual(fs.readlinkSync("symlink"), vpath.combine(root, "file")); - }); - - it("ok (relative target)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.symlinkSync("file", "symlink"); - assert.strictEqual(fs.readlinkSync("symlink"), "file"); - }); - }); - - describe("realpathSync", () => { - it("ok (absolute target)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.symlinkSync(vpath.combine(root, "file"), "symlink"); - assert.strictEqual(fs.realpathSync("symlink"), vpath.combine(root, "file")); - }); - - it("ok (relative target)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.symlinkSync("file", "symlink"); - assert.strictEqual(fs.realpathSync("symlink"), vpath.combine(root, "file")); - }); - - it("ok (indirect target)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.symlinkSync("file", "symlink1"); - fs.symlinkSync("symlink1", "symlink2"); - assert.strictEqual(fs.realpathSync("symlink2"), vpath.combine(root, "file")); - }); - }); - - describe("openSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - const fd = fs.openSync("file", "r"); - assert.isTrue(isFinite(fd)); - assert.isTrue(fd !== 0); - }); - }); - - describe("closeSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - const fd = fs.openSync("file", "r"); - fs.closeSync(fd); - assert.throws(() => fs.fstatSync(fd), /EBADF/); - }); - }); - - describe("readSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - const fd = fs.openSync("file", "r"); - const buffer = Buffer.alloc(4); - const bytesRead = fs.readSync(fd, buffer, 0, buffer.byteLength); - assert.strictEqual(bytesRead, buffer.byteLength); - assert.strictEqual(buffer.toString("utf8"), "test"); - }); - }); - - describe("readFileSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - assert.strictEqual(fs.readFileSync("file", "utf8"), "test"); - }); - }); - - describe("writeSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - const fd = fs.openSync("file", "w"); - const buffer = Buffer.from("replacement", "utf8"); - const bytesWritten = fs.writeSync(fd, buffer, 0, buffer.byteLength); - fs.closeSync(fd); - assert.strictEqual(bytesWritten, buffer.byteLength); - assert.strictEqual(fs.readFileSync("file", "utf8"), "replacement"); - }); - }); - - describe("writeFileSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.writeFileSync("file", "replacement", "utf8"); - assert.strictEqual(fs.readFileSync("file", "utf8"), "replacement"); - }); - }); - - describe("appendFileSync", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "test" } } }); - fs.appendFileSync("file", "addition", "utf8"); - assert.strictEqual(fs.readFileSync("file", "utf8"), "testaddition"); - }); - }); - - describe("mount", () => { - it("ok", () => { - const other = new FileSystem(/*ignoreCase*/ false, { files: { - "/": { - "subdir": {}, - "file": "" - } - } }); - - const fs = new FileSystem(ignoreCase, { files: { [root]: {} } }); - fs.mountSync("/", vpath.combine(root, "dir"), other) - - const names = fs.readdirSync(vpath.combine(root, "dir")); - assert.deepEqual(names, ["file", "subdir"]); - }); - }); - - describe("shadow", () => { - it("ok", () => { - const rootFs = new FileSystem(ignoreCase, { files: { [root]: { } } }).makeReadonly(); - const shadowFs = rootFs.shadow(); - assert.strictEqual(shadowFs.shadowRoot, rootFs); - }); - - it("shadow reads from root", () => { - const rootFs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {}, "file": "test" } } }).makeReadonly(); - const shadowFs = rootFs.shadow(); - assert.deepEqual(shadowFs.readdirSync(root), ["dir", "file"]); - assert.strictEqual(shadowFs.readFileSync("file", "utf8"), "test"); - }); - - it("shadow write does not affect root", () => { - const rootFs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {}, "file": "test" } } }).makeReadonly(); - const shadowFs = rootFs.shadow(); - shadowFs.writeFileSync("file", "replacement", "utf8"); - assert.strictEqual(rootFs.readFileSync("file", "utf8"), "test"); - }); - }); - - describe("meta", () => { - it("ok", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - fs.filemeta("file").set("testKey", "testValue"); - assert.strictEqual(fs.filemeta("file").get("testKey"), "testValue"); - }); - - it("shadow inherits from root", () => { - const rootFs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - rootFs.filemeta("file").set("testKey", "testValue"); - rootFs.makeReadonly(); - const shadowFs = rootFs.shadow(); - assert.strictEqual(shadowFs.filemeta("file").get("testKey"), "testValue"); - }); - - it("shadow inherits from root with mutation", () => { - const rootFs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - rootFs.makeReadonly(); - const shadowFs = rootFs.shadow(); - rootFs.filemeta("file").set("testKey", "testValue"); - assert.strictEqual(shadowFs.filemeta("file").get("testKey"), "testValue"); - }); - - it("shadow does not mutate root", () => { - const rootFs = new FileSystem(ignoreCase, { files: { [root]: { "file": "" } } }); - rootFs.filemeta("file").set("testKey", "testValue"); - rootFs.makeReadonly(); - const shadowFs = rootFs.shadow(); - shadowFs.filemeta("file").set("testKey", "newValue"); - assert.strictEqual(rootFs.filemeta("file").get("testKey"), "testValue"); - }); - }); - - describe("watch", () => { - describe("directory (non-recursive)", () => { - it("open() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {} } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.openSync("dir/file", "w+"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"] - ]); - }); - it("open() + write() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {} } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - const fd = fs.openSync("dir/file", "w+"); - fs.writeSync(fd, Buffer.from("test"), 0, 4); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"] - ]); - }); - it("open() + write() + close() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {} } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - const fd = fs.openSync("dir/file", "w+"); - fs.writeSync(fd, Buffer.from("test"), 0, 4); - fs.closeSync(fd); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"], - ["change", "file"] - ]); - }); - it("open() + close() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {} } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - const fd = fs.openSync("dir/file", "w+"); - fs.closeSync(fd); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"], - ["change", "file"] - ]); - }); - it("writeFile() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {} } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.writeFileSync("dir/file", "test"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"], - ["change", "file"] - ]); - }); - it("writeFile() replace file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "" } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.writeFileSync("dir/file", "test"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["change", "file"] - ]); - }); - it("truncate() file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "test" } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.truncateSync(vpath.combine(root, "dir/file")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["change", "file"] - ]); - }); - it("rename() file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "test" } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.renameSync(vpath.combine(root, "dir/file"), vpath.combine(root, "dir/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"], - ["rename", "file1"] - ]); - }); - it("link() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "test" } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.linkSync(vpath.combine(root, "dir/file"), vpath.combine(root, "dir/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file1"] - ]); - }); - it("symlink() file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "test" } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.symlinkSync(vpath.combine(root, "dir/file"), vpath.combine(root, "dir/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file1"] - ]); - }); - it("unlink() file (single link)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "" } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.unlinkSync(vpath.combine(root, "dir/file")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"] - ]); - }); - it("unlink() file (multiple links)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "", "file1": new Link("file") } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.unlinkSync(vpath.combine(root, "dir/file")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"] - ]); - }); - it("mkdir() new subdirectory", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.mkdirSync(vpath.combine(root, "dir/subdir")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "subdir"] - ]); - }); - it("rmdir() subdirectory", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "subdir": {} } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), callbackSpy.proxy); - fs.rmdirSync(vpath.combine(root, "dir/subdir")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "subdir"] - ]); - }); - }); - describe("directory (recursive)", () => { - it("open() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": {} } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.openSync("dir/sub1/file1", "w+"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file1"], - ["change", "sub1"] - ]); - }); - it("open() + write() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": {} } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - const fd = fs.openSync("dir/sub1/file1", "w+"); - fs.writeSync(fd, Buffer.from("test"), 0, 4); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file1"], - ["change", "sub1"] - ]); - }); - it("open() + write() + close() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": {} } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - const fd = fs.openSync("dir/sub1/file1", "w+"); - fs.writeSync(fd, Buffer.from("test"), 0, 4); - fs.closeSync(fd); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file1"], - ["change", "sub1/file1"], - ["change", "sub1"] - ]); - }); - it("open() + close() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": {} } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - const fd = fs.openSync("dir/sub1/file1", "w+"); - fs.closeSync(fd); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file1"], - ["change", "sub1"], - ["change", "sub1/file1"] - ]); - }); - it("writeFile() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": {} } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.writeFileSync("dir/sub1/file1", "test"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file1"], - ["change", "sub1/file1"], - ["change", "sub1"] - ]); - }); - it("writeFile() replace file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": { "file1": "" } } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.writeFileSync("dir/sub1/file1", "test"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["change", "sub1/file1"] - ]); - }); - it("truncate() file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": { "file1": "test" } } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.truncateSync(vpath.combine(root, "dir/sub1/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["change", "sub1/file1"] - ]); - }); - it("rename() file (same directory)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": { "file1": "test" } } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.renameSync(vpath.combine(root, "dir/sub1/file1"), vpath.combine(root, "dir/sub1/file2")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file1"], - ["rename", "sub1/file2"], - ["change", "sub1"] - ]); - }); - it("rename() file (different directory)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": { "file1": "test" }, "sub2": {} } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.renameSync(vpath.combine(root, "dir/sub1/file1"), vpath.combine(root, "dir/sub2/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file1"], - ["rename", "sub2/file1"], - ["change", "sub1"], - ["change", "sub2"] - ]); - }); - it("link() file (same directory)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": { "file1": "test" } } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.linkSync(vpath.combine(root, "dir/sub1/file1"), vpath.combine(root, "dir/sub1/file2")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file2"], - ["change", "sub1"] - ]); - }); - it("link() file (different directory)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": { "file1": "test" }, "sub2": {} } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.linkSync(vpath.combine(root, "dir/sub1/file1"), vpath.combine(root, "dir/sub2/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub2/file1"], - ["change", "sub2"] - ]); - }); - it("symlink() file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": { "file": "test" } } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.symlinkSync(vpath.combine(root, "dir/sub1/file"), vpath.combine(root, "dir/sub1/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file1"], - ["change", "sub1"] - ]); - }); - it("unlink() file (single link)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": { "file1": "" } } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.unlinkSync(vpath.combine(root, "dir/sub1/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file1"], - ["change", "sub1"] - ]); - }); - it("unlink() file (multiple links)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": { "file1": "", "file2": new Link("file1") } } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.unlinkSync(vpath.combine(root, "dir/sub1/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/file1"], - ["change", "sub1"] - ]); - }); - it("mkdir() new subdirectory", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": {} } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.mkdirSync(vpath.combine(root, "dir/sub1/sub2")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/sub2"], - ["change", "sub1"] - ]); - }); - it("rmdir() subdirectory", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "sub1": { "sub2": {} } } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir"), { recursive: true }, callbackSpy.proxy); - fs.rmdirSync(vpath.combine(root, "dir/sub1/sub2")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "sub1/sub2"], - ["change", "sub1"] - ]); - }); - }); - describe("file", () => { - it("writeFile() replace", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file1": "" } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir/file1"), callbackSpy.proxy); - fs.writeFileSync("dir/file1", "test"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["change", "file1"] - ]); - }); - it("unlink() (single link)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file1": "" } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir/file1"), callbackSpy.proxy); - fs.unlinkSync(vpath.combine(root, "dir/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file1"] - ]); - }); - }); - describe("symlink (directory, non-recursive)", () => { - it("open() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {}, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.openSync("dir/file", "w+"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"] - ]); - }); - it("open() + write() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {}, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - const fd = fs.openSync("dir/file", "w+"); - fs.writeSync(fd, Buffer.from("test"), 0, 4); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"] - ]); - }); - it("open() + write() + close() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {}, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - const fd = fs.openSync("dir/file", "w+"); - fs.writeSync(fd, Buffer.from("test"), 0, 4); - fs.closeSync(fd); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"], - ["change", "file"] - ]); - }); - it("open() + close() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {}, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - const fd = fs.openSync("dir/file", "w+"); - fs.closeSync(fd); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"], - ["change", "file"] - ]); - }); - it("writeFile() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": {}, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.writeFileSync("dir/file", "test"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"], - ["change", "file"] - ]); - }); - it("writeFile() replace file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "" }, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.writeFileSync("dir/file", "test"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["change", "file"] - ]); - }); - it("truncate() file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "test" }, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.truncateSync(vpath.combine(root, "dir/file")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["change", "file"] - ]); - }); - it("rename() file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "test" }, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.renameSync(vpath.combine(root, "dir/file"), vpath.combine(root, "dir/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"], - ["rename", "file1"] - ]); - }); - it("link() new file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "test" }, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.linkSync(vpath.combine(root, "dir/file"), vpath.combine(root, "dir/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file1"] - ]); - }); - it("symlink() file", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "test" }, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.symlinkSync(vpath.combine(root, "dir/file"), vpath.combine(root, "dir/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file1"] - ]); - }); - it("unlink() file (single link)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "" }, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.unlinkSync(vpath.combine(root, "dir/file")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"] - ]); - }); - it("unlink() file (multiple links)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file": "", "file1": new Link("file") }, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.unlinkSync(vpath.combine(root, "dir/file")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file"] - ]); - }); - it("mkdir() new subdirectory", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { }, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.mkdirSync(vpath.combine(root, "dir/subdir")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "subdir"] - ]); - }); - it("rmdir() subdirectory", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "subdir": {} }, "dir2": new Symlink("dir") } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir2"), callbackSpy.proxy); - fs.rmdirSync(vpath.combine(root, "dir/subdir")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "subdir"] - ]); - }); - }); - describe("symlink (file)", () => { - it("writeFile() replace", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file1": "", "file2": new Symlink("file1") } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir/file2"), callbackSpy.proxy); - fs.writeFileSync("dir/file1", "test"); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["change", "file2"] - ]); - }); - it("unlink() (single link)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: { "dir": { "file1": "", "file2": new Symlink("file1") } } } }); - const callbackSpy = createSpy(); - - fs.watch(vpath.combine(root, "dir/file2"), callbackSpy.proxy); - fs.unlinkSync(vpath.combine(root, "dir/file1")); - - assert.sameDeepMembers(callbackSpy.calls, [ - ["rename", "file2"] - ]); - }); - }); - it("fail: invalid path (ENOENT)", () => { - const fs = new FileSystem(ignoreCase, { files: { [root]: {} } }); - assert.throws(() => fs.watch(vpath.combine(root, "dir")), /ENOENT/); - }); - }); - }); - } - - describeFileSystem("posix", /*ignoreCase*/ false, /*root*/ "/"); - describeFileSystem("win32", /*ignoreCase*/ true, /*root*/ "c:/"); -}); \ No newline at end of file diff --git a/scripts/vfs/src/tests/index.ts b/scripts/vfs/src/tests/index.ts deleted file mode 100644 index 6dffd033e51..00000000000 --- a/scripts/vfs/src/tests/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { install } from "source-map-support"; -install(); - -import "./fileSystemTests"; diff --git a/scripts/vfs/src/tests/utils.ts b/scripts/vfs/src/tests/utils.ts deleted file mode 100644 index 1ac906f3892..00000000000 --- a/scripts/vfs/src/tests/utils.ts +++ /dev/null @@ -1,20 +0,0 @@ -export interface Theory { - title: string; - args: any[]; -} - -export function theory(name: string, data: (Theory | any[])[], callback: (...args: any[]) => any) { - describe(name, () => { - for (const theory of data) { - const title = Array.isArray(theory) ? theory.toString() : theory.title; - const args = Array.isArray(theory) ? theory : theory.args; - it(title, () => callback(...args)); - } - }); -} - -export function createSpy() { - const calls: any[][] = []; - const proxy = function(...args: any[]): any { calls.push(args); } - return { proxy, calls }; -} \ No newline at end of file diff --git a/scripts/vfs/src/watcher.ts b/scripts/vfs/src/watcher.ts deleted file mode 100644 index 077f86077ac..00000000000 --- a/scripts/vfs/src/watcher.ts +++ /dev/null @@ -1,109 +0,0 @@ -import * as events from "events"; -import { FileSystem } from "./fileSystem"; -import { Stats } from "./stats"; - -/* @internal */ -export interface FSWatcherEntry { - watcher: FSWatcher; - path: string; - container: FSWatcherEntrySet; - recursive: boolean; -} - -/* @internal */ -export class FSWatcherEntrySet extends Set { - private _recursiveCount: number; - private _nonRecursiveCount: number; - - public readonly path: string; - - constructor(path: string) { - super(); - this.path = path; - this._recursiveCount = 0; - this._nonRecursiveCount = 0; - } - - public get recursiveCount() { return this._recursiveCount; } - public get nonRecursiveCount() { return this._nonRecursiveCount; } - - public add(entry: FSWatcherEntry) { - const size = this.size; - super.add(entry); - if (this.size !== size) { - if (entry.recursive) { - this._recursiveCount++; - } - else { - this._nonRecursiveCount++; - } - } - return this; - } - - public delete(entry: FSWatcherEntry) { - if (super.delete(entry)) { - if (entry.recursive) { - this._recursiveCount--; - } - else { - this._nonRecursiveCount--; - } - return true; - } - return false; - } - - public clear() { - this._recursiveCount = 0; - this._nonRecursiveCount = 0; - } -} - -/* @internal */ -export interface WatchedFile { - path: string; - handle: any; - previous: Stats; - listener: (current: Stats, previous: Stats) => void; -} - -export class FSWatcher extends events.EventEmitter { - private _fs: FileSystem; - private _entry: FSWatcherEntry | undefined; - - constructor(fs: FileSystem) { - super(); - this._fs = fs; - } - - public close(): void { - if (this._entry) { - this._fs["_removeWatcher"](this._entry); - } - } -} - -// #region FSWatcher Event "change" -export interface FSWatcher { - on(event: "change", listener: (eventType: string, filename: string) => void): this; - once(event: "change", listener: (eventType: string, filename: string) => void): this; - addListener(event: "change", listener: (eventType: string, filename: string) => void): this; - removeListener(event: "change", listener: (eventType: string, filename: string) => void): this; - prependListener(event: "change", listener: (eventType: string, filename: string) => void): this; - prependOnceListener(event: "change", listener: (eventType: string, filename: string) => void): this; - emit(name: "change", eventType: string, filename: string): boolean; -} -// #endregion FSWatcher Event "change" - -// #region FSWatcher Event "error" -export interface FSWatcher { - on(event: "error", listener: (error: Error) => void): this; - once(event: "error", listener: (error: Error) => void): this; - addListener(event: "error", listener: (error: Error) => void): this; - removeListener(event: "error", listener: (error: Error) => void): this; - prependListener(event: "error", listener: (error: Error) => void): this; - prependOnceListener(event: "error", listener: (error: Error) => void): this; - emit(name: "error", error: Error): boolean; -} -// #endregion FSWatcher Event "error" \ No newline at end of file diff --git a/scripts/vfs/tsconfig.json b/scripts/vfs/tsconfig.json deleted file mode 100644 index 2d51a1d60ba..00000000000 --- a/scripts/vfs/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es2015", - "module": "commonjs", - "outDir": "dist", - "types": ["node", "mocha"], - "declaration": true, - "sourceMap": true, - "strict": true, - "preserveConstEnums": true - }, - "include": [ - "src/**/*" - ] -} \ No newline at end of file diff --git a/src/harness/assert.ts b/src/harness/assert.ts deleted file mode 100644 index 6e3be2b6e12..00000000000 --- a/src/harness/assert.ts +++ /dev/null @@ -1,117 +0,0 @@ -/// -declare namespace Chai { - interface ChaiStatic { - util: UtilStatic; - } - - interface UtilStatic { - objDisplay(obj: any): string; - } - - interface AssertStatic { - sameMembers(actual: Iterable, expected: Iterable, message?: string): void; - notSameMembers(actual: Iterable, expected: Iterable, message?: string): void; - includeMembersOnce(actual: Iterable, expected: Iterable, message?: string): void; - includeMembers(actual: Iterable, expected: Iterable, message?: string): void; - notIncludeMembers(actual: Iterable, expected: Iterable, message?: string): void; - } -} - -// patch assert.sameMembers to support other iterables -_chai.use((chai: Chai.ChaiStatic, util: Chai.UtilStatic) => { - function isIdenticalTo(left: Iterable, right: Iterable) { - if (!(left instanceof core.SortedSet) && right instanceof core.SortedSet) { - [left, right] = [right, left]; - } - - const set = asReadonlySet(left); - const seen = set instanceof core.SortedSet ? new core.SortedSet(set.comparer) : new Set(); - const iterator = Array.isArray(right) ? ts.arrayIterator(right) : right[Symbol.iterator](); - for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { - if (!set.has(value)) return false; - seen.add(value); - } - return set.size === seen.size; - } - - function isSubsetOf(subset: Iterable, superset: Iterable, unique?: boolean) { - const set = asReadonlySet(superset); - const seen = unique ? set instanceof core.SortedSet ? new core.SortedSet(set.comparer) : new Set() : undefined; - const iterator = Array.isArray(subset) ? ts.arrayIterator(subset) : subset[Symbol.iterator](); - for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { - if (!set.has(value)) return false; - if (seen) { - if (seen.has(value)) return false; - seen.add(value); - } - } - return true; - } - - function overlaps(left: Iterable, right: Iterable) { - if (!(left instanceof core.SortedSet) && right instanceof core.SortedSet) { - [left, right] = [right, left]; - } - - const set = asReadonlySet(left); - const iterator = Array.isArray(right) ? ts.arrayIterator(right) : right[Symbol.iterator](); - for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { - if (set.has(value)) return true; - } - return false; - } - - function asReadonlySet(iterable: Iterable): ReadonlySet { - return iterable instanceof Set ? iterable : - iterable instanceof core.SortedSet ? iterable : - new Set(iterable); - } - - function asReadonlyArray(iterable: Iterable): ReadonlyArray { - return Array.isArray(iterable) ? iterable : Array.from(iterable); - } - - // patch `assert.sameMembers` to support any iterable - chai.assert.sameMembers = (actual: Iterable, expected: Iterable, message?: string) => { - if (!isIdenticalTo(actual, expected)) { - actual = asReadonlyArray(actual); - expected = asReadonlyArray(expected); - assert.fail(actual, expected, message || `expected ${util.objDisplay(actual)} to have the same members as ${util.objDisplay(expected)}`); - } - }; - - // patch `assert.notSameMembers` to support any iterable - chai.assert.notSameMembers = (actual: Iterable, expected: Iterable, message?: string) => { - if (isIdenticalTo(actual, expected)) { - actual = asReadonlyArray(actual); - expected = asReadonlyArray(expected); - assert.fail(actual, expected, message || `expected ${util.objDisplay(actual)} to not have the same members as ${util.objDisplay(expected)}`); - } - }; - - chai.assert.includeMembersOnce = (actual: Iterable, expected: Iterable, message?: string) => { - if (!isSubsetOf(expected, actual, /*unique*/ true)) { - actual = asReadonlyArray(actual); - expected = asReadonlyArray(expected); - assert.fail(actual, expected, message || `expected ${util.objDisplay(actual)} to include the members of ${util.objDisplay(expected)} only once`); - } - }; - - // patch `assert.includeMembers` to support any iterable - chai.assert.includeMembers = (actual: Iterable, expected: Iterable, message?: string) => { - if (!isSubsetOf(expected, actual)) { - actual = asReadonlyArray(actual); - expected = asReadonlyArray(expected); - assert.fail(actual, expected, message || `expected ${util.objDisplay(actual)} to include the members of ${util.objDisplay(expected)}`); - } - }; - - // patch `assert.notIncludeMembers` to support any iterable - chai.assert.notIncludeMembers = (actual: Iterable, expected: Iterable, message?: string) => { - if (overlaps(expected, actual)) { - actual = asReadonlyArray(actual); - expected = asReadonlyArray(expected); - assert.fail(actual, expected, message || `expected ${util.objDisplay(actual)} to not include the members of ${util.objDisplay(expected)}`); - } - }; -}); \ No newline at end of file diff --git a/src/harness/core.ts b/src/harness/core.ts index 03a6c81605d..cb94af50bde 100644 --- a/src/harness/core.ts +++ b/src/harness/core.ts @@ -1,64 +1,4 @@ -// NOTE: This namespace re-exports all of the exports from the @typescript/harness-core private package. - -namespace core { - const _core = require("@typescript/harness-core"); - // tslint:disable:no-unnecessary-qualifier - core.identity = _core.identity; - core.compareNumbers = _core.compareNumbers; - core.compareStrings = _core.compareStrings; - core.compareStringsCaseSensitive = _core.compareStringsCaseSensitive; - core.compareStringsCaseInsensitive = _core.compareStringsCaseInsensitive; - core.equateStringsCaseSensitive = _core.equateStringsCaseSensitive; - core.equateStringsCaseInsensitive = _core.equateStringsCaseInsensitive; - core.SortedMap = _core.SortedMap; - core.SortedSet = _core.SortedSet; - core.binarySearch = _core.binarySearch; - core.removeAt = _core.removeAt; - core.insertAt = _core.insertAt; - core.Metadata = _core.Metadata; - core.padLeft = _core.padLeft; - core.padRight = _core.padRight; - core.getByteOrderMark = _core.getByteOrderMark; - core.getByteOrderMarkLength = _core.getByteOrderMarkLength; - core.removeByteOrderMark = _core.removeByteOrderMark; - core.addUTF8ByteOrderMark = _core.addUTF8ByteOrderMark; - core.getLinesAndLineStarts = _core.getLinesAndLineStarts; - core.splitLines = _core.splitLines; - core.computeLineStarts = _core.computeLineStarts; - core.sha1 = _core.sha1; - // tslint:enable:no-unnecessary-qualifier -} - -declare module "_core" { - import * as _core from "@typescript/harness-core"; - global { - namespace core { - export import identity = _core.identity; - export import compareNumbers = _core.compareNumbers; - export import compareStrings = _core.compareStrings; - export import compareStringsCaseSensitive = _core.compareStringsCaseSensitive; - export import compareStringsCaseInsensitive = _core.compareStringsCaseInsensitive; - export import equateStringsCaseSensitive = _core.equateStringsCaseSensitive; - export import equateStringsCaseInsensitive = _core.equateStringsCaseInsensitive; - export import SortOptions = _core.SortOptions; - export import SortedMap = _core.SortedMap; - export import SortedSet = _core.SortedSet; - export import binarySearch = _core.binarySearch; - export import removeAt = _core.removeAt; - export import insertAt = _core.insertAt; - export import Metadata = _core.Metadata; - export import padLeft = _core.padLeft; - export import padRight = _core.padRight; - export import getByteOrderMark = _core.getByteOrderMark; - export import getByteOrderMarkLength = _core.getByteOrderMarkLength; - export import removeByteOrderMark = _core.removeByteOrderMark; - export import addUTF8ByteOrderMark = _core.addUTF8ByteOrderMark; - export import LineStarts = _core.LineStarts; - export import LinesAndLineStarts = _core.LinesAndLineStarts; - export import getLinesAndLineStarts = _core.getLinesAndLineStarts; - export import splitLines = _core.splitLines; - export import computeLineStarts = _core.computeLineStarts; - export import sha1 = _core.sha1; - } - } -} \ No newline at end of file +/// +/// +/// +/// \ No newline at end of file diff --git a/src/harness/core/collections.ts b/src/harness/core/collections.ts new file mode 100644 index 00000000000..7936b5a9323 --- /dev/null +++ b/src/harness/core/collections.ts @@ -0,0 +1,542 @@ +/// +namespace core { + export interface SortOptions { + comparer: (a: T, b: T) => number; + sort: "insertion" | "comparison"; + } + + export class SortedMap { + private _comparer: (a: K, b: K) => number; + private _keys: K[] = []; + private _values: V[] = []; + private _order: number[] | undefined; + private _version = 0; + private _copyOnWrite = false; + + constructor(comparer: ((a: K, b: K) => number) | SortOptions, iterable?: Iterable<[K, V]>) { + this._comparer = typeof comparer === "object" ? comparer.comparer : comparer; + this._order = typeof comparer === "object" && comparer.sort === "insertion" ? [] : undefined; + if (iterable) { + const iterator = getIterator(iterable); + try { + for (let i = nextResult(iterator); i; i = nextResult(iterator)) { + const [key, value] = i.value; + this.set(key, value); + } + } + finally { + closeIterator(iterator); + } + } + } + + public get size() { + return this._keys.length; + } + + public get comparer() { + return this._comparer; + } + + public get [Symbol.toStringTag]() { + return "SortedMap"; + } + + public has(key: K) { + return binarySearch(this._keys, key, identity, this._comparer) >= 0; + } + + public get(key: K) { + const index = binarySearch(this._keys, key, identity, this._comparer); + return index >= 0 ? this._values[index] : undefined; + } + + public set(key: K, value: V) { + const index = binarySearch(this._keys, key, identity, this._comparer); + if (index >= 0) { + this._values[index] = value; + } + else { + this.writePreamble(); + insertAt(this._keys, ~index, key); + insertAt(this._values, ~index, value); + if (this._order) insertAt(this._order, ~index, this._version); + this.writePostScript(); + } + return this; + } + + public delete(key: K) { + const index = binarySearch(this._keys, key, identity, this._comparer); + if (index >= 0) { + this.writePreamble(); + removeAt(this._keys, index); + removeAt(this._values, index); + if (this._order) removeAt(this._order, index); + this.writePostScript(); + return true; + } + return false; + } + + public clear() { + if (this.size > 0) { + this.writePreamble(); + this._keys.length = 0; + this._values.length = 0; + if (this._order) this._order.length = 0; + this.writePostScript(); + } + } + + public forEach(callback: (value: V, key: K, collection: this) => void, thisArg?: any) { + const keys = this._keys; + const values = this._values; + const indices = this.getIterationOrder(); + const version = this._version; + this._copyOnWrite = true; + try { + if (indices) { + for (const i of indices) { + callback.call(thisArg, values[i], keys[i], this); + } + } + else { + for (let i = 0; i < keys.length; i++) { + callback.call(thisArg, values[i], keys[i], this); + } + } + } + finally { + if (version === this._version) { + this._copyOnWrite = false; + } + } + } + + public * keys() { + const keys = this._keys; + const indices = this.getIterationOrder(); + const version = this._version; + this._copyOnWrite = true; + try { + if (indices) { + for (const i of indices) { + yield keys[i]; + } + } + else { + yield* keys; + } + } + finally { + if (version === this._version) { + this._copyOnWrite = false; + } + } + } + + public * values() { + const values = this._values; + const indices = this.getIterationOrder(); + const version = this._version; + this._copyOnWrite = true; + try { + if (indices) { + for (const i of indices) { + yield values[i]; + } + } + else { + yield* values; + } + } + finally { + if (version === this._version) { + this._copyOnWrite = false; + } + } + } + + public * entries() { + const keys = this._keys; + const values = this._values; + const indices = this.getIterationOrder(); + const version = this._version; + this._copyOnWrite = true; + try { + if (indices) { + for (const i of indices) { + yield [keys[i], values[i]] as [K, V]; + } + } + else { + for (let i = 0; i < keys.length; i++) { + yield [keys[i], values[i]] as [K, V]; + } + } + } + finally { + if (version === this._version) { + this._copyOnWrite = false; + } + } + } + + public [Symbol.iterator]() { + return this.entries(); + } + + private writePreamble() { + if (this._copyOnWrite) { + this._keys = this._keys.slice(); + this._values = this._values.slice(); + if (this._order) this._order = this._order.slice(); + this._copyOnWrite = false; + } + } + + private writePostScript() { + this._version++; + } + + private getIterationOrder() { + if (this._order) { + const order = this._order; + return this._order + .map((_, i) => i) + .sort((x, y) => order[x] - order[y]); + } + return undefined; + } + } + + export class SortedSet { + private _comparer: (a: T, b: T) => number; + private _values: T[] = []; + private _order: number[] | undefined; + private _version = 0; + private _copyOnWrite = false; + + constructor(comparer: ((a: T, b: T) => number) | SortOptions, iterable?: Iterable) { + this._comparer = typeof comparer === "object" ? comparer.comparer : comparer; + this._order = typeof comparer === "object" && comparer.sort === "insertion" ? [] : undefined; + + if (iterable) { + const iterator = getIterator(iterable); + try { + for (let i = nextResult(iterator); i; i = nextResult(iterator)) { + const value = i.value; + this.add(value); + } + } + finally { + closeIterator(iterator); + } + } + } + + public get size() { + return this._values.length; + } + + public get comparer() { + return this._comparer; + } + + public get [Symbol.toStringTag]() { + return "SortedSet"; + } + + public has(value: T) { + return binarySearch(this._values, value, identity, this._comparer) >= 0; + } + + public add(value: T) { + const index = binarySearch(this._values, value, identity, this._comparer); + if (index < 0) { + this.writePreamble(); + insertAt(this._values, ~index, value); + if (this._order) insertAt(this._order, ~index, this._version); + this.writePostScript(); + } + return this; + } + + public delete(value: T) { + const index = binarySearch(this._values, value, identity, this._comparer); + if (index >= 0) { + this.writePreamble(); + removeAt(this._values, index); + if (this._order) removeAt(this._order, index); + this.writePostScript(); + return true; + } + return false; + } + + public clear() { + if (this.size > 0) { + this.writePreamble(); + this._values.length = 0; + if (this._order) this._order.length = 0; + this.writePostScript(); + } + } + + public forEach(callback: (value: T, key: T, collection: this) => void, thisArg?: any) { + const values = this._values; + const indices = this.getIterationOrder(); + const version = this._version; + this._copyOnWrite = true; + try { + if (indices) { + for (const i of indices) { + callback.call(thisArg, values[i], values[i], this); + } + } + else { + for (const value of values) { + callback.call(thisArg, value, value, this); + } + } + } + finally { + if (version === this._version) { + this._copyOnWrite = false; + } + } + } + + public keys() { + return this.values(); + } + + public * values() { + const values = this._values; + const indices = this.getIterationOrder(); + const version = this._version; + this._copyOnWrite = true; + try { + if (indices) { + for (const i of indices) { + yield values[i]; + } + } + else { + for (const value of values) { + yield value; + } + } + } + finally { + if (version === this._version) { + this._copyOnWrite = false; + } + } + } + + public * entries() { + const values = this._values; + const indices = this.getIterationOrder(); + const version = this._version; + this._copyOnWrite = true; + try { + if (indices) { + for (const i of indices) { + yield [values[i], values[i]] as [T, T]; + } + } + else { + for (const value of values) { + yield [value, value] as [T, T]; + } + } + } + finally { + if (version === this._version) { + this._copyOnWrite = false; + } + } + } + + public [Symbol.iterator]() { + return this.values(); + } + + private writePreamble() { + if (this._copyOnWrite) { + this._values = this._values.slice(); + if (this._order) this._order = this._order.slice(); + this._copyOnWrite = false; + } + } + + private writePostScript() { + this._version++; + } + + private getIterationOrder() { + if (this._order) { + const order = this._order; + return this._order + .map((_, i) => i) + .sort((x, y) => order[x] - order[y]); + } + return undefined; + } + } + + export function binarySearch(array: ReadonlyArray, value: T, keySelector: (v: T) => U, keyComparer: (a: U, b: U) => number, offset?: number): number { + if (!array || array.length === 0) { + return -1; + } + + let low = offset || 0; + let high = array.length - 1; + const key = keySelector(value); + while (low <= high) { + const middle = low + ((high - low) >> 1); + const midKey = keySelector(array[middle]); + const result = keyComparer(midKey, key); + if (result < 0) { + low = middle + 1; + } + else if (result > 0) { + high = middle - 1; + } + else { + return middle; + } + } + + return ~low; + } + + export function removeAt(array: T[], index: number): void { + if (index < 0 || index >= array.length) { + return; + } + else if (index === 0) { + array.shift(); + } + else if (index === array.length - 1) { + array.pop(); + } + else { + for (let i = index; i < array.length - 1; i++) { + array[i] = array[i + 1]; + } + array.length--; + } + } + + export function insertAt(array: T[], index: number, value: T): void { + if (index === 0) { + array.unshift(value); + } + else if (index === array.length) { + array.push(value); + } + else { + for (let i = array.length; i > index; i--) { + array[i] = array[i - 1]; + } + array[index] = value; + } + } + + export function getIterator(iterable: Iterable): Iterator { + return iterable[Symbol.iterator](); + } + + export function nextResult(iterator: Iterator): IteratorResult | undefined { + const result = iterator.next(); + return result.done ? undefined : result; + } + + export function closeIterator(iterator: Iterator) { + const fn = iterator.return; + if (typeof fn === "function") fn.call(iterator); + } + + /** + * A collection of metadata that supports inheritance. + */ + export class Metadata { + private static readonly _undefinedValue = {}; + private _parent: Metadata | undefined; + private _map: { [key: string]: any }; + private _version = 0; + private _size = -1; + private _parentVersion: number | undefined; + + constructor(parent?: Metadata) { + this._parent = parent; + this._map = Object.create(parent ? parent._map : null); // tslint:disable-line:no-null-keyword + } + + public get size(): number { + if (this._size === -1 || (this._parent && this._parent._version !== this._parentVersion)) { + let size = 0; + for (const _ in this._map) size++; + this._size = size; + if (this._parent) { + this._parentVersion = this._parent._version; + } + } + return this._size; + } + + public get parent() { + return this._parent; + } + + public has(key: string): boolean { + return this._map[Metadata._escapeKey(key)] !== undefined; + } + + public get(key: string): any { + const value = this._map[Metadata._escapeKey(key)]; + return value === Metadata._undefinedValue ? undefined : value; + } + + public set(key: string, value: any): this { + this._map[Metadata._escapeKey(key)] = value === undefined ? Metadata._undefinedValue : value; + this._size = -1; + this._version++; + return this; + } + + public delete(key: string): boolean { + const escapedKey = Metadata._escapeKey(key); + if (this._map[escapedKey] !== undefined) { + delete this._map[escapedKey]; + this._size = -1; + this._version++; + return true; + } + return false; + } + + public clear(): void { + this._map = Object.create(this._parent ? this._parent._map : null); // tslint:disable-line:no-null-keyword + this._size = -1; + this._version++; + } + + public forEach(callback: (value: any, key: string, map: this) => void) { + for (const key in this._map) { + callback(this._map[key], Metadata._unescapeKey(key), this); + } + } + + private static _escapeKey(text: string) { + return (text.length >= 2 && text.charAt(0) === "_" && text.charAt(1) === "_" ? "_" + text : text); + } + + private static _unescapeKey(text: string) { + return (text.length >= 3 && text.charAt(0) === "_" && text.charAt(1) === "_" && text.charAt(2) === "_" ? text.slice(1) : text); + } + } +} \ No newline at end of file diff --git a/src/harness/core/comparers.ts b/src/harness/core/comparers.ts new file mode 100644 index 00000000000..c0846437308 --- /dev/null +++ b/src/harness/core/comparers.ts @@ -0,0 +1,43 @@ +namespace core { + export function compareNumbers(a: number, b: number): number { + if (a === b) return 0; + if (a === undefined) return -1; + if (b === undefined) return +1; + return a < b ? -1 : +1; + } + + export function compareStrings(a: string, b: string, ignoreCase: boolean): number { + return ignoreCase + ? compareStringsCaseInsensitive(a, b) + : compareStringsCaseSensitive(a, b); + } + + // NOTE: This is a duplicate of `compareNumbers` above, but is intended to be used only with + // strings to reduce polymorphism. + export function compareStringsCaseSensitive(a: string, b: string): number { + if (a === b) return 0; + if (a === undefined) return -1; + if (b === undefined) return +1; + return a < b ? -1 : +1; + } + + export function compareStringsCaseInsensitive(a: string, b: string): number { + if (a === b) return 0; + if (a === undefined) return -1; + if (b === undefined) return +1; + a = a.toUpperCase(); + b = b.toUpperCase(); + return a < b ? -1 : a > b ? +1 : 0; + } + + export function equateStringsCaseSensitive(a: string, b: string): boolean { + return a === b; + } + + export function equateStringsCaseInsensitive(a: string, b: string): boolean { + return a === b + || a !== undefined + && b !== undefined + && a.toUpperCase() === b.toUpperCase(); + } +} \ No newline at end of file diff --git a/src/harness/core/functions.ts b/src/harness/core/functions.ts new file mode 100644 index 00000000000..870ea844b32 --- /dev/null +++ b/src/harness/core/functions.ts @@ -0,0 +1,3 @@ +namespace core { + export function identity(v: T): T { return v; } +} diff --git a/src/harness/core/strings.ts b/src/harness/core/strings.ts new file mode 100644 index 00000000000..9d1b270ae25 --- /dev/null +++ b/src/harness/core/strings.ts @@ -0,0 +1,134 @@ +namespace core { + export function padLeft(text: string, size: number, ch = " "): string { + while (text.length < size) text = ch + text; + return text; + } + + export function padRight(text: string, size: number, ch = " "): string { + while (text.length < size) text += ch; + return text; + } + + export function getByteOrderMark(text: string): string { + const length = getByteOrderMarkLength(text); + return length > 0 ? text.slice(0, length) : ""; + } + + export function getByteOrderMarkLength(text: string): number { + if (text.length >= 2) { + const ch0 = text.charCodeAt(0); + const ch1 = text.charCodeAt(1); + if ((ch0 === 0xff && ch1 === 0xfe) || + (ch0 === 0xfe && ch1 === 0xff)) { + return 2; + } + if (text.length >= 3 && ch0 === 0xef && ch1 === 0xbb && text.charCodeAt(2) === 0xbf) { + return 3; + } + } + return 0; + } + + export function removeByteOrderMark(text: string): string { + const length = getByteOrderMarkLength(text); + return length ? text.slice(length) : text; + } + + export function addUTF8ByteOrderMark(text: string) { + return getByteOrderMarkLength(text) === 0 ? "\u00EF\u00BB\u00BF" + text : text; + } + + function splitLinesWorker(text: string, lineStarts: number[] | undefined, lines: string[] | undefined, removeEmptyElements: boolean) { + let pos = 0; + let end = 0; + let lineStart = 0; + let nonWhiteSpace = false; + while (pos < text.length) { + const ch = text.charCodeAt(pos); + end = pos; + pos++; + switch (ch) { + // LineTerminator + case 0x000d: // carriage return + if (pos < text.length && text.charCodeAt(pos) === 0x000a) { + pos++; + } + // falls through + + case 0x000a: // line feed + case 0x2028: // line separator + case 0x2029: // paragraph separator + if (lineStarts) { + lineStarts.push(lineStart); + } + if (lines && (!removeEmptyElements || nonWhiteSpace)) { + lines.push(text.slice(lineStart, end)); + } + lineStart = pos; + nonWhiteSpace = false; + break; + + // WhiteSpace + case 0x0009: // tab + case 0x000b: // vertical tab + case 0x000c: // form feed + case 0x0020: // space + case 0x00a0: // no-break space + case 0xfeff: // zero width no-break space + case 0x1680: // ogham space mark + case 0x2000: // en quad + case 0x2001: // em quad + case 0x2002: // en space + case 0x2003: // em space + case 0x2004: // three-per-em space + case 0x2005: // four-per-em space + case 0x2006: // six-per-em space + case 0x2007: // figure space + case 0x2008: // punctuation space + case 0x2009: // thin space + case 0x200a: // hair space + case 0x202f: // narrow no-break space + case 0x205f: // medium mathematical space + case 0x3000: // ideographic space + case 0x0085: // next-line (not strictly per spec, but used by the compiler) + break; + + default: + nonWhiteSpace = true; + break; + } + } + if (lineStarts) { + lineStarts.push(lineStart); + } + if (lines && (!removeEmptyElements || nonWhiteSpace)) { + lines.push(text.slice(lineStart, text.length)); + } + } + + export type LineStarts = ReadonlyArray; + + export interface LinesAndLineStarts { + readonly lines: ReadonlyArray; + readonly lineStarts: LineStarts; + } + + export function getLinesAndLineStarts(text: string): LinesAndLineStarts { + const lines: string[] = []; + const lineStarts: number[] = []; + splitLinesWorker(text, lineStarts, lines, /*removeEmptyElements*/ false); + return { lines, lineStarts }; + } + + export function splitLines(text: string, removeEmptyElements = false): string[] { + const lines: string[] = []; + splitLinesWorker(text, /*lineStarts*/ undefined, lines, removeEmptyElements); + return lines; + } + + export function computeLineStarts(text: string): LineStarts { + const lineStarts: number[] = []; + splitLinesWorker(text, lineStarts, /*lines*/ undefined, /*removeEmptyElements*/ false); + return lineStarts; + } +} \ No newline at end of file diff --git a/src/harness/events.ts b/src/harness/events.ts deleted file mode 100644 index f882f461729..00000000000 --- a/src/harness/events.ts +++ /dev/null @@ -1,30 +0,0 @@ -/// - -// NOTE: The contents of this file are all exported from the namespace 'events'. This is to -// support the eventual conversion of harness into a modular system. - -namespace events { - const _events = require("events"); - - export const EventEmitter: { - new (): EventEmitter; - prototype: EventEmitter; - defaultMaxListeners: number; - } = _events.EventEmitter; - - export interface EventEmitter { - on(event: string | symbol, listener: (...args: any[]) => void): this; - once(event: string | symbol, listener: (...args: any[]) => void): this; - addListener(event: string | symbol, listener: (...args: any[]) => void): this; - prependListener(event: string | symbol, listener: (...args: any[]) => void): this; - prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; - removeListener(event: string | symbol, listener: (...args: any[]) => void): this; - removeAllListeners(event?: string | symbol): this; - setMaxListeners(n: number): this; - getMaxListeners(): number; - listeners(event: string | symbol): Function[]; - emit(event: string | symbol, ...args: any[]): boolean; - eventNames(): (string | symbol)[]; - listenerCount(type: string | symbol): number; - } -} \ No newline at end of file diff --git a/src/harness/fakes.ts b/src/harness/fakes.ts index 580a23f22c4..f57f14cf007 100644 --- a/src/harness/fakes.ts +++ b/src/harness/fakes.ts @@ -1,5 +1,4 @@ /// -/// /// /// @@ -111,12 +110,12 @@ namespace fakes { return vfsutils.getFileSize(this.vfs, path); } - public watchFile(path: string, cb: ts.FileWatcherCallback) { - return vfsutils.watchFile(this.vfs, path, cb); + public watchFile(_path: string, _cb: ts.FileWatcherCallback): ts.FileWatcher { + return { close(): void { /*ignored*/ } }; } - public watchDirectory(path: string, cb: ts.DirectoryWatcherCallback, recursive?: boolean): ts.FileWatcher { - return vfsutils.watchDirectory(this.vfs, path, cb, recursive); + public watchDirectory(_path: string, _cb: ts.DirectoryWatcherCallback, _recursive?: boolean): ts.FileWatcher { + return { close(): void { /*ignored*/ } }; } public resolvePath(path: string) { @@ -132,7 +131,7 @@ namespace fakes { } public createHash(data: string): string { - return core.sha1(data); + return data; } public realpath(path: string) { diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index 634cd44db5a..7292ca921c3 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -133,10 +133,13 @@ "../server/session.ts", "../server/scriptVersionCache.ts", - "assert.ts", + "./core/functions.ts", + "./core/comparers.ts", + "./core/collections.ts", + "./core/strings.ts", "core.ts", + "utils.ts", - "events.ts", "documents.ts", "vpath.ts", "vfs.ts", diff --git a/src/harness/vfs.ts b/src/harness/vfs.ts index b780f8b4e2a..cd2b340ced6 100644 --- a/src/harness/vfs.ts +++ b/src/harness/vfs.ts @@ -1,37 +1,2606 @@ -// NOTE: This namespace re-exports all of the exports from the @typescript/vfs private package. - +// tslint:disable:no-null-keyword namespace vfs { - const _vfs = require("@typescript/vfs"); - // tslint:disable:no-unnecessary-qualifier - vfs.Directory = _vfs.Directory; - vfs.File = _vfs.File; - vfs.Link = _vfs.Link; - vfs.Symlink = _vfs.Symlink; - vfs.Mount = _vfs.Mount; - vfs.FileSystem = _vfs.FileSystem; - vfs.Stats = _vfs.Stats; - vfs.FSWatcher = _vfs.FSWatcher; - // tslint:enable:no-unnecessary-qualifier -} + const _throw = (e: any) => { throw e; }; -declare module "_vfs" { - import * as _vfs from "@typescript/vfs"; - global { - namespace vfs { - export import FileSystemResolver = _vfs.FileSystemResolver; - export import FileSystemTimers = _vfs.FileSystemTimers; - export import FileSet = _vfs.FileSet; - export import Directory = _vfs.Directory; - export import DirectoryLike = _vfs.DirectoryLike; - export import File = _vfs.File; - export import FileLike = _vfs.FileLike; - export import Link = _vfs.Link; - export import Symlink = _vfs.Symlink; - export import Mount = _vfs.Mount; - export import FileSystemOptions = _vfs.FileSystemOptions; - export import FileSystem = _vfs.FileSystem; - export import Stats = _vfs.Stats; - export import FSWatcher = _vfs.FSWatcher; + // file type + const S_IFMT = 0o170000; // file type + const S_IFSOCK = 0o140000; // socket + const S_IFLNK = 0o120000; // symbolic link + const S_IFREG = 0o100000; // regular file + const S_IFBLK = 0o060000; // block device + const S_IFDIR = 0o040000; // directory + const S_IFCHR = 0o020000; // character device + const S_IFIFO = 0o010000; // FIFO + + // file mode bits + const S_ISUID = 0o004000; // set-user-ID bit + const S_ISGID = 0o002000; // set-group-ID bit + + // file permission bits + const S_IRUSR = 0o000400; // read by owner + const S_IWUSR = 0o000200; // write by owner + const S_IRWXU = 0o000700; // read/write/execute by owner + const S_IRWXG = 0o000070; // read/write/execute by group + const S_IRWXO = 0o000007; // read/write/execute by others + + const O_ACCMODE = 0o00000003; + const O_RDONLY = 0o00000000; + const O_WRONLY = 0o00000001; + const O_RDWR = 0o00000002; + + const O_CREAT = 0o00000100; + const O_EXCL = 0o00000200; + const O_TRUNC = 0o00001000; + const O_APPEND = 0o00002000; + const O_SYNC = 0o00010000; // explicit fsync + const O_DIRECTORY = 0o00200000; + const O_NOFOLLOW = 0o00400000; + + const F_OK = 0o00000000; // path is visible to the current process + const X_OK = 0o00000001; // path can be executed or searched by the current process + const W_OK = 0o00000002; // path can be written to by the current process + const R_OK = 0o00000004; // path can be read by the current process + const RWX_OK = R_OK | W_OK | X_OK; + + let devCount = 0; // A monotonically increasing count of device ids + let inoCount = 0; // A monotonically increasing count of inodes + let fdCount = 0; // A monotonically increasing count of file descriptors + + /** + * Represents a virtual POSIX-like file system. + */ + export class FileSystem { + /** Indicates whether the file system is case-sensitive (`false`) or case-insensitive (`true`). */ + public readonly ignoreCase: boolean; + + /** Gets the comparison function used to compare two paths. */ + public readonly stringComparer: (a: string, b: string) => number; + + private static _portableFilenameCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"; + + // lazy-initialized state that should be mutable even if the FileSystem is frozen. + private _lazy: { + links?: core.SortedMap; + shadows?: Map; + meta?: core.Metadata; + } = {}; + + private _cwd: string; // current working directory + private _uid: number; + private _gid: number; + private _umask: number; + private _time: number | Date | (() => number | Date); + private _openFiles = new Map(); + private _shadowRoot: FileSystem | undefined; + private _dirStack: string[] | undefined; + + constructor(ignoreCase: boolean, options: FileSystemOptions = {}) { + const { uid = 0, gid = 0, umask = 0o022, time = -1, files, meta } = options; + this.ignoreCase = ignoreCase; + this.stringComparer = this.ignoreCase ? vpath.compareCaseInsensitive : vpath.compareCaseSensitive; + this._uid = uid; + this._gid = gid; + this._umask = umask; + this._time = time; + + if (meta) { + for (const key of Object.keys(meta)) { + this.meta.set(key, meta[key]); + } + } + + if (files) { + this._applyFiles(files, /*dirname*/ ""); + } + + let cwd = options.cwd; + if ((!cwd || !vpath.isRoot(cwd)) && this._lazy.links) { + const iterator = core.getIterator(this._lazy.links.keys()); + try { + for (let i = core.nextResult(iterator); i; i = core.nextResult(iterator)) { + const name = i.value; + cwd = cwd ? vpath.resolve(name, cwd) : name; + break; + } + } + finally { + core.closeIterator(iterator); + } + } + + if (cwd) { + vpath.validate(cwd, vpath.ValidationFlags.Absolute); + this.mkdirpSync(cwd, /*mode*/ 0o777); + } + + this._cwd = cwd || ""; + } + + /** + * Gets metadata for this `FileSystem`. + */ + public get meta(): core.Metadata { + if (!this._lazy.meta) { + this._lazy.meta = new core.Metadata(this._shadowRoot ? this._shadowRoot.meta : undefined); + } + return this._lazy.meta; + } + + /** + * Gets a value indicating whether the file system is read-only. + */ + public get isReadonly() { + return Object.isFrozen(this); + } + + /** + * Makes the file system read-only. + */ + public makeReadonly() { + Object.freeze(this); + return this; + } + + /** + * Gets the file system shadowed by this file system. + */ + public get shadowRoot() { + return this._shadowRoot; + } + + /** + * Gets a shadow of this file system. + */ + public shadow(ignoreCase = this.ignoreCase) { + if (!this.isReadonly) throw new Error("Cannot shadow a mutable file system."); + if (ignoreCase && !this.ignoreCase) throw new Error("Cannot create a case-insensitive file system from a case-sensitive one."); + const fs = new FileSystem(ignoreCase, { + uid: this._uid, + gid: this._gid, + umask: this._umask, + time: this._time, + }); + fs._shadowRoot = this; + fs._cwd = this._cwd; + return fs; + } + + /** + * Gets the metadata object for a path. + * @param path + */ + public filemeta(path: string): core.Metadata { + path = this._resolve(path); + const { node } = this._find(path) || _throw(new IOError("ENOENT", "scandir", path)); + return this._filemeta(node); + } + + private _filemeta(node: Inode): core.Metadata { + if (!node.meta) { + const parentMeta = node.shadowRoot && this._shadowRoot && this._shadowRoot._filemeta(node.shadowRoot); + node.meta = new core.Metadata(parentMeta); + } + return node.meta; + } + + /** + * Gets the user ID to use for file system access. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/geteuid.html + */ + public getuid() { + return this._uid; + } + + /** + * Sets the user ID to use for file system access. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/setuid.html + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/seteuid.html + */ + public setuid(value: number) { + if (this.isReadonly) throw new IOError("EPERM", "setuid"); + this._uid = value; + } + + /** + * Gets the group ID to use for file system access. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html + */ + public getgid() { + return this._gid; + } + + /** + * Sets the group ID to use for file system access. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/setgid.html + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html + */ + public setgid(value: number) { + if (this.isReadonly) throw new IOError("EPERM", "setgid"); + this._gid = value; + } + + /** + * Gets or sets the virtual process's file mode creation mask (umask) + * to `mask & 0o777` and returns the previous value of the mask. + * + * @link http://man7.org/linux/man-pages/man2/umask.2.html + */ + public umask(value?: number): number { + if (value !== undefined && this.isReadonly) throw new IOError("EPERM", "umask"); + const result = this._umask; + if (value !== undefined) { + this._umask = value; + } + return result; + } + + /** + * Gets or sets the timestamp (in milliseconds) used for file status, returning the previous timestamp. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/time.html + */ + public time(value?: number | Date | (() => number | Date)): number { + if (value !== undefined && this.isReadonly) throw new IOError("EPERM"); + let result = this._time; + if (typeof result === "function") result = result(); + if (typeof result === "object") result = result.getTime(); + if (result === -1) result = Date.now(); + if (value !== undefined) { + this._time = value; + } + return result; + } + + /** + * Get the pathname of the current working directory. + * + * @link - http://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html + */ + public cwd() { + if (!this._cwd) throw new Error("The current working directory has not been set."); + const { node } = this._find(this._cwd) || _throw(new IOError("ENOENT", "getcwd")); + if (!isDirectoryInode(node)) throw new IOError("ENOTDIR", "getcwd"); + if (!this._access(node, X_OK)) throw new IOError("EPERM", "getcwd"); + return this._cwd; + } + + /** + * Changes the current working directory. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html + */ + public chdir(path: string) { + if (this.isReadonly) throw new IOError("EPERM", "chdir", path); + path = this._resolve(path); + const { node } = this._find(path) || _throw(new IOError("ENOENT", "chdir", path)); + if (!isDirectoryInode(node)) throw new IOError("ENOTDIR", "chdir", path); + if (this._cwd !== path) { + this._cwd = path; + } + } + + /** + * Pushes the current directory onto the directory stack and changes the current working directory to the supplied path. + */ + public pushd(path?: string) { + if (this.isReadonly) throw new IOError("EPERM", "chdir", path); + if (path) path = this._resolve(path); + if (this._cwd) { + if (!this._dirStack) this._dirStack = []; + this._dirStack.push(this._cwd); + } + if (path && path !== this._cwd) { + this.chdir(path); + } + } + + /** + * Pops the previous directory from the location stack and changes the current directory to that directory. + */ + public popd() { + if (this.isReadonly) throw new IOError("EPERM", "popd"); + const path = this._dirStack && this._dirStack.pop(); + if (path) { + this.chdir(path); + } + } + + /** + * Apply a file map to the file system. + */ + public apply(files: FileSet) { + this._applyFiles(files, this._cwd); + } + + /** + * Recursively remove all files and directories underneath the provided path. + */ + public rimrafSync(path: string) { + try { + const stats = this.lstatSync(path); + if (stats.isFile() || stats.isSymbolicLink()) { + this.unlinkSync(path); + } + else if (stats.isDirectory()) { + for (const file of this.readdirSync(path)) { + this.rimrafSync(vpath.combine(path, file)); + } + this.rmdirSync(path); + } + } + catch (e) { + if (e.code === "ENOENT") return; + throw e; + } + } + + /** + * Checks whether the calling process can access the file `path`. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html + */ + public access(path: string, callback: (err: Error | null) => void): void; + /** + * Checks whether the calling process can access the file `path`. If `path` is a symbolic link, it is dereferenced. + * @param mode One or more of the constants `F_OK`, `R_OK`, `W_OK`, or `X_OK`. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html + */ + public access(path: string, mode: number | undefined, callback: (err: Error | null) => void): void; + public access(path: string, modeOrCallback: number | typeof callback, callback?: (err: Error | null) => void) { + let mode: number | undefined; + if (typeof modeOrCallback === "function") callback = modeOrCallback; + else if (typeof modeOrCallback === "number") mode = modeOrCallback; + if (!callback) throw new IOError("EINVAL"); + try { + this.accessSync(path, mode); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Checks whether the calling process can access the file `path`. If `path` is a symbolic link, it is dereferenced. + * @param mode One or more of the constants `F_OK`, `R_OK`, `W_OK`, or `X_OK`. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html + */ + public accessSync(path: string, mode: number = F_OK) { + path = this._resolve(path); + if (!isFinite(mode) || (mode & ~RWX_OK)) throw new IOError("EINVAL", "access", path); + const { node } = this._find(path) || _throw(new IOError("ENOENT", "access", path)); + if (!this._access(node, mode)) throw new IOError("EACCES", "access", path); + } + + private _access(node: Inode, mode: number) { + let flags = (node.mode & S_IRWXO) >> 0; + if (this.getgid() === node.gid) flags |= (node.mode & S_IRWXG) >> 3; + if (this.getuid() === node.uid) flags |= (node.mode & S_IRWXU) >> 6; + return (flags & mode) === mode; + } + + /** + * Changes the permissions of a file. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html + */ + public chmod(path: string, mode: number, callback: (err: Error | null) => void) { + try { + this.chmodSync(path, mode); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Changes the permissions of a file. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html + */ + public chmodSync(path: string, mode: number) { + path = this._resolve(path); + this._chmod("chmod", this._find(path) || _throw(new IOError("ENOENT", "chmod", path)), mode, path); + } + + /** + * Changes the permissions of a file. Like `chmod`, except symbolic links are not dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html + */ + public lchmod(path: string, mode: number, callback: (err: Error | null) => void) { + try { + this.lchmodSync(path, mode); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Changes the permissions of a file. Like `chmod`, except symbolic links are not dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html + */ + public lchmodSync(path: string, mode: number) { + path = this._resolve(path); + this._chmod("lchmod", this._lfind(path) || _throw(new IOError("ENOENT", "lchmod", path)), mode, path); + } + + /** + * Changes the permissions of an open file + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html + */ + public fchmod(fd: number, mode: number, callback: (err: Error | null) => void) { + try { + this.fchmodSync(fd, mode); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Changes the permissions of an open file + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html + */ + public fchmodSync(fd: number, mode: number) { + this._chmod("fchmod", this._file("fchmod", fd), mode); + } + + private _chmod(syscall: string, entry: FileDescription, mode: number, path?: string) { + if (!isFinite(mode)) throw new IOError("EINVAL", syscall, path); + if (this._uid !== 0 && this._uid !== entry.node.uid) throw new IOError("EPERM", syscall, path); + if (this.isReadonly) throw new IOError("EROFS", syscall, path); + + entry.node.mode = (entry.node.mode & S_IFMT) | (mode & 0o7777); + entry.node.ctimeMs = this.time(); + } + + /** + * Changes the ownership of a file. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html + */ + public chown(path: string, uid: number, gid: number, callback: (err: Error | null) => void) { + try { + this.chownSync(path, uid, gid); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Changes the ownership of a file. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html + */ + public chownSync(path: string, uid: number, gid: number) { + path = this._resolve(path); + this._chown("chown", this._find(path) || _throw(new IOError("ENOENT", "chown", path)), uid, gid, path); + } + + /** + * Changes the ownership of a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lchown.html + */ + public lchown(path: string, uid: number, gid: number, callback: (err: Error | null) => void) { + try { + this.lchownSync(path, uid, gid); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Changes the ownership of a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lchown.html + */ + public lchownSync(path: string, uid: number, gid: number) { + path = this._resolve(path); + this._chown("lchown", this._lfind(path) || _throw(new IOError("ENOENT", "lchown", path)), uid, gid, path); + } + + /** + * Changes the ownership of an open file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html + */ + public fchown(fd: number, uid: number, gid: number, callback: (err: Error | null) => void) { + try { + this.fchownSync(fd, uid, gid); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Changes the ownership of an open file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html + */ + public fchownSync(fd: number, uid: number, gid: number) { + this._chown("fchown", this._file("fchown", fd), uid, gid); + } + + private _chown(syscall: string, entry: FileDescription, uid: number, gid: number, path?: string) { + if (!isFinite(uid) || !isFinite(gid)) throw new IOError("EINVAL", syscall, path); + if (uid === entry.node.uid) uid = -1; + if (gid === entry.node.gid) gid = -1; + if (uid === -1 && gid === -1) return; + if (uid !== -1 && this._uid !== 0) throw new IOError("EPERM", syscall, path); + if (gid !== -1 && this._uid !== 0 && this._uid !== entry.node.uid) throw new IOError("EPERM", syscall, path); + if (this.isReadonly) throw new IOError("EROFS", syscall, path); + + if (uid !== -1) entry.node.uid = uid; + if (gid !== -1) entry.node.gid = gid; + entry.node.mode &= ~(S_ISGID | S_ISUID); + entry.node.ctimeMs = this.time(); + } + + /** + * Sets file access and modification times. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html + */ + public utimes(path: string, atime: number | Date, mtime: number | Date, callback: (err: Error | null) => void) { + try { + this.utimesSync(path, atime, mtime); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Sets file access and modification times. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html + */ + public utimesSync(path: string, atime: number | Date, mtime: number | Date) { + path = this._resolve(path); + this._utimes("utimes", this._find(path) || _throw(new IOError("ENOENT", "utimes", path)), atime, mtime, path); + } + + /** + * Sets file access and modification times. If `path` is a symbolic link, it is not dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html + */ + public lutimes(path: string, atime: number | Date, mtime: number | Date, callback: (err: Error | null) => void) { + try { + this.lutimesSync(path, atime, mtime); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Sets file access and modification times. If `path` is a symbolic link, it is not dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html + */ + public lutimesSync(path: string, atime: number | Date, mtime: number | Date) { + path = this._resolve(path); + this._utimes("lutimes", this._lfind(path) || _throw(new IOError("ENOENT", "lutimes", path)), atime, mtime, path); + } + + /** + * Sets file access and modification times. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/futimes.html + */ + public futimes(fd: number, atime: number | Date, mtime: number | Date, callback: (err: Error | null) => void) { + try { + this.futimesSync(fd, atime, mtime); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Sets file access and modification times. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/futimes.html + */ + public futimesSync(fd: number, atime: number | Date, mtime: number | Date) { + this._utimes("futimes", this._file("futimes", fd), atime, mtime); + } + + private _utimes(syscall: string, entry: FileDescription, atime: number | Date, mtime: number | Date, path?: string) { + if (this.isReadonly) throw new IOError("EROFS", syscall, path); + const atimeMs = typeof atime === "number" ? atime : atime.getTime(); + const mtimeMs = typeof mtime === "number" ? mtime : mtime.getTime(); + if (!isFinite(atimeMs) || !isFinite(mtimeMs)) throw new IOError("EINVAL", syscall, path); + + entry.node.atimeMs = atimeMs; + entry.node.mtimeMs = mtimeMs; + entry.node.ctimeMs = this.time(); + } + + public fsync(fd: number, callback: (err: Error | null) => void): void { + try { + this.fsyncSync(fd); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + public fsyncSync(fd: number): void { + this._fsync(this._file("fsync", fd, "file"), /*metadata*/ true); + } + + public fdatasync(fd: number, callback: (err: Error | null) => void): void { + try { + this.fdatasyncSync(fd); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + public fdatasyncSync(fd: number): void { + this._fsync(this._file("fsyncdata", fd, "file"), /*metadata*/ false); + } + + private _fsync(entry: OpenFileDescription, metadata: boolean) { + if (isFileInode(entry.node) && entry.buffer && entry.buffer !== entry.node.buffer) { + const time = this.time(); + entry.node.buffer = entry.buffer; + entry.node.mtimeMs = time; + entry.node.ctimeMs = time; + if (metadata) { + entry.node.size = entry.node.buffer.byteLength; + } + } + } + + /** + * Get file status. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html + */ + public stat(path: string, callback: (err: Error | null, stats: Stats | null) => void) { + try { + process.nextTick(callback, /*e*/ null, this.statSync(path)); + } + catch (e) { + process.nextTick(callback, e, /*stats*/ null); + } + } + + /** + * Get file status. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html + */ + public statSync(path: string) { + path = this._resolve(path); + return this._stat("stat", this._find(path) || _throw(new IOError("ENOENT", "stat", path)), path); + } + + /** + * Get file status. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html + */ + public lstat(path: string, callback: (err: Error | null, stats: Stats | null) => void) { + try { + process.nextTick(callback, /*e*/ null, this.lstatSync(path)); + } + catch (e) { + process.nextTick(callback, e, /*stats*/ null); + } + } + + /** + * Get file status. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html + */ + public lstatSync(path: string) { + path = this._resolve(path); + return this._stat("lstat", this._lfind(path) || _throw(new IOError("ENOENT", "lstat", path)), path); + } + + /** + * Get file status. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fstat.html + */ + public fstat(fd: number, callback: (err: Error | null, stats: Stats | null) => void) { + try { + process.nextTick(callback, /*e*/ null, this.fstatSync(fd)); + } + catch (e) { + process.nextTick(callback, e, /*stats*/ null); + } + } + + /** + * Get file status. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/fstat.html + */ + public fstatSync(fd: number) { + return this._stat("fstat", this._file("fstat", fd)); + } + + private _stat(_syscall: string, entry: FileDescription, _path?: string) { + const node = entry.node; + return new Stats( + node.dev, + node.ino, + node.mode, + node.nlink, + node.uid, + node.gid, + /*rdev*/ 0, + /*size*/ isFileInode(node) ? this._getSize(node) : isSymlinkInode(node) ? node.symlink.length : 0, + /*blksize*/ 4096, + /*blocks*/ 0, + node.atimeMs, + node.mtimeMs, + node.ctimeMs, + node.birthtimeMs, + ); + } + + public scanSync(path: string, axis: Axis, traversal: Traversal) { + path = this._resolve(path); + const results: string[] = []; + this._scan(path, this.statSync(path), axis, traversal, /*noFollow*/ false, results); + return results; + } + + public lscanSync(path: string, axis: Axis, traversal: Traversal) { + path = this._resolve(path); + const results: string[] = []; + this._scan(path, this.statSync(path), axis, traversal, /*noFollow*/ true, results); + return results; + } + + private _scan(path: string, stats: Stats, axis: Axis, traversal: Traversal, noFollow: boolean, results: string[]) { + if (axis === "ancestors-or-self" || axis === "self" || axis === "descendants-or-self") { + if (!traversal.accept || traversal.accept(path, stats)) { + results.push(path); + } + } + if (axis === "ancestors-or-self" || axis === "ancestors") { + const dirname = vpath.dirname(path); + if (dirname !== path) { + try { + const stats = this._stat("scandir", this._walk(dirname, noFollow) || _throw(new IOError("ENOENT", "scandir", dirname))); + if (!traversal.traverse || traversal.traverse(dirname, stats)) { + this._scan(dirname, stats, "ancestors-or-self", traversal, noFollow, results); + } + } + catch { /*ignored*/ } + } + } + if (axis === "descendants-or-self" || axis === "descendants") { + if (stats.isDirectory() && (!traversal.traverse || traversal.traverse(path, stats))) { + for (const file of this.readdirSync(path)) { + try { + const childpath = vpath.combine(path, file); + const stats = this._stat("scandir", this._walk(childpath, noFollow) || _throw(new IOError("ENOENT", "scandir", childpath))); + this._scan(childpath, stats, "descendants-or-self", traversal, noFollow, results); + } + catch { /*ignored*/ } + } + } + } + } + + /** + * Read a directory. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html + */ + public readdir(path: string, callback: (err: Error | null, files: string[] | null) => void) { + try { + process.nextTick(callback, /*e*/ null, this.readdirSync(path)); + } + catch (e) { + process.nextTick(callback, e, /*files*/ null); + } + } + + /** + * Read a directory. If `path` is a symbolic link, it is dereferenced. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html + */ + public readdirSync(path: string) { + path = this._resolve(path); + const { node } = this._find(path) || _throw(new IOError("ENOENT", "readdir", path)); + if (!isDirectoryInode(node)) throw new IOError("ENOTDIR", "readdir", path); + if (!this._access(node, R_OK)) throw new IOError("EACCES", "readdir", path); + return Array.from(this._getLinks(node).keys()); + } + + /** + * Mounts a physical or virtual file system at a location in this virtual file system. + * + * @param source The path in the physical (or other virtual) file system. + * @param target The path in this virtual file system. + * @param resolver An object used to resolve files in `source`. + */ + public mount(source: string, target: string, resolver: FileSystemResolver, callback: (err: Error | null) => void): void; + /** + * Mounts a physical or virtual file system at a location in this virtual file system. + * + * @param source The path in the physical (or other virtual) file system. + * @param target The path in this virtual file system. + * @param resolver An object used to resolve files in `source`. + */ + public mount(source: string, target: string, resolver: FileSystemResolver, mode: number | undefined, callback: (err: Error | null) => void): void; + public mount(source: string, target: string, resolver: FileSystemResolver, modeOrCallback: number | typeof callback, callback?: (err: Error | null) => void) { + let mode: number | undefined; + if (typeof modeOrCallback === "function") callback = modeOrCallback; + else if (typeof modeOrCallback === "number") mode = modeOrCallback; + if (typeof callback !== "function") throw new IOError("EINVAL"); + try { + this.mountSync(source, target, resolver, mode); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Mounts a physical or virtual file system at a location in this virtual file system. + * + * @param source The path in the physical (or other virtual) file system. + * @param target The path in this virtual file system. + * @param resolver An object used to resolve files in `source`. + */ + public mountSync(source: string, target: string, resolver: FileSystemResolver, mode = 0o777) { + source = vpath.validate(source, vpath.ValidationFlags.Absolute); + target = this._resolve(target); + mode = mode & 0o1777; // allows S_ISVTX bit + + let parent: DirectoryInode | undefined; + let name: string; + + // special case for FS root + if (this.stringComparer(vpath.dirname(target), target) === 0) { + if (this.getuid() !== 0) throw new IOError("EPERM", "mount", source, target); + name = target; + } + else { + const entry = this._find(vpath.dirname(target)) || _throw(new IOError("ENOENT", "mount", source, target)); + if (!isDirectoryInode(entry.node)) throw new IOError("ENOTDIR", "mount", source, target); + if (!this._access(entry.node, W_OK)) throw new IOError("EACCES", "mount", source, target); + parent = entry.node; + name = vpath.basename(target); + } + + const links = this._getLinks(parent); + if (links.has(name)) throw new IOError("EEXIST", "mount", source, target); + if (this.isReadonly) throw new IOError("EROFS", "mount", source, target); + + const node = parent + ? this._mknod(parent.dev, S_IFDIR, mode) + : this._mknod(++devCount, S_IFDIR, mode, /*uid*/ 0, /*gid*/ 0, /*umask*/ 0o000); + + node.source = source; + node.resolver = resolver; + + this._addLink(parent, links, name, node); + + if (parent) { + parent.mtimeMs = this.time(); + } + else if (!this._cwd) { + this._cwd = name; + } + } + + /** + * Make a directory. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html + */ + public mkdir(path: string, callback: (error: Error | null) => void): void; + /** + * Make a directory. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html + */ + public mkdir(path: string, mode: number | undefined, callback: (error: Error | null) => void): void; + public mkdir(path: string, modeOrCallback: number | typeof callback, callback?: (error: Error | null) => void) { + let mode: number | undefined; + if (typeof modeOrCallback === "function") callback = modeOrCallback; + else if (typeof modeOrCallback === "number") mode = modeOrCallback; + if (typeof callback !== "function") throw new IOError("EINVAL"); + try { + this.mkdirSync(path, mode); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Make a directory. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html + */ + public mkdirSync(path: string, mode = 0o777) { + path = this._resolve(path); + if (!isFinite(mode)) throw new IOError("EINVAL", "mkdir", path); + mode = mode & 0o1777; // allows S_ISVTX bit + + let parent: DirectoryInode | undefined; + let name: string; + + // special case for FS root + if (this.stringComparer(vpath.dirname(path), path) === 0) { + if (this.getuid() !== 0) throw new IOError("EPERM", "mkdir", path); + name = path; + } + else { + const parentEntry = this._find(vpath.dirname(path)) || _throw(new IOError("ENOENT", "mkdir", path)); + if (!isDirectoryInode(parentEntry.node)) throw new IOError("ENOTDIR", "mkdir", path); + if (!this._access(parentEntry.node, W_OK)) throw new IOError("EACCES", "mkdir", path); + parent = parentEntry.node; + name = vpath.basename(path); + } + + const links = this._getLinks(parent); + if (links.has(name)) throw new IOError("EEXIST", "mkdir", path); + if (this.isReadonly) throw new IOError("EROFS", "mkdir", path); + + const node = parent + ? this._mknod(parent.dev, S_IFDIR, mode) + : this._mknod(++devCount, S_IFDIR, mode, /*uid*/ 0, /*gid*/ 0, /*umask*/ 0o000); + + if (parent && parent.mode & S_ISGID) { + node.mode |= S_ISGID; + node.gid = parent.gid; + } + + this._addLink(parent, links, name, node); + + if (parent) { + parent.mtimeMs = this.time(); + } + else if (!this._cwd) { + this._cwd = name; + } + } + + /** + * Make a directory and all of its parent paths (if they don't exist). + */ + public mkdirp(path: string, callback: (error: Error | null) => void): void; + /** + * Make a directory and all of its parent paths (if they don't exist). + */ + public mkdirp(path: string, mode: number | undefined, callback: (error: Error | null) => void): void; + public mkdirp(path: string, modeOrCallback: number | typeof callback, callback?: (error: Error | null) => void) { + let mode: number | undefined; + if (typeof modeOrCallback === "function") callback = modeOrCallback; + else if (typeof modeOrCallback === "number") mode = modeOrCallback; + if (typeof callback !== "function") throw new IOError("EINVAL"); + try { + this.mkdirpSync(path, mode); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Make a directory and all of its parent paths (if they don't exist). + */ + public mkdirpSync(path: string, mode = 0o777) { + path = this._resolve(path); + try { + this.mkdirSync(path, mode); + } + catch (e) { + if (e.code === "ENOENT") { + this.mkdirpSync(vpath.dirname(path), mode); + this.mkdirSync(path, mode); + } + else if (e.code !== "EEXIST") { + throw e; + } + } + } + + /** + * Remove a directory. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html + */ + public rmdir(path: string, callback: (err: Error | null) => void) { + try { + this.rmdirSync(path); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Remove a directory. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html + */ + public rmdirSync(path: string) { + path = this._resolve(path); + + const { parent, node } = this._find(path) || _throw(new IOError("ENOENT", "rmdir", path)); + if (!isDirectoryInode(node)) throw new IOError("ENOTDIR", "rmdir", path); + if (this._getLinks(node).size !== 0) throw new IOError("ENOTEMPTY", "rmdir", path); + if (!this._access(parent, W_OK)) throw new IOError("EACCES", "mkdir", path); + if (this.isReadonly) throw new IOError("EROFS", "rmdir", path); + + const name = vpath.basename(path); + const links = this._getLinks(parent); + this._removeLink(parent, links, name, node); + + const time = this.time(); + parent.mtimeMs = time; + node.ctimeMs = time; + this._invalidatePaths(node); + } + + /** + * Link one file to another file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html + */ + public link(oldpath: string, newpath: string, callback: (err: Error | null) => void) { + try { + this.linkSync(oldpath, newpath); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Link one file to another file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html + */ + public linkSync(oldpath: string, newpath: string) { + oldpath = this._resolve(oldpath); + newpath = this._resolve(newpath); + + const { node } = this._find(oldpath) || _throw(new IOError("ENOENT", "link", oldpath, newpath)); + if (isDirectoryInode(node)) throw new IOError("EPERM", "link", oldpath, newpath); + + const { node: newParent } = this._find(vpath.dirname(newpath)) || _throw(new IOError("ENOENT", "link", oldpath, newpath)); + if (!isDirectoryInode(newParent)) throw new IOError("ENOTDIR", "link", oldpath, newpath); + + const newParentLinks = this._getLinks(newParent); + const newBasename = vpath.basename(newpath); + + if (newParentLinks.has(newBasename)) throw new IOError("EEXIST", "link", oldpath, newpath); + if (!this._access(newParent, W_OK)) throw new IOError("EACCES", "link", oldpath, newpath); + if (this.isReadonly) throw new IOError("EROFS", "link", oldpath, newpath); + + this._addLink(newParent, newParentLinks, newBasename, node); + + const time = this.time(); + newParent.mtimeMs = time; + node.ctimeMs = time; + this._invalidatePaths(node); + } + + /** + * Remove a directory entry. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html + */ + public unlink(path: string, callback: (err: Error | null) => void) { + try { + this.unlinkSync(path); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Remove a directory entry. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html + */ + public unlinkSync(path: string) { + path = this._resolve(path); + + const { parent, node, basename } = this._lfind(path) || _throw(new IOError("ENOENT", "unlink", path)); + if (isDirectoryInode(node)) throw new IOError("EISDIR", "unlink", path); + if (!this._access(parent, W_OK)) throw new IOError("EACCES", "unlink", path); + if (this.isReadonly) throw new IOError("EROFS", "unlink", path); + + const links = this._getLinks(parent); + this._removeLink(parent, links, basename, node); + + const time = this.time(); + parent.mtimeMs = time; + node.ctimeMs = time; + this._invalidatePaths(node); + } + + /** + * Rename a file + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html + */ + public rename(oldpath: string, newpath: string, callback: (err: Error | null) => void) { + try { + this.renameSync(oldpath, newpath); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Rename a file + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html + */ + public renameSync(oldpath: string, newpath: string) { + oldpath = this._resolve(oldpath); + newpath = this._resolve(newpath); + + const { parent: oldParent, node, basename: oldBasename } = this._lfind(oldpath) || _throw(new IOError("ENOENT", "rename", oldpath, newpath)); + const { node: newParent } = this._find(vpath.dirname(newpath)) || _throw(new IOError("ENOENT", "rename", oldpath, newpath)); + if (!isDirectoryInode(newParent)) throw new IOError("ENOTDIR", "rename", oldpath, newpath); + + const newBasename = vpath.basename(newpath); + const newParentLinks = this._getLinks(newParent); + const existingNode = newParentLinks.get(newBasename); + if (existingNode) { + if (isDirectoryInode(node)) { + if (!isDirectoryInode(existingNode)) throw new IOError("ENOTDIR", "rename", oldpath, newpath); + if (this._getLinks(existingNode).size > 0) throw new IOError("ENOTEMPTY", "rename", oldpath, newpath); + } + else { + if (isDirectoryInode(existingNode)) throw new IOError("EISDIR", "rename", oldpath, newpath); + } + } + + if (!this._access(oldParent, W_OK)) throw new IOError("EACCES", "rename", oldpath, newpath); + if (!this._access(newParent, W_OK)) throw new IOError("EACCES", "rename", oldpath, newpath); + if (this.isReadonly) throw new IOError("EROFS", "rename", oldpath, newpath); + + const time = this.time(); + if (existingNode) { + this._removeLink(newParent, newParentLinks, newBasename, existingNode); + this._invalidatePaths(existingNode); + existingNode.ctimeMs = time; + } + + const oldParentLinks = this._getLinks(oldParent); + this._replaceLink(oldParent, oldParentLinks, oldBasename, newParent, newParentLinks, newBasename, node); + + oldParent.mtimeMs = time; + newParent.mtimeMs = time; + this._invalidatePaths(node); + } + + /** + * Make a symbolic link + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html + */ + public symlink(target: string, linkpath: string, callback: (err: Error | null) => void) { + try { + this.symlinkSync(target, linkpath); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Make a symbolic link + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html + */ + public symlinkSync(target: string, linkpath: string) { + target = vpath.validate(target, vpath.ValidationFlags.RelativeOrAbsolute); + linkpath = this._resolve(linkpath); + + const dirname = vpath.dirname(linkpath); + const { node: parent } = this._find(dirname) || _throw(new IOError("ENOENT", "symlink", target, linkpath)); + if (!isDirectoryInode(parent)) throw new IOError("ENOTDIR", "symlink", target, linkpath); + + const basename = vpath.basename(linkpath); + const parentLinks = this._getLinks(parent); + + if (parentLinks.has(basename)) throw new IOError("EEXIST", "symlink", target, linkpath); + if (!this._access(parent, W_OK)) throw new IOError("EACCES", "symlink", target, linkpath); + if (this.isReadonly) throw new IOError("EROFS", "symlink", target, linkpath); + + const node = this._mknod(parent.dev, S_IFLNK, 0o666); + node.symlink = target; + + this._addLink(parent, parentLinks, basename, node); + + const time = this.time(); + parent.mtimeMs = time; + } + + /** + * Read the contents of a symbolic link + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html + */ + public readlink(path: string, callback: (err: Error | null, path: string | null) => void) { + try { + process.nextTick(callback, /*e*/ null, this.readlinkSync(path)); + } + catch (e) { + process.nextTick(callback, e, /*path*/ null); + } + } + + /** + * Read the contents of a symbolic link + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html + */ + public readlinkSync(path: string) { + path = this._resolve(path); + + const { node } = this._lfind(path) || _throw(new IOError("ENOENT", "readlink", path)); + if (!isSymlinkInode(node)) throw new IOError("EINVAL", "readlink", path); + + return node.symlink; + } + + /** + * Resolve a pathname + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html + */ + public realpath(path: string, callback: (err: Error | null, path: string | null) => void) { + try { + process.nextTick(callback, /*e*/ null, this.realpathSync(path)); + } + catch (e) { + process.nextTick(callback, e, /*path*/ null); + } + } + + /** + * Resolve a pathname + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html + */ + public realpathSync(path: string) { + path = this._resolve(path); + const entry = this._find(path) || _throw(new IOError("ENOENT", "realpath", path)); + return entry.path; + } + + /** + * Open a file + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html + */ + public open(path: string, flags: string | number, callback: (err: Error | null, fd: number | null) => void): void; + /** + * Open a file + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html + */ + public open(path: string, flags: string | number, mode: number, callback: (err: Error | null, fd: number | null) => void): void; + public open(path: string, flags: string | number, modeOrCallback: number | typeof callback, callback?: (err: Error | null, fd: number | null) => void) { + let mode: number | undefined; + if (typeof modeOrCallback === "function") callback = modeOrCallback; + else if (typeof modeOrCallback === "number") mode = modeOrCallback; + if (typeof callback !== "function") throw new IOError("EINVAL"); + try { + process.nextTick(callback, /*e*/ null, this.openSync(path, flags, mode)); + } + catch (e) { + process.nextTick(callback, e, /*fd*/ null); + } + } + + /** + * Open a file + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html + */ + public openSync(path: string, flags: string | number, mode = 0o666): number { + path = this._resolve(path); + flags = parseFlags(flags); + if (!isFinite(flags) || !isFinite(mode)) throw new IOError("EINVAL", "open", path); + + const read = (flags & O_ACCMODE) !== O_WRONLY; + const write = flags & O_ACCMODE && flags & O_CREAT | O_TRUNC; + if (write && this.isReadonly) throw new IOError("EROFS", "open", path); + + const basename = vpath.basename(path); + const entry = this._walk(path, /*noFollow*/ !!(flags & O_NOFOLLOW)); + + let node: Inode; + let parent: Inode; + if (!entry) { + if (~flags & O_CREAT) throw new IOError("ENOENT", "open", path); + if (flags & O_DIRECTORY) throw new IOError("ENOTDIR", "open", path); + flags |= O_TRUNC; + + const entry = this._walk(vpath.dirname(path), /*noFollow*/ false) || _throw(new IOError("ENOENT", "open", path)); + parent = entry.node; + + if (!isDirectoryInode(parent)) throw new IOError("ENOTDIR", "open", path); + if (!this._access(parent, W_OK)) throw new IOError("EACCES", "open", path); + + node = this._mknod(parent.dev, S_IFREG, mode); + node.buffer = Buffer.allocUnsafe(0); + if (parent.mode & S_ISGID) { + node.mode |= S_ISGID; + node.gid = parent.gid; + } + + const links = this._getLinks(parent); + this._addLink(parent, links, basename, node); + + const time = this.time(); + parent.mtimeMs = time; + } + else { + if (flags & O_EXCL) throw new IOError("EEXIST", "open", path); + node = entry.node; + parent = entry.parent; + } + + if (flags & O_DIRECTORY && isFileInode(node)) throw new IOError("ENOTDIR", "open", path); + if (write && isDirectoryInode(node)) throw new IOError("EISDIR", "open", path); + if (write && !this._access(node, W_OK)) throw new IOError("EACCES", "open", path); + if (read && !this._access(node, R_OK)) throw new IOError("EACCES", "open", path); + + const file: OpenFileDescription = { + fd: ++fdCount, + path, + basename, + parent, + node, + flags, + written: false, + offset: isFileInode(node) && (flags & (O_APPEND | O_TRUNC)) === O_APPEND ? this._getSize(node) : 0, + buffer: undefined + }; + + this._openFiles.set(file.fd, file); + + if (flags & O_TRUNC) { + file.buffer = Buffer.allocUnsafe(0); + if (flags & O_SYNC) { + this._fsync(file, /*metadata*/ true); + } + } + + return file.fd; + } + + /** + * Close a file descriptor. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html# + */ + public close(fd: number, callback: (err: Error | null) => void) { + try { + this.closeSync(fd); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Close a file descriptor. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html# + */ + public closeSync(fd: number) { + const entry = this._file("close", fd); + this._openFiles.delete(entry.fd); + this._fsync(entry, /*metadata*/ true); + } + + /** + * Read from a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html + */ + public read(fd: number, buffer: Buffer, offset: number, length: number, position: number | undefined, callback: (err: Error | null, bytesRead: number | null, buffer: Buffer) => void) { + try { + process.nextTick(callback, /*e*/ null, this.readSync(fd, buffer, offset, length, position), buffer); + } + catch (e) { + process.nextTick(callback, e, /*bytesRead*/ null, buffer); + } + } + + /** + * Read from a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html + */ + public readSync(fd: number, buffer: Buffer, offset: number, length: number, position?: number | null) { + if (typeof position !== "number") position = -1; + if (!isFinite(offset) || !isFinite(length) || !isFinite(position)) throw new IOError("EINVAL", "read"); + if (offset < 0 || length < 0 || position < -1 || offset > buffer.byteLength - length) throw new IOError("EINVAL", "read"); + if (length === 0) return 0; + + const entry = this._file("read", fd, "file", R_OK); + const node = entry.node; + if (position !== -1) entry.offset = position; + if (!entry.buffer) entry.buffer = this._getBuffer(node); + const bytesRead = entry.buffer.copy(buffer, offset, entry.offset, entry.offset + length); + entry.offset += bytesRead; + return bytesRead; + } + + /** + * Read from a file. + */ + public readFile(path: string | number, callback: (error: Error | null, data: string | Buffer | null) => void): void; + /** + * Read from a file. + */ + public readFile(path: string | number, options: { encoding?: null, flag?: string | number } | null | undefined, callback: (error: Error | null, data: Buffer | null) => void): void; + /** + * Read from a file. + */ + public readFile(path: string | number, options: { encoding: string, flag?: string | number } | string, callback: (error: Error | null, data: string | null) => void): void; + /** + * Read from a file. + */ + public readFile(path: string | number, options: { encoding?: string | null, flag?: string | number } | string | null | undefined, callback: (error: Error | null, data: string | Buffer | null) => void): void; + public readFile(path: string | number, optionsOrCallback: { encoding?: string | null, flag?: string | number } | string | null | undefined | typeof callback, callback?: ((error: Error | null, data: string | null) => void) | ((error: Error | null, data: Buffer | null) => void)) { + let options: { encoding?: string | null, flag?: string | number } | string | null | undefined; + if (typeof optionsOrCallback === "function") callback = optionsOrCallback; + else options = optionsOrCallback; + if (typeof callback !== "function") throw new IOError("EINVAL"); + try { + process.nextTick(callback, /*e*/ null, this.readFileSync(path, options)); + } + catch (e) { + process.nextTick(callback, e, /*data*/ null); + } + } + + /** + * Read from a file. + */ + public readFileSync(path: string | number, options?: { encoding?: null, flag?: string | number } | null): Buffer; + /** + * Read from a file. + */ + public readFileSync(path: string | number, options: { encoding: string, flag?: string | number } | string): string; + /** + * Read from a file. + */ + public readFileSync(path: string | number, options?: { encoding?: string | null, flag?: string | number } | string | null): string | Buffer; + public readFileSync(path: string | number, options: { encoding?: string | null, flag?: string | number } | string | null = {}) { + if (options === null || typeof options === "string") options = { encoding: options as string }; + const { encoding, flag = "r" } = options; + const fd = typeof path === "number" ? path : this.openSync(path, flag, /*mode*/ 0o666); + const size = this.fstatSync(fd).size; + let buffer = Buffer.allocUnsafe(size); + try { + let offset = 0; + let lastOffset: number | undefined; + while (lastOffset !== offset && offset < size) { + lastOffset = offset; + offset += this.readSync(fd, buffer, offset, size - offset, offset); + } + if (offset < size) buffer = buffer.slice(0, offset); + } + finally { + if (typeof path !== "number") { + this.closeSync(fd); + } + } + return encoding ? buffer.toString(encoding) : buffer; + } + + /** + * Write to a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html + */ + public write(fd: number, buffer: Buffer, callback: (error: Error | null, bytesWritten: number | null, buffer: Buffer) => void): void; + /** + * Write to a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html + */ + public write(fd: number, buffer: Buffer, offset: number | undefined, callback: (error: Error | null, bytesWritten: number | null, buffer: Buffer) => void): void; + /** + * Write to a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html + */ + public write(fd: number, buffer: Buffer, offset: number | undefined, length: number | undefined, callback: (error: Error | null, bytesWritten: number | null, buffer: Buffer) => void): void; + /** + * Write to a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html + */ + public write(fd: number, buffer: Buffer, offset: number | undefined, length: number | undefined, position: number | undefined, callback: (error: Error | null, bytesWritten: number | null, buffer: Buffer) => void): void; + /** + * Write to a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html + */ + public write(fd: number, text: string, callback: (error: Error | null, bytesWritten: number | null, text: string) => void): void; + /** + * Write to a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html + */ + public write(fd: number, text: string, position: number | null | undefined, callback: (error: Error | null, bytesWritten: number | null, text: string) => void): void; + /** + * Write to a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html + */ + public write(fd: number, text: string, position: number | null | undefined, encoding: string | undefined, callback: (error: Error | null, bytesWritten: number | null, text: string) => void): void; + public write(fd: number, buffer: Buffer | string, offset?: number | null | typeof callback, length?: number | string | typeof callback, position?: number | typeof callback, callback?: ((error: Error | null, bytesWritten: number | null, buffer: Buffer) => void) | ((error: Error | null, bytesWritten: number | null, buffer: string) => void)) { + if (typeof offset === "function") callback = offset; + else if (typeof length === "function") callback = length; + else if (typeof position === "function") callback = position; + if (typeof callback !== "function") throw new IOError("EINVAL"); + try { + if (Buffer.isBuffer(buffer)) { + process.nextTick(callback, /*e*/ null, this.writeSync(fd, buffer, typeof offset === "number" ? offset : undefined, typeof length === "number" ? length : undefined, typeof position === "number" ? position : undefined), buffer); + } + else { + process.nextTick(callback, /*e*/ null, this.writeSync(fd, buffer, typeof offset === "number" ? offset : undefined, typeof length === "string" ? length : undefined)); + } + } + catch (e) { + process.nextTick(callback, e, /*bytesWritten*/ null, buffer); + } + } + + /** + * Write to a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html + */ + public writeSync(fd: number, buffer: Buffer, offset?: number, length?: number, position?: number | null): number; + /** + * Write to a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html + */ + public writeSync(fd: number, text: string, position?: number | null, encoding?: string): number; + public writeSync(fd: number, buffer: Buffer | string, offset?: number | null, length?: number | string, position?: number | null) { + if (Buffer.isBuffer(buffer)) { + if (typeof offset !== "number") offset = 0; + if (typeof length !== "number") length = buffer.byteLength - offset; + } + else { + buffer = Buffer.from(buffer, typeof length === "string" ? length : "utf8"); + position = offset; + offset = 0; + length = buffer.byteLength; + } + + if (typeof position !== "number") position = -1; + if (!isFinite(offset) || !isFinite(length) || !isFinite(position)) throw new IOError("EINVAL", "write"); + if (offset < 0 || length < 0 || position < -1 || offset > buffer.byteLength - length) throw new IOError("EINVAL", "write"); + if (length === 0) return; + + const entry = this._file("write", fd, "file", W_OK); + const node = entry.node; + if (position !== -1) entry.offset = position; + if (!entry.buffer) { + // if we haven't yet started writing, get a copy of the storage buffer + const buffer = this._getBuffer(node); + entry.buffer = Buffer.allocUnsafe(buffer.byteLength); + buffer.copy(entry.buffer, 0, 0, buffer.byteLength); + } + if (entry.offset >= entry.buffer.byteLength - length) { + // if we are writing to a point outside of the size of the buffer, resize it + entry.buffer = resizeBuffer(entry.buffer, entry.offset + length); + } + const bytesWritten = buffer.copy(entry.buffer, entry.offset, offset, offset + length); + entry.offset += bytesWritten; + if (entry.flags & O_SYNC) { + this._fsync(entry, /*metadata*/ true); + } + return bytesWritten; + } + + /** + * Append to a file. + */ + public appendFile(path: string | number, data: string | Buffer, callback: (error: Error | null) => void): void; + /** + * Append to a file. + */ + public appendFile(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null | undefined, callback: (error: Error | null) => void): void; + public appendFile(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null | typeof callback | undefined, callback?: (error: Error | null) => void) { + if (typeof options === "function") callback = options; + if (typeof callback !== "function") throw new IOError("EINVAL"); + try { + this.appendFileSync(path, data, typeof options !== "function" ? options : undefined); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Append to a file. + */ + public appendFileSync(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null = {}) { + if (options === null) options = { encoding: null }; + else if (typeof options === "string") options = { encoding: options }; + const { encoding, mode = 0o666, flag = "a" } = options; + this.writeFileSync(path, data, { encoding, mode, flag }); + } + + /** + * Write to a file. + */ + public writeFile(path: string | number, data: string | Buffer, callback: (error: Error | null) => void): void; + /** + * Write to a file. + */ + public writeFile(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null | undefined, callback: (error: Error | null) => void): void; + public writeFile(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null | typeof callback | undefined, callback?: (error: Error | null) => void) { + if (typeof options === "function") callback = options; + if (typeof callback !== "function") throw new IOError("EINVAL"); + try { + this.writeFileSync(path, data, typeof options !== "function" ? options : undefined); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Write to a file. + */ + public writeFileSync(path: string | number, data: string | Buffer, options: { encoding?: string | null, mode?: number, flag?: string | number } | string | null = {}) { + if (options === null) options = { encoding: null }; + else if (typeof options === "string") options = { encoding: options }; + const { encoding, mode = 0o666, flag = "w" } = options; + const flags = parseFlags(flag); + const fd = typeof path === "number" ? path : this.openSync(path, flags, mode); + const buffer = Buffer.isBuffer(data) ? data : Buffer.from("" + data, encoding || "utf8"); + try { + let offset = 0; + while (offset < buffer.byteLength) { + offset += this.writeSync(fd, buffer, offset, buffer.byteLength - offset, flags & O_APPEND ? null : offset); + } + } + finally { + if (typeof path !== "number") { + this.closeSync(fd); + } + } + } + + /** + * Truncate a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html + */ + public truncate(path: string, callback: (error: Error | null) => void): void; + /** + * Truncate a file to a specified length. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html + */ + public truncate(path: string, length: number | undefined, callback: (error: Error | null) => void): void; + public truncate(path: string, length: number | typeof callback | undefined, callback?: (error: Error | null) => void) { + if (typeof length === "function") callback = length; + if (typeof callback !== "function") throw new IOError("EINVAL"); + try { + this.truncateSync(path, typeof length !== "function" ? length : undefined); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Truncate a file to a specified length. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html + */ + public truncateSync(path: string, length = 0) { + path = this._resolve(path); + this._truncate("truncate", this._find(path) || _throw(new IOError("ENOENT", "truncate", path)), length); + } + + /** + * Truncate a file. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html + */ + public ftruncate(fd: number, callback: (error: Error | null) => void): void; + /** + * Truncate a file to a specified length. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html + */ + public ftruncate(fd: number, length: number | undefined, callback: (error: Error | null) => void): void; + public ftruncate(fd: number, length: number | typeof callback | undefined, callback?: (error: Error | null) => void) { + if (typeof length === "function") callback = length, length = undefined; + if (typeof callback !== "function") throw new IOError("EINVAL"); + try { + this.ftruncateSync(fd, typeof length !== "function" ? length : undefined); + process.nextTick(callback, /*e*/ null); + } + catch (e) { + process.nextTick(callback, e); + } + } + + /** + * Truncate a file to a specified length. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html + */ + public ftruncateSync(fd: number, length = 0) { + this._truncate("ftruncate", this._file("ftruncate", fd, "file", W_OK), length); + } + + private _truncate(syscall: string, entry: FileDescription, length: number, path?: string) { + if (!isFinite(length)) throw new IOError("EINVAL", syscall, path); + if (!isFileInode(entry.node)) throw new IOError("ENOENT", syscall, path); + if (this.isReadonly) throw new IOError("EROFS", syscall, path); + + if (this._getSize(entry.node) !== length) { + this._resize(entry.node, entry.node, length); + } + + entry.node.mtimeMs = this.time(); + } + + /** + * Makes a temporary directory. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdtemp.html + */ + public mkdtemp(template: string, callback: (error: Error | null, folder: string | null) => void) { + try { + process.nextTick(callback, /*e*/ null, this.mkdtempSync(template)); + } + catch (e) { + process.nextTick(callback, e, /*folder*/ null); + } + } + + /** + * Makes a temporary directory. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdtemp.html + */ + public mkdtempSync(template: string) { + this.mkdirSync(this._mktemp("mkdtemp", template)); + } + + /** + * Makes a temporary file, returning a file descriptor. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html + */ + public mkstemp(template: string, callback: (error: Error | null, fd: number | null) => void) { + try { + process.nextTick(callback, /*e*/ null, this.mkstempSync(template)); + } + catch (e) { + process.nextTick(callback, e, /*fd*/ null); + } + } + + /** + * Makes a temporary file, returning a file descriptor. + * + * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html + */ + public mkstempSync(template: string) { + return this.openSync(this._mktemp("mkstemp", template), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + } + + private _mktemp(syscall: string, template: string) { + if (this.isReadonly) throw new IOError("EROFS", syscall, template); + + template = this._resolve(template); + if (vpath.hasTrailingSeparator(template)) throw new IOError("EINVAL", syscall, template); + + const basename = vpath.basename(template); + let count = 0; + for (let i = basename.length - 1; i >= 0; i--) { + if (basename.charAt(i) !== "X") break; + count++; + } + if (count < 6) throw new IOError("EINVAL", syscall, template); + + const { node: parent, path } = this._find(vpath.dirname(template)) || _throw(new IOError("ENOENT", syscall, template)); + if (!isDirectoryInode(parent)) throw new IOError("ENOTDIR", syscall, template); + if (!this._access(parent, W_OK)) throw new IOError("EACCES", syscall, template); + + const parentLinks = this._getLinks(parent); + const prefix = basename.slice(0, basename.length - count); + while (true) { + let suffix = ""; + while (suffix.length < count) { + suffix += FileSystem._portableFilenameCharSet.charAt(Math.floor(Math.random() * FileSystem._portableFilenameCharSet.length)); + } + const name = prefix + suffix; + if (!parentLinks.has(name)) return vpath.combine(path, name); + } + } + + public debugPrint(): void { + let result = ""; + const printLinks = (dirname: string | undefined, links: core.SortedMap) => { + const iterator = core.getIterator(links); + try { + for (let i = core.nextResult(iterator); i; i = core.nextResult(iterator)) { + const [name, node] = i.value; + const path = dirname ? vpath.combine(dirname, name) : name; + const marker = vpath.compare(this._cwd, path, this.ignoreCase) === 0 ? "*" : " "; + if (result) result += "\n"; + result += marker; + if (isDirectoryInode(node)) { + result += vpath.addTrailingSeparator(path); + printLinks(path, this._getLinks(node)); + } + else if (isFileInode(node)) { + result += path; + } + else if (isSymlinkInode(node)) { + result += path + " -> " + node.symlink; + } + } + } + finally { + core.closeIterator(iterator); + } + }; + printLinks(/*dirname*/ undefined, this._getRootLinks()); + console.log(result); + } + + private _mknod(dev: number, type: typeof S_IFREG, mode: number, uid?: number, gid?: number, umask?: number): FileInode; + private _mknod(dev: number, type: typeof S_IFDIR, mode: number, uid?: number, gid?: number, umask?: number): DirectoryInode; + private _mknod(dev: number, type: typeof S_IFLNK, mode: number, uid?: number, gid?: number, umask?: number): SymlinkInode; + private _mknod(dev: number, type: number, mode: number, uid = this.getuid(), gid = this.getgid(), umask = this.umask()) { + const timestamp = this.time(); + return { + dev, + ino: ++inoCount, + mode: (mode & ~S_IFMT & ~umask & 0o7777) | (type & S_IFMT), + uid, + gid, + atimeMs: timestamp, + mtimeMs: timestamp, + ctimeMs: timestamp, + birthtimeMs: timestamp, + nlink: 0, + incomingLinks: new Map>(), + }; + } + + private _addLink(parent: DirectoryInode | undefined, links: core.SortedMap, name: string, node: Inode) { + links.set(name, node); + node.nlink++; + + let set = node.incomingLinks.get(parent); + if (!set) node.incomingLinks.set(parent, set = new core.SortedSet(this.stringComparer)); + set.add(name); + } + + private _removeLink(parent: DirectoryInode | undefined, links: core.SortedMap, name: string, node: Inode) { + links.delete(name); + node.nlink--; + + const set = node.incomingLinks.get(parent); + if (set) { + set.delete(name); + if (set.size === 0) node.incomingLinks.delete(parent); + } + } + + private _replaceLink(oldParent: DirectoryInode, oldLinks: core.SortedMap, oldName: string, newParent: DirectoryInode, newLinks: core.SortedMap, newName: string, node: Inode) { + if (oldParent !== newParent) { + this._removeLink(oldParent, oldLinks, oldName, node); + this._addLink(newParent, newLinks, newName, node); + return; + } + + oldLinks.delete(oldName); + oldLinks.set(newName, node); + + const set = node.incomingLinks.get(oldParent); + if (set) { + set.delete(oldName); + set.add(newName); + } + } + + private _getRootLinks() { + if (!this._lazy.links) { + this._lazy.links = new core.SortedMap(this.stringComparer); + if (this._shadowRoot) { + this._copyShadowLinks(this._shadowRoot._getRootLinks(), this._lazy.links); + } + this._lazy.links = this._lazy.links; + } + return this._lazy.links; + } + + private _getLinks(node: DirectoryInode | undefined) { + if (!node) return this._getRootLinks(); + if (!node.links) { + const links = new core.SortedMap(this.stringComparer); + const { source, resolver } = node; + if (source && resolver) { + node.source = undefined; + node.resolver = undefined; + for (const name of resolver.readdirSync(source)) { + const path = vpath.combine(source, name); + const stats = resolver.statSync(path); + switch (stats.mode & S_IFMT) { + case S_IFDIR: + const dir = this._mknod(node.dev, S_IFDIR, 0o777); + dir.source = vpath.combine(source, name); + dir.resolver = resolver; + this._addLink(node, links, name, dir); + break; + case S_IFREG: + const file = this._mknod(node.dev, S_IFREG, 0o666); + file.source = vpath.combine(source, name); + file.resolver = resolver; + file.size = stats.size; + this._addLink(node, links, name, file); + break; + } + } + } + else if (this._shadowRoot && node.shadowRoot) { + this._copyShadowLinks(this._shadowRoot._getLinks(node.shadowRoot), links); + } + node.links = links; + } + return node.links; + } + + private _getShadow(root: DirectoryInode): DirectoryInode; + private _getShadow(root: Inode): Inode; + private _getShadow(root: Inode) { + const shadows = this._lazy.shadows || (this._lazy.shadows = new Map()); + + let shadow = shadows.get(root.ino); + if (!shadow) { + shadow = { + dev: root.dev, + ino: root.ino, + mode: root.mode, + uid: root.uid, + gid: root.gid, + atimeMs: root.atimeMs, + mtimeMs: root.mtimeMs, + ctimeMs: root.ctimeMs, + birthtimeMs: root.birthtimeMs, + nlink: root.nlink, + shadowRoot: root, + incomingLinks: new Map>(), + paths: root.paths + }; + + if (isSymlinkInode(root)) (shadow).symlink = root.symlink; + shadows.set(shadow.ino, shadow); + + const iterator = core.getIterator(root.incomingLinks); + try { + for (let i = core.nextResult(iterator); i; i = core.nextResult(iterator)) { + const [rootParent, rootNames] = i.value; + shadow.incomingLinks.set(rootParent && this._getShadow(rootParent), new core.SortedSet(this.stringComparer, rootNames)); + } + } + finally { + core.closeIterator(iterator); + } + } + + return shadow; + } + + private _copyShadowLinks(source: ReadonlyMap, target: core.SortedMap) { + const iterator = core.getIterator(source); + try { + for (let i = core.nextResult(iterator); i; i = core.nextResult(iterator)) { + const [name, root] = i.value; + target.set(name, this._getShadow(root)); + } + } + finally { + core.closeIterator(iterator); + } + } + + private _invalidatePaths(node: Inode) { + node.paths = undefined; + if (isDirectoryInode(node)) { + const iterator = core.getIterator(this._getLinks(node).values()); + try { + for (let i = core.nextResult(iterator); i; i = core.nextResult(iterator)) { + const child = i.value; + this._invalidatePaths(child); + } + } + finally { + core.closeIterator(iterator); + } + } + } + + private _getSize(node: FileInode): number { + if (node.buffer) return node.buffer.byteLength; + if (node.size !== undefined) return node.size; + if (node.source && node.resolver) return node.size = node.resolver.statSync(node.source).size; + if (this._shadowRoot && node.shadowRoot) return node.size = this._shadowRoot._getSize(node.shadowRoot); + return 0; + } + + private _getBuffer(node: FileInode): Buffer { + if (!node.buffer) { + const { source, resolver } = node; + if (source && resolver) { + node.source = undefined; + node.resolver = undefined; + node.size = undefined; + node.buffer = resolver.readFileSync(source); + } + else if (this._shadowRoot && node.shadowRoot) { + node.buffer = this._shadowRoot._getBuffer(node.shadowRoot); + } + else { + node.buffer = Buffer.allocUnsafe(0); + } + } + return node.buffer; + } + + private _file(syscall: string, fd: number): OpenFileDescription; + private _file(syscall: string, fd: number, kind: "directory"): OpenFileDescription; + private _file(syscall: string, fd: number, kind: "file", mode?: number): OpenFileDescription; + private _file(syscall: string, fd: number, kind?: "file" | "directory", mode: number = F_OK) { + const entry = this._openFiles.get(fd); + if (!entry) throw new IOError("EBADF", syscall); + if (kind === "file" && isDirectoryInode(entry.node)) throw new IOError("EISDIR", syscall); + if (kind === "file" && !isFileInode(entry.node)) throw new IOError("EBADF", syscall); + if (kind === "directory" && !isDirectoryInode(entry.node)) throw new IOError("EBADF", syscall); + if (mode & W_OK && !isWritable(entry)) throw new IOError("EBADF", syscall); + if (mode & R_OK && !isReadable(entry)) throw new IOError("EBADF", syscall); + return entry; + } + + private _find(path: string): FileDescription | undefined { + return this._walk(path, /*noFollow*/ false); + } + + private _lfind(path: string): FileDescription | undefined { + return this._walk(path, /*noFollow*/ true); + } + + // http://man7.org/linux/man-pages/man7/path_resolution.7.html + private _walk(path: string, noFollow: boolean): FileDescription | undefined { + let links: core.SortedMap = this._getRootLinks(); + let parent: DirectoryInode | undefined; + let components = vpath.parse(path); + let step = 0; + let depth = 0; + while (step < components.length) { + if (depth >= 40) throw new IOError("ELOOP", "scandir", vpath.format(components.slice(0, step))); + + const lastStep = step === components.length - 1; + const basename = components[step]; + const node = links.get(basename); + if (node === undefined) return undefined; + + if (isSymlinkInode(node) && !(noFollow && lastStep)) { + const dirname = vpath.format(components.slice(0, step)); + const symlink = vpath.resolve(dirname, node.symlink); + if (!vpath.isAbsolute(symlink)) throw new Error("Path not absolute"); + + links = this._getRootLinks(); + parent = undefined; + components = vpath.parse(symlink).concat(components.slice(step + 1)); + step = 0; + depth++; + continue; + } + + if (lastStep) { + const path = vpath.format(components); + if (!parent && isDirectoryInode(node)) parent = node; + if (!parent) throw new IOError("ENOENT", "scandir", path); + return { path, basename, parent, node }; + } + + if (isDirectoryInode(node)) { + if (!this._access(node, X_OK)) throw new IOError("EACCES", "scandir", path); + links = this._getLinks(node); + parent = node; + step++; + continue; + } + + throw new IOError("ENOTDIR", "scandir", vpath.format(components.slice(0, step + 1))); + } + + return undefined; + } + + private _resize(node: FileInode, entry: { buffer: Buffer | undefined }, size: number) { + if (!entry.buffer) { + entry.buffer = this._getBuffer(node); + } + const oldSize = entry.buffer.byteLength; + if (entry.buffer.byteLength !== size) { + const oldBuffer = entry.buffer; + entry.buffer = size < oldSize ? Buffer.allocUnsafe(size) : Buffer.alloc(size); + oldBuffer.copy(entry.buffer, 0, 0, Math.min(oldSize, size)); + } + } + + private _resolve(path: string) { + return this._cwd + ? vpath.resolve(this._cwd, vpath.validate(path, vpath.ValidationFlags.RelativeOrAbsolute)) + : vpath.validate(path, vpath.ValidationFlags.Absolute); + } + + private _applyFiles(files: FileSet, dirname: string) { + const deferred: [Symlink | Link | Mount, string][] = []; + this._applyFilesWorker(files, dirname, deferred); + for (const [entry, path] of deferred) { + this.mkdirpSync(vpath.dirname(path), 0o777); + this.pushd(vpath.dirname(path)); + if (entry instanceof Symlink) { + if (this.stringComparer(vpath.dirname(path), path) === 0) { + throw new TypeError("Roots cannot be symbolic links."); + } + this.symlinkSync(entry.symlink, path); + this._applyFileExtendedOptions(path, entry); + } + else if (entry instanceof Link) { + if (this.stringComparer(vpath.dirname(path), path) === 0) { + throw new TypeError("Roots cannot be hard links."); + } + this.linkSync(entry.path, path); + } + else { + this.mountSync(entry.source, path, entry.resolver); + this._applyFileExtendedOptions(path, entry); + } + this.popd(); + } + } + + private _applyFileExtendedOptions(path: string, entry: Directory | File | Symlink | Mount) { + const { uid = -1, gid = -1, mode, meta } = entry; + if (uid !== -1 || gid !== -1) this.chownSync(path, uid, gid); + if (mode !== undefined) this.chmodSync(path, mode); + if (meta !== undefined) { + const filemeta = this.filemeta(path); + for (const key of Object.keys(meta)) { + filemeta.set(key, meta[key]); + } + } + } + + private _applyFilesWorker(files: FileSet, dirname: string, deferred: [Symlink | Link | Mount, string][]) { + for (const key of Object.keys(files)) { + const value = this._normalizeFileMapEntry(files[key]); + const path = dirname ? vpath.resolve(dirname, key) : key; + vpath.validate(path, vpath.ValidationFlags.Absolute); + if (value === null || value === undefined) { + if (this.stringComparer(vpath.dirname(path), path) === 0) { + throw new TypeError("Roots cannot be deleted."); + } + this.rimrafSync(path); + } + else if (value instanceof File) { + if (this.stringComparer(vpath.dirname(path), path) === 0) { + throw new TypeError("Roots cannot be files."); + } + this.mkdirpSync(vpath.dirname(path), 0o777); + this.writeFileSync(path, value.data, value.encoding); + this._applyFileExtendedOptions(path, value); + } + else if (value instanceof Directory) { + this.mkdirpSync(path, 0o777); + this._applyFileExtendedOptions(path, value); + this._applyFilesWorker(value.files, path, deferred); + } + else { + deferred.push([value as Symlink | Link | Mount, path]); + } + } + } + + private _normalizeFileMapEntry(value: FileSet[string]) { + if (value === undefined || + value === null || + value instanceof Directory || + value instanceof File || + value instanceof Link || + value instanceof Symlink || + value instanceof Mount) { + return value; + } + return typeof value === "string" || Buffer.isBuffer(value) ? new File(value) : new Directory(value); } } -} \ No newline at end of file + + export interface FileSystemOptions { + uid?: number; + gid?: number; + umask?: number; + time?: number | Date | (() => number | Date); + files?: FileSet; + cwd?: string; + meta?: Record; + } + + export type Axis = "ancestors" | "ancestors-or-self" | "self" | "descendants-or-self" | "descendants"; + + export interface Traversal { + traverse?(path: string, stats: Stats): boolean; + accept?(path: string, stats: Stats): boolean; + } + + export interface FileSystemResolver { + statSync(path: string): { mode: number; size: number; }; + readdirSync(path: string): string[]; + readFileSync(path: string): Buffer; + } + + export interface FileSystemTimers { + setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; + clearInterval(handle: any): void; + } + + export class Stats { + public dev: number; + public ino: number; + public mode: number; + public nlink: number; + public uid: number; + public gid: number; + public rdev: number; + public size: number; + public blksize: number; + public blocks: number; + public atimeMs: number; + public mtimeMs: number; + public ctimeMs: number; + public birthtimeMs: number; + public atime: Date; + public mtime: Date; + public ctime: Date; + public birthtime: Date; + + constructor(); + constructor(dev: number, ino: number, mode: number, nlink: number, uid: number, gid: number, rdev: number, size: number, blksize: number, blocks: number, atimeMs: number, mtimeMs: number, ctimeMs: number, birthtimeMs: number); + constructor(dev = 0, ino = 0, mode = 0, nlink = 0, uid = 0, gid = 0, rdev = 0, size = 0, blksize = 0, blocks = 0, atimeMs = 0, mtimeMs = 0, ctimeMs = 0, birthtimeMs = 0) { + this.dev = dev; + this.ino = ino; + this.mode = mode; + this.nlink = nlink; + this.uid = uid; + this.gid = gid; + this.rdev = rdev; + this.size = size; + this.blksize = blksize; + this.blocks = blocks; + this.atimeMs = atimeMs; + this.mtimeMs = mtimeMs; + this.ctimeMs = ctimeMs; + this.birthtimeMs = birthtimeMs; + this.atime = new Date(this.atimeMs); + this.mtime = new Date(this.mtimeMs); + this.ctime = new Date(this.ctimeMs); + this.birthtime = new Date(this.birthtimeMs); + } + + public isFile() { return (this.mode & S_IFMT) === S_IFREG; } + public isDirectory() { return (this.mode & S_IFMT) === S_IFDIR; } + public isSymbolicLink() { return (this.mode & S_IFMT) === S_IFLNK; } + public isBlockDevice() { return (this.mode & S_IFMT) === S_IFBLK; } + public isCharacterDevice() { return (this.mode & S_IFMT) === S_IFCHR; } + public isFIFO() { return (this.mode & S_IFMT) === S_IFIFO; } + public isSocket() { return (this.mode & S_IFMT) === S_IFSOCK; } + } + + // tslint:disable-next-line:variable-name + export const IOErrorMessages = Object.freeze({ + EACCES: "access denied", + EIO: "an I/O error occurred", + ENOENT: "no such file or directory", + EEXIST: "file already exists", + ELOOP: "too many symbolic links encountered", + ENOTDIR: "no such directory", + EISDIR: "path is a directory", + EBADF: "invalid file descriptor", + EINVAL: "invalid value", + ENOTEMPTY: "directory not empty", + EPERM: "operation not permitted", + EROFS: "file system is read-only" + }); + + export class IOError extends Error { + public readonly code: string; + public readonly syscall: string | undefined; + public readonly path: string | undefined; + public readonly dest: string | undefined; + + constructor(code: keyof typeof IOErrorMessages, syscall?: string, path?: string, dest?: string) { + let message = `${code}: ${IOErrorMessages[code]}`; + if (syscall !== undefined) { + message += `, ${syscall}`; + if (path !== undefined) { + message += ` '${path}'`; + if (dest !== undefined) { + message += ` -> '${dest}'`; + } + } + } + super(message); + this.name = "Error"; + this.code = code; + this.syscall = syscall; + this.path = path; + this.dest = dest; + } + } + + export interface FileSet { + [name: string]: DirectoryLike | FileLike | Link | Symlink | Mount | null | undefined; + } + + export type DirectoryLike = FileSet | Directory; + export type FileLike = File | Buffer | string; + + /** Extended options for a directory in a `FileMap` */ + export class Directory { + public readonly files: FileSet; + public readonly uid: number | undefined; + public readonly gid: number | undefined; + public readonly mode: number | undefined; + public readonly meta: Record | undefined; + constructor(files: FileSet, { uid, gid, mode, meta }: { uid?: number, gid?: number, mode?: number, meta?: Record } = {}) { + this.files = files; + this.uid = uid; + this.gid = gid; + this.mode = mode; + this.meta = meta; + } + } + + /** Extended options for a file in a `FileMap` */ + export class File { + public readonly data: Buffer | string; + public readonly encoding: string | undefined; + public readonly uid: number | undefined; + public readonly gid: number | undefined; + public readonly mode: number | undefined | undefined; + public readonly meta: Record | undefined; + constructor(data: Buffer | string, { uid, gid, mode, meta, encoding }: { encoding?: string, uid?: number, gid?: number, mode?: number, meta?: Record } = {}) { + this.data = data; + this.encoding = encoding; + this.uid = uid; + this.gid = gid; + this.mode = mode; + this.meta = meta; + } + } + + /** Extended options for a hard link in a `FileMap` */ + export class Link { + public readonly path: string; + constructor(path: string) { + this.path = path; + } + } + + /** Extended options for a symbolic link in a `FileMap` */ + export class Symlink { + public readonly symlink: string; + public readonly uid: number | undefined; + public readonly gid: number | undefined; + public readonly mode: number | undefined; + public readonly meta: Record | undefined; + constructor(symlink: string, { uid, gid, mode, meta }: { uid?: number, gid?: number, mode?: number, meta?: Record } = {}) { + this.symlink = symlink; + this.uid = uid; + this.gid = gid; + this.mode = mode; + this.meta = meta; + } + } + + /** Extended options for mounting a virtual copy of an external file system via a `FileMap` */ + export class Mount { + public readonly source: string; + public readonly resolver: FileSystemResolver; + public readonly uid: number | undefined; + public readonly gid: number | undefined; + public readonly mode: number | undefined; + public readonly meta: Record | undefined; + constructor(source: string, resolver: FileSystemResolver, { uid, gid, mode, meta }: { uid?: number, gid?: number, mode?: number, meta?: Record } = {}) { + this.source = source; + this.resolver = resolver; + this.uid = uid; + this.gid = gid; + this.mode = mode; + this.meta = meta; + } + } + + // a generic POSIX inode + type Inode = FileInode | DirectoryInode | SymlinkInode; + + interface InodeBase { + // inode + dev: number; // device id + ino: number; // inode id + mode: number; // file mode + uid: number; // owner user id + gid: number; // owner group id + atimeMs: number; // access time + mtimeMs: number; // modified time + ctimeMs: number; // status change time + birthtimeMs: number; // creation time + nlink: number; // number of hard links + + // extra + shadowRoot: Inode | undefined; + incomingLinks: Map>; + paths?: ReadonlyArray; + meta?: core.Metadata; // metadata stored on the inode + } + + interface FileInode extends InodeBase { + // file inode + size: number | undefined; + buffer: Buffer; + source: string | undefined; + resolver: FileSystemResolver | undefined; + shadowRoot: FileInode | undefined; + } + + interface DirectoryInode extends InodeBase { + // directory inode + links: core.SortedMap | undefined; + source: string | undefined; + resolver: FileSystemResolver | undefined; + shadowRoot: DirectoryInode | undefined; + } + + interface SymlinkInode extends InodeBase { + // symlink inode + symlink: string; + shadowRoot: SymlinkInode | undefined; + } + + function isFileInode(node: Inode): node is FileInode { + return (node.mode & S_IFMT) === S_IFREG; + } + + function isDirectoryInode(node: Inode): node is DirectoryInode { + return (node.mode & S_IFMT) === S_IFDIR; + } + + function isSymlinkInode(node: Inode): node is SymlinkInode { + return (node.mode & S_IFMT) === S_IFLNK; + } + + interface FileDescription { + path: string; + basename: string; + parent: DirectoryInode; + node: TInode; + } + + interface OpenFileDescription extends FileDescription { + fd: number; + flags: number; + offset: number; + written: boolean; + buffer: Buffer | undefined; + } + + function isReadable(file: OpenFileDescription) { + return (file.flags & O_ACCMODE) !== O_WRONLY; + } + + function isWritable(file: OpenFileDescription) { + return (file.flags & O_ACCMODE) !== O_RDONLY; + } + + function parseFlags(flags: string | number) { + if (typeof flags === "string") { + switch (flags) { + case "r": return O_RDONLY; + case "r+": return O_RDWR; + case "rs+": return O_RDWR; + case "w": return O_WRONLY | O_TRUNC | O_CREAT; + case "wx": return O_WRONLY | O_TRUNC | O_CREAT | O_EXCL; + case "w+": return O_RDWR | O_TRUNC | O_CREAT; + case "wx+": return O_RDWR | O_TRUNC | O_CREAT | O_EXCL; + case "a": return O_WRONLY | O_APPEND | O_CREAT; + case "ax": return O_WRONLY | O_APPEND | O_CREAT | O_EXCL; + case "a+": return O_RDWR | O_APPEND | O_CREAT; + case "ax+": return O_RDWR | O_APPEND | O_CREAT | O_EXCL; + default: throw new Error(`Unrecognized file open flag: ${flags}`); + } + } + return flags; + } + + function resizeBuffer(buffer: Buffer, size: number) { + if (buffer.byteLength === size) return buffer; + const newBuffer = Buffer.allocUnsafe(size); + newBuffer.fill(0, buffer.copy(newBuffer)); + return newBuffer; + } +} +// tslint:enable:no-null-keyword \ No newline at end of file diff --git a/src/harness/vfsutils.ts b/src/harness/vfsutils.ts index 2f5680e6a23..0174b07f980 100644 --- a/src/harness/vfsutils.ts +++ b/src/harness/vfsutils.ts @@ -1,6 +1,6 @@ /// /// -/// +/// /// /// /// @@ -253,81 +253,6 @@ namespace vfsutils { fs.writeFileSync(path, writeByteOrderMark ? core.addUTF8ByteOrderMark(content) : content); } - export function watchFile(fs: vfs.FileSystem, path: string, callback: ts.FileWatcherCallback): ts.FileWatcher { - path = vpath.resolve(fs.cwd(), path); - - let prevStats = getStats(fs, path) || new vfs.Stats(); - let exists = fileExists(fs, path); - return fsWatch(fs, path, fileExists, () => { - const currStats = getStats(fs, path) || new vfs.Stats(); - const currTime = currStats.mtimeMs; - const prevTime = prevStats.mtimeMs; - - const eventKind = currTime !== 0 && prevTime === 0 ? ts.FileWatcherEventKind.Created : - currTime === 0 && prevTime !== 0 ? ts.FileWatcherEventKind.Deleted : - ts.FileWatcherEventKind.Changed; - - if (eventKind === ts.FileWatcherEventKind.Created && !exists) { - // file was created (via open()) but hasn't yet been written (via write()) - exists = true; - return; - } - - if (eventKind === ts.FileWatcherEventKind.Changed && currTime <= prevTime) { - // no change - return; - } - - exists = eventKind !== ts.FileWatcherEventKind.Deleted; - prevStats = currStats; - callback(path, eventKind); - }); - } - - export function watchDirectory(fs: vfs.FileSystem, path: string, callback: ts.DirectoryWatcherCallback, recursive?: boolean): ts.FileWatcher { - path = vpath.resolve(fs.cwd(), path); - return fsWatch(fs, path, directoryExists, (eventType, filename) => { - if (eventType === "rename") callback(filename ? vpath.resolve(path, filename) : path); - }, recursive); - } - - function fsWatch(fs: vfs.FileSystem, path: string, exists: (fs: vfs.FileSystem, path: string) => boolean, callback: (eventType: string, filename: string) => void, recursive?: boolean): ts.FileWatcher { - let watcher = !exists(fs, path) ? - watchMissing() : - watchPresent(); - - return { - close() { - watcher.close(); - } - }; - - function watchPresent(): ts.FileWatcher { - const dirWatcher = fs.watch(path, { recursive }, callback); - dirWatcher.on("error", () => { - if (!exists(fs, path)) { - watcher.close(); - watcher = watchMissing(); - callback("rename", ""); - } - }); - return dirWatcher; - } - - function watchMissing(): ts.FileWatcher { - const dirname = vpath.dirname(path); - if (dirname === path) throw new Error("ENOENT: path not found."); - const basename = vpath.basename(path); - return fsWatch(fs, dirname, directoryExists, (eventType, filename) => { - if (eventType === "rename" && fs.stringComparer(basename, filename) === 0 && exists(fs, path)) { - watcher.close(); - watcher = watchPresent(); - callback("rename", ""); - } - }); - } - } - const typeScriptExtensions: ReadonlyArray = [".ts", ".tsx"]; export function isTypeScript(path: string) { diff --git a/src/harness/vpath.ts b/src/harness/vpath.ts index cc26169d270..e8f0f2b126c 100644 --- a/src/harness/vpath.ts +++ b/src/harness/vpath.ts @@ -1,63 +1,437 @@ -// NOTE: This namespace re-exports all of the exports from the @typescript/vfs-path private package. - +/// +/// namespace vpath { - const _vpath = require("@typescript/vfs-path") as typeof vpath; - // tslint:disable:no-unnecessary-qualifier - vpath.sep = _vpath.sep; - vpath.normalizeSeparators = _vpath.normalizeSeparators; - (vpath).ValidationFlags = (_vpath).ValidationFlags; - vpath.valid = _vpath.valid; - vpath.validate = _vpath.validate; - vpath.isAbsolute = _vpath.isAbsolute; - vpath.hasTrailingSeparator = _vpath.hasTrailingSeparator; - vpath.addTrailingSeparator = _vpath.addTrailingSeparator; - vpath.removeTrailingSeparator = _vpath.removeTrailingSeparator; - vpath.normalize = _vpath.normalize; - vpath.combine = _vpath.combine; - vpath.resolve = _vpath.resolve; - vpath.relative = _vpath.relative; - vpath.compareCaseSensitive = _vpath.compareCaseSensitive; - vpath.compareCaseInsensitive = _vpath.compareCaseInsensitive; - vpath.compare = _vpath.compare; - vpath.equals = _vpath.equals; - vpath.beneath = _vpath.beneath; - vpath.parse = _vpath.parse; - vpath.format = _vpath.format; - vpath.dirname = _vpath.dirname; - vpath.basename = _vpath.basename; - vpath.extname = _vpath.extname; - vpath.changeExtension = _vpath.changeExtension; - // tslint:enable:no-unnecessary-qualifier -} + /** + * Virtual path separator. + */ + export const sep = "/"; -declare module "_vpath" { - import * as _vpath from "@typescript/vfs-path"; - global { - namespace vpath { - export import sep = _vpath.sep; - export import normalizeSeparators = _vpath.normalizeSeparators; - export import ValidationFlags = _vpath.ValidationFlags; - export import valid = _vpath.valid; - export import validate = _vpath.validate; - export import isAbsolute = _vpath.isAbsolute; - export import hasTrailingSeparator = _vpath.hasTrailingSeparator; - export import addTrailingSeparator = _vpath.addTrailingSeparator; - export import removeTrailingSeparator = _vpath.removeTrailingSeparator; - export import normalize = _vpath.normalize; - export import combine = _vpath.combine; - export import resolve = _vpath.resolve; - export import relative = _vpath.relative; - export import compareCaseSensitive = _vpath.compareCaseSensitive; - export import compareCaseInsensitive = _vpath.compareCaseInsensitive; - export import compare = _vpath.compare; - export import equals = _vpath.equals; - export import beneath = _vpath.beneath; - export import parse = _vpath.parse; - export import format = _vpath.format; - export import dirname = _vpath.dirname; - export import basename = _vpath.basename; - export import extname = _vpath.extname; - export import changeExtension = _vpath.changeExtension; - } + /** + * Normalize path separators. + */ + export function normalizeSeparators(path: string): string { + return path.replace(/\s*[\\/]\s*/g, sep).trim(); } -} + + const invalidRootComponentRegExp = /^(?!(\/|\/\/\w+\/|[a-zA-Z]:\/?|)$)/; + const invalidNavigableComponentRegExp = /[:*?"<>|]/; + const invalidNonNavigableComponentRegExp = /^\.{1,2}$|[:*?"<>|]/; + + export const enum ValidationFlags { + None = 0, + + RequireRoot = 1 << 0, + RequireDirname = 1 << 1, + RequireBasename = 1 << 2, + RequireExtname = 1 << 3, + RequireTrailingSeparator = 1 << 4, + + AllowRoot = 1 << 5, + AllowDirname = 1 << 6, + AllowBasename = 1 << 7, + AllowExtname = 1 << 8, + AllowTrailingSeparator = 1 << 9, + AllowNavigation = 1 << 10, + + /** Path must be a valid directory root */ + Root = RequireRoot | AllowRoot | AllowTrailingSeparator, + + /** Path must be a absolute */ + Absolute = RequireRoot | AllowRoot | AllowDirname | AllowBasename | AllowExtname | AllowTrailingSeparator | AllowNavigation, + + /** Path may be relative or absolute */ + RelativeOrAbsolute = AllowRoot | AllowDirname | AllowBasename | AllowExtname | AllowTrailingSeparator | AllowNavigation, + + /** Path may only be a filename */ + Basename = RequireBasename | AllowExtname, + } + + export function valid(path: string, flags: ValidationFlags = ValidationFlags.RelativeOrAbsolute) { + return validateComponents(parse(path), flags, hasTrailingSeparator(path)); + } + + export function validate(path: string, flags: ValidationFlags = ValidationFlags.RelativeOrAbsolute) { + const components = parse(path); + const trailing = hasTrailingSeparator(path); + if (!validateComponents(components, flags, trailing)) throw new vfs.IOError("ENOENT", "scandir", path); + return components.length > 1 && trailing ? format(reduce(components)) + sep : format(reduce(components)); + } + + function validateComponents(components: string[], flags: ValidationFlags, hasTrailingSeparator: boolean) { + const hasRoot = !!components[0]; + const hasDirname = components.length > 2; + const hasBasename = components.length > 1; + const hasExtname = hasBasename && extRegExp.test(components[components.length - 1]); + const invalidComponentRegExp = flags & ValidationFlags.AllowNavigation ? invalidNavigableComponentRegExp : invalidNonNavigableComponentRegExp; + + // Validate required components + if (flags & ValidationFlags.RequireRoot && !hasRoot) return false; + if (flags & ValidationFlags.RequireDirname && !hasDirname) return false; + if (flags & ValidationFlags.RequireBasename && !hasBasename) return false; + if (flags & ValidationFlags.RequireExtname && !hasExtname) return false; + if (flags & ValidationFlags.RequireTrailingSeparator && !hasTrailingSeparator) return false; + + // Required components indicate allowed components + if (flags & ValidationFlags.RequireRoot) flags |= ValidationFlags.AllowRoot; + if (flags & ValidationFlags.RequireDirname) flags |= ValidationFlags.AllowDirname; + if (flags & ValidationFlags.RequireBasename) flags |= ValidationFlags.AllowBasename; + if (flags & ValidationFlags.RequireExtname) flags |= ValidationFlags.AllowExtname; + if (flags & ValidationFlags.RequireTrailingSeparator) flags |= ValidationFlags.AllowTrailingSeparator; + + // Validate disallowed components + if (~flags & ValidationFlags.AllowRoot && hasRoot) return false; + if (~flags & ValidationFlags.AllowDirname && hasDirname) return false; + if (~flags & ValidationFlags.AllowBasename && hasBasename) return false; + if (~flags & ValidationFlags.AllowExtname && hasExtname) return false; + if (~flags & ValidationFlags.AllowTrailingSeparator && hasTrailingSeparator) return false; + + // Validate component strings + if (invalidRootComponentRegExp.test(components[0])) return false; + for (let i = 1; i < components.length; i++) { + if (invalidComponentRegExp.test(components[i])) return false; + } + + return true; + } + + const absolutePathRegExp = /^[\\/]([\\/](.*?[\\/](.*?[\\/])?)?)?|^[a-zA-Z]:[\\/]?|^\w+:\/{2}[^\\/]*\/?/; + + function getRootLength(path: string) { + const match = absolutePathRegExp.exec(path); + return match ? match[0].length : 0; + } + + /** + * Determines whether a path is an absolute path (e.g. starts with `/`, `\\`, or a dos path + * like `c:`). + */ + export function isAbsolute(path: string) { + return absolutePathRegExp.test(path); + } + + /** + * Determines whether a path consists only of a path root. + */ + export function isRoot(path: string) { + const rootLength = getRootLength(path); + return rootLength > 0 && rootLength === path.length; + } + + const trailingSeperatorRegExp = /[\\/]$/; + + /** + * Determines whether a path has a trailing separator (`/`). + */ + export function hasTrailingSeparator(path: string) { + return trailingSeperatorRegExp.test(path) && !isRoot(path); + } + + /** + * Adds a trailing separator (`/`) to a path if it doesn't have one. + */ + export function addTrailingSeparator(path: string) { + return !trailingSeperatorRegExp.test(path) && path ? path + "/" : path; + } + + /** + * Removes a trailing separator (`/`) from a path if it has one. + */ + export function removeTrailingSeparator(path: string) { + return trailingSeperatorRegExp.test(path) && !isRoot(path) ? path.slice(0, -1) : path; + } + + function reduce(components: ReadonlyArray) { + const normalized = [components[0]]; + for (let i = 1; i < components.length; i++) { + const component = components[i]; + if (component === ".") continue; + if (component === "..") { + if (normalized.length > 1) { + if (normalized[normalized.length - 1] !== "..") { + normalized.pop(); + continue; + } + } + else if (normalized[0]) continue; + } + normalized.push(component); + } + return normalized; + } + + /** + * Normalize a path containing path traversal components (`.` or `..`). + */ + export function normalize(path: string): string { + const components = reduce(parse(path)); + return components.length > 1 && hasTrailingSeparator(path) ? format(components) + sep : format(components); + } + + /** + * Combines two or more paths. If a path is absolute, it replaces any previous path. + */ + export function combine(path: string, ...paths: string[]) { + path = normalizeSeparators(path); + for (let name of paths) { + name = normalizeSeparators(name); + if (name.length === 0) continue; + path = path.length === 0 || isAbsolute(name) ? name : + addTrailingSeparator(path) + name; + } + return path; + } + + /** + * Combines and normalizes two or more paths. + */ + export function resolve(path: string, ...paths: string[]) { + return normalize(combine(path, ...paths)); + } + + function relativeWorker(from: string, to: string, stringEqualityComparer: (a: string, b: string) => boolean) { + if (!isAbsolute(from)) throw new Error("Path not absolute"); + if (!isAbsolute(to)) throw new Error("Path not absolute"); + + const fromComponents = reduce(parse(from)); + const toComponents = reduce(parse(to)); + + let start: number; + for (start = 0; start < fromComponents.length && start < toComponents.length; start++) { + if (!stringEqualityComparer(fromComponents[start], toComponents[start])) { + break; + } + } + + if (start === 0 || (start === 1 && fromComponents[0] === "/")) { + return format(toComponents); + } + + const components = toComponents.slice(start); + for (; start < fromComponents.length; start++) { + components.unshift(".."); + } + + return format(["", ...components]); + } + + function relativeCaseSensitive(from: string, to: string) { + return relativeWorker(from, to, core.equateStringsCaseSensitive); + } + + function relativeCaseInsensitive(from: string, to: string) { + return relativeWorker(from, to, core.equateStringsCaseInsensitive); + } + + /** + * Gets a relative path that can be used to traverse between `from` and `to`. + */ + export function relative(from: string, to: string, ignoreCase: boolean) { + return ignoreCase ? relativeCaseInsensitive(from, to) : relativeCaseSensitive(from, to); + } + + function compareWorker(a: string, b: string, stringComparer: (a: string, b: string) => number) { + if (a === b) return 0; + a = removeTrailingSeparator(a); + b = removeTrailingSeparator(b); + if (a === b) return 0; + const aComponents = reduce(parse(a)); + const bComponents = reduce(parse(b)); + const len = Math.min(aComponents.length, bComponents.length); + for (let i = 0; i < len; i++) { + const result = stringComparer(aComponents[i], bComponents[i]); + if (result !== 0) return result; + } + return core.compareNumbers(aComponents.length, bComponents.length); + } + + /** + * Performs a case-sensitive comparison of two paths. + */ + export function compareCaseSensitive(a: string, b: string) { + return compareWorker(a, b, core.compareStringsCaseSensitive); + } + + /** + * Performs a case-insensitive comparison of two paths. + */ + export function compareCaseInsensitive(a: string, b: string) { + return compareWorker(a, b, core.compareStringsCaseInsensitive); + } + + /** + * Compare two paths. + */ + export function compare(a: string, b: string, ignoreCase: boolean) { + return ignoreCase ? compareCaseInsensitive(a, b) : compareCaseSensitive(a, b); + } + + /** + * Determines whether two strings are equal. + */ + export function equals(a: string, b: string, ignoreCase: boolean) { + if (!isAbsolute(a)) throw new Error("Path not absolute"); + if (!isAbsolute(b)) throw new Error("Path not absolute"); + if (a === b) return true; + a = removeTrailingSeparator(a); + b = removeTrailingSeparator(b); + if (a === b) return true; + a = normalize(a); + b = normalize(b); + if (a === b) return true; + return ignoreCase && a.toUpperCase() === b.toUpperCase(); + } + + function beneathWorker(ancestor: string, descendant: string, stringEqualityComparer: (a: string, b: string) => boolean) { + if (!isAbsolute(ancestor)) throw new Error("Path not absolute"); + if (!isAbsolute(descendant)) throw new Error("Path not absolute"); + const ancestorComponents = reduce(parse(ancestor)); + const descendantComponents = reduce(parse(descendant)); + if (descendantComponents.length < ancestorComponents.length) return false; + for (let i = 0; i < ancestorComponents.length; i++) { + if (!stringEqualityComparer(ancestorComponents[i], descendantComponents[i])) { + return false; + } + } + return true; + } + + function beneathCaseSensitive(ancestor: string, descendant: string) { + return beneathWorker(ancestor, descendant, core.equateStringsCaseSensitive); + } + + function beneathCaseInsensitive(ancestor: string, descendant: string) { + return beneathWorker(ancestor, descendant, core.equateStringsCaseInsensitive); + } + + /** + * Determines whether the path `descendant` is beneath the path `ancestor`. + */ + export function beneath(ancestor: string, descendant: string, ignoreCase: boolean) { + return ignoreCase ? beneathCaseInsensitive(ancestor, descendant) : beneathCaseSensitive(ancestor, descendant); + } + + /** + * Parse a path into a root component and zero or more path segments. + * If the path is relative, the root component is `""`. + * If the path is absolute, the root component includes the first path separator (`/`). + */ + export function parse(path: string) { + path = normalizeSeparators(path); + const rootLength = getRootLength(path); + const root = path.substring(0, rootLength); + const rest = path.substring(rootLength).split(/\/+/g); + if (rest.length && !rest[rest.length - 1]) rest.pop(); + return [root, ...rest.map(component => component.trim())]; + } + + /** + * Formats a parsed path consisting of a root component and zero or more path segments. + */ + export function format(components: ReadonlyArray) { + return components.length ? components[0] + components.slice(1).join(sep) : ""; + } + + /** + * Gets the parent directory name of a path. + */ + export function dirname(path: string) { + path = normalizeSeparators(path); + path = removeTrailingSeparator(path); + return path.substr(0, Math.max(getRootLength(path), path.lastIndexOf(sep))); + } + + /** + * Gets the portion of a path following the last separator (`/`). + */ + export function basename(path: string): string; + /** + * Gets the portion of a path following the last separator (`/`). + * If the base name has any one of the provided extensions, it is removed. + */ + export function basename(path: string, extensions: string | ReadonlyArray, ignoreCase: boolean): string; + export function basename(path: string, extensions?: string | ReadonlyArray, ignoreCase?: boolean) { + path = normalizeSeparators(path); + path = removeTrailingSeparator(path); + const name = path.substr(Math.max(getRootLength(path), path.lastIndexOf(sep) + 1)); + const extension = extensions !== undefined && ignoreCase !== undefined ? extname(path, extensions, ignoreCase) : undefined; + return extension ? name.slice(0, name.length - extension.length) : name; + } + + function extnameWorker(path: string, extensions: string | ReadonlyArray, stringEqualityComparer: (a: string, b: string) => boolean) { + const manyExtensions = Array.isArray(extensions) ? extensions : undefined; + const singleExtension = Array.isArray(extensions) ? undefined : extensions; + const length = manyExtensions ? manyExtensions.length : 1; + for (let i = 0; i < length; i++) { + let extension = manyExtensions ? manyExtensions[i] : singleExtension; + if (!extension.startsWith(".")) extension = "." + extension; + if (path.length >= extension.length && path.charAt(path.length - extension.length) === ".") { + const pathExtension = path.slice(path.length - extension.length); + if (stringEqualityComparer(pathExtension, extension)) { + return pathExtension; + } + } + } + return ""; + } + + const extRegExp = /\.\w+$/; + + /** + * Gets the file extension for a path. + */ + export function extname(path: string): string; + /** + * Gets the file extension for a path, provided it is one of the provided extensions. + */ + export function extname(path: string, extensions: string | ReadonlyArray, ignoreCase: boolean): string; + export function extname(path: string, extensions?: string | ReadonlyArray, ignoreCase?: boolean) { + if (extensions) { + return extnameWorker(path, extensions, ignoreCase ? core.equateStringsCaseInsensitive : core.equateStringsCaseSensitive); + } + + const match = extRegExp.exec(path); + return match ? match[0] : ""; + } + + export function changeExtension(path: string, ext: string): string; + export function changeExtension(path: string, ext: string, extensions: string | ReadonlyArray, ignoreCase: boolean): string; + export function changeExtension(path: string, ext: string, extensions?: string | ReadonlyArray, ignoreCase?: boolean) { + const pathext = extensions !== undefined && ignoreCase !== undefined ? extname(path, extensions, ignoreCase) : extname(path); + return pathext ? path.slice(0, path.length - pathext.length) + (ext.startsWith(".") ? ext : "." + ext) : path; + } + + const typeScriptExtensions: ReadonlyArray = [".ts", ".tsx"]; + + export function isTypeScript(path: string) { + return extname(path, typeScriptExtensions, /*ignoreCase*/ false).length > 0; + } + + const javaScriptExtensions: ReadonlyArray = [".js", ".jsx"]; + + export function isJavaScript(path: string) { + return extname(path, javaScriptExtensions, /*ignoreCase*/ false).length > 0; + } + + export function isDeclaration(path: string) { + return extname(path, ".d.ts", /*ignoreCase*/ false).length > 0; + } + + export function isSourceMap(path: string) { + return extname(path, ".map", /*ignoreCase*/ false).length > 0; + } + + const javaScriptSourceMapExtensions: ReadonlyArray = [".js.map", ".jsx.map"]; + + export function isJavaScriptSourceMap(path: string) { + return extname(path, javaScriptSourceMapExtensions, /*ignoreCase*/ false).length > 0; + } + + export function isJson(path: string) { + return extname(path, ".json", /*ignoreCase*/ false).length > 0; + } + + export function isDefaultLibrary(path: string) { + return isDeclaration(path) + && basename(path).startsWith("lib."); + } +} \ No newline at end of file