mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-23 10:29:01 -06:00
Reorganize project structure.
This commit is contained in:
parent
1cbe930e06
commit
154f09bee9
1
.gitignore
vendored
1
.gitignore
vendored
@ -46,7 +46,6 @@ scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js
|
||||
scripts/generateLocalizedDiagnosticMessages.js
|
||||
scripts/*.js.map
|
||||
scripts/typings/
|
||||
scripts/*/dist
|
||||
coverage/
|
||||
internal/
|
||||
**/.DS_Store
|
||||
|
||||
@ -3,6 +3,7 @@ language: node_js
|
||||
node_js:
|
||||
- 'stable'
|
||||
- '8'
|
||||
- '6'
|
||||
|
||||
sudo: false
|
||||
|
||||
|
||||
50
Gulpfile.ts
50
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(".", <any>{ 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"]);
|
||||
});
|
||||
gulp.watch("src/**/*.*", ["runtests-parallel"]);
|
||||
});
|
||||
|
||||
303
Jakefile.js
303
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"));
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 ->
|
||||
|
||||
|
||||
1847
package-lock.json
generated
1847
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
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",
|
||||
|
||||
@ -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"]);
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export * from "@typescript/vfs-core";
|
||||
export * from "./strings";
|
||||
export * from "./crypto";
|
||||
@ -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: // <CR> carriage return
|
||||
if (pos < text.length && text.charCodeAt(pos) === 0x000a) {
|
||||
pos++;
|
||||
}
|
||||
// falls through
|
||||
|
||||
case 0x000a: // <LF> line feed
|
||||
case 0x2028: // <LS> line separator
|
||||
case 0x2029: // <PS> 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> tab
|
||||
case 0x000b: // <VT> vertical tab
|
||||
case 0x000c: // <FF> form feed
|
||||
case 0x0020: // <SP> space
|
||||
case 0x00a0: // <NBSP> no-break space
|
||||
case 0xfeff: // <ZWNBSP> zero width no-break space
|
||||
case 0x1680: // <USP> ogham space mark
|
||||
case 0x2000: // <USP> en quad
|
||||
case 0x2001: // <USP> em quad
|
||||
case 0x2002: // <USP> en space
|
||||
case 0x2003: // <USP> em space
|
||||
case 0x2004: // <USP> three-per-em space
|
||||
case 0x2005: // <USP> four-per-em space
|
||||
case 0x2006: // <USP> six-per-em space
|
||||
case 0x2007: // <USP> figure space
|
||||
case 0x2008: // <USP> punctuation space
|
||||
case 0x2009: // <USP> thin space
|
||||
case 0x200a: // <USP> hair space
|
||||
case 0x202f: // <USP> narrow no-break space
|
||||
case 0x205f: // <USP> medium mathematical space
|
||||
case 0x3000: // <USP> 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<number>;
|
||||
|
||||
export interface LinesAndLineStarts {
|
||||
readonly lines: ReadonlyArray<string>;
|
||||
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;
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"outDir": "dist",
|
||||
"types": ["node"],
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"preserveConstEnums": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
@ -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"]);
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -1,517 +0,0 @@
|
||||
import { identity } from "./functions";
|
||||
|
||||
export interface SortOptions<T> {
|
||||
comparer: (a: T, b: T) => number;
|
||||
sort: "insertion" | "comparison";
|
||||
}
|
||||
|
||||
export class SortedMap<K, V> {
|
||||
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<K>, 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<T> {
|
||||
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<T>, iterable?: Iterable<T>) {
|
||||
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<T, U>(array: ReadonlyArray<T>, 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<T>(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<T>(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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export function identity<T>(v: T): T { return v; }
|
||||
@ -1,3 +0,0 @@
|
||||
export * from "./functions";
|
||||
export * from "./comparers";
|
||||
export * from "./collections";
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"outDir": "dist",
|
||||
"types": ["node"],
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"preserveConstEnums": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
@ -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"]);
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"outDir": "dist",
|
||||
"types": ["node"],
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"preserveConstEnums": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
@ -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"]);
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -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<string>) {
|
||||
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<string>) {
|
||||
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<string>, ignoreCase: boolean): string;
|
||||
export function basename(path: string, extensions?: string | ReadonlyArray<string>, 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<string>, 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<string>, ignoreCase: boolean): string;
|
||||
export function extname(path: string, extensions?: string | ReadonlyArray<string>, 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<string>, ignoreCase: boolean): string;
|
||||
export function changeExtension(path: string, ext: string, extensions?: string | ReadonlyArray<string>, 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<string> = [".ts", ".tsx"];
|
||||
|
||||
export function isTypeScript(path: string) {
|
||||
return extname(path, typeScriptExtensions, /*ignoreCase*/ false).length > 0;
|
||||
}
|
||||
|
||||
const javaScriptExtensions: ReadonlyArray<string> = [".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<string> = [".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.");
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"outDir": "dist",
|
||||
"types": ["node"],
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"preserveConstEnums": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
@ -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"]);
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -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<string, any> | undefined;
|
||||
constructor(files: FileSet, { uid, gid, mode, meta }: { uid?: number, gid?: number, mode?: number, meta?: Record<string, any> } = {}) {
|
||||
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<string, any> | undefined;
|
||||
constructor(data: Buffer | string, { uid, gid, mode, meta, encoding }: { encoding?: string, uid?: number, gid?: number, mode?: number, meta?: Record<string, any> } = {}) {
|
||||
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<string, any> | undefined;
|
||||
constructor(symlink: string, { uid, gid, mode, meta }: { uid?: number, gid?: number, mode?: number, meta?: Record<string, any> } = {}) {
|
||||
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<string, any> | undefined;
|
||||
constructor(source: string, resolver: FileSystemResolver, { uid, gid, mode, meta }: { uid?: number, gid?: number, mode?: number, meta?: Record<string, any> } = {}) {
|
||||
this.source = source;
|
||||
this.resolver = resolver;
|
||||
this.uid = uid;
|
||||
this.gid = gid;
|
||||
this.mode = mode;
|
||||
this.meta = meta;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -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";
|
||||
@ -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<DirectoryInode | undefined, core.SortedSet<string>>;
|
||||
paths?: ReadonlyArray<string>;
|
||||
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<string, Inode> | 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;
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,4 +0,0 @@
|
||||
import { install } from "source-map-support";
|
||||
install();
|
||||
|
||||
import "./fileSystemTests";
|
||||
@ -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 };
|
||||
}
|
||||
@ -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<FSWatcherEntry> {
|
||||
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"
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"outDir": "dist",
|
||||
"types": ["node", "mocha"],
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"preserveConstEnums": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
@ -1,117 +0,0 @@
|
||||
/// <reference path="./harness.ts" />
|
||||
declare namespace Chai {
|
||||
interface ChaiStatic {
|
||||
util: UtilStatic;
|
||||
}
|
||||
|
||||
interface UtilStatic {
|
||||
objDisplay(obj: any): string;
|
||||
}
|
||||
|
||||
interface AssertStatic {
|
||||
sameMembers<T>(actual: Iterable<T>, expected: Iterable<T>, message?: string): void;
|
||||
notSameMembers<T>(actual: Iterable<T>, expected: Iterable<T>, message?: string): void;
|
||||
includeMembersOnce<T>(actual: Iterable<T>, expected: Iterable<T>, message?: string): void;
|
||||
includeMembers<T>(actual: Iterable<T>, expected: Iterable<T>, message?: string): void;
|
||||
notIncludeMembers<T>(actual: Iterable<T>, expected: Iterable<T>, message?: string): void;
|
||||
}
|
||||
}
|
||||
|
||||
// patch assert.sameMembers to support other iterables
|
||||
_chai.use((chai: Chai.ChaiStatic, util: Chai.UtilStatic) => {
|
||||
function isIdenticalTo<T>(left: Iterable<T>, right: Iterable<T>) {
|
||||
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<T>(set.comparer) : new Set<T>();
|
||||
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<T>(subset: Iterable<T>, superset: Iterable<T>, unique?: boolean) {
|
||||
const set = asReadonlySet(superset);
|
||||
const seen = unique ? set instanceof core.SortedSet ? new core.SortedSet<T>(set.comparer) : new Set<T>() : 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<T>(left: Iterable<T>, right: Iterable<T>) {
|
||||
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<T>(iterable: Iterable<T>): ReadonlySet<T> {
|
||||
return iterable instanceof Set ? iterable :
|
||||
iterable instanceof core.SortedSet ? iterable :
|
||||
new Set(iterable);
|
||||
}
|
||||
|
||||
function asReadonlyArray<T>(iterable: Iterable<T>): ReadonlyArray<T> {
|
||||
return Array.isArray(iterable) ? iterable : Array.from(iterable);
|
||||
}
|
||||
|
||||
// patch `assert.sameMembers` to support any iterable
|
||||
chai.assert.sameMembers = <T>(actual: Iterable<T>, expected: Iterable<T>, 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 = <T>(actual: Iterable<T>, expected: Iterable<T>, 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 = <T>(actual: Iterable<T>, expected: Iterable<T>, 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 = <T>(actual: Iterable<T>, expected: Iterable<T>, 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 = <T>(actual: Iterable<T>, expected: Iterable<T>, 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)}`);
|
||||
}
|
||||
};
|
||||
});
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <reference path="./core/functions.ts" />
|
||||
/// <reference path="./core/comparers.ts" />
|
||||
/// <reference path="./core/collections.ts" />
|
||||
/// <reference path="./core/strings.ts" />
|
||||
542
src/harness/core/collections.ts
Normal file
542
src/harness/core/collections.ts
Normal file
@ -0,0 +1,542 @@
|
||||
/// <reference path="./functions.ts" />
|
||||
namespace core {
|
||||
export interface SortOptions<T> {
|
||||
comparer: (a: T, b: T) => number;
|
||||
sort: "insertion" | "comparison";
|
||||
}
|
||||
|
||||
export class SortedMap<K, V> {
|
||||
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<K>, 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<T> {
|
||||
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<T>, iterable?: Iterable<T>) {
|
||||
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<T, U>(array: ReadonlyArray<T>, 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<T>(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<T>(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<T>(iterable: Iterable<T>): Iterator<T> {
|
||||
return iterable[Symbol.iterator]();
|
||||
}
|
||||
|
||||
export function nextResult<T>(iterator: Iterator<T>): IteratorResult<T> | undefined {
|
||||
const result = iterator.next();
|
||||
return result.done ? undefined : result;
|
||||
}
|
||||
|
||||
export function closeIterator<T>(iterator: Iterator<T>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/harness/core/comparers.ts
Normal file
43
src/harness/core/comparers.ts
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
3
src/harness/core/functions.ts
Normal file
3
src/harness/core/functions.ts
Normal file
@ -0,0 +1,3 @@
|
||||
namespace core {
|
||||
export function identity<T>(v: T): T { return v; }
|
||||
}
|
||||
134
src/harness/core/strings.ts
Normal file
134
src/harness/core/strings.ts
Normal file
@ -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: // <CR> carriage return
|
||||
if (pos < text.length && text.charCodeAt(pos) === 0x000a) {
|
||||
pos++;
|
||||
}
|
||||
// falls through
|
||||
|
||||
case 0x000a: // <LF> line feed
|
||||
case 0x2028: // <LS> line separator
|
||||
case 0x2029: // <PS> 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> tab
|
||||
case 0x000b: // <VT> vertical tab
|
||||
case 0x000c: // <FF> form feed
|
||||
case 0x0020: // <SP> space
|
||||
case 0x00a0: // <NBSP> no-break space
|
||||
case 0xfeff: // <ZWNBSP> zero width no-break space
|
||||
case 0x1680: // <USP> ogham space mark
|
||||
case 0x2000: // <USP> en quad
|
||||
case 0x2001: // <USP> em quad
|
||||
case 0x2002: // <USP> en space
|
||||
case 0x2003: // <USP> em space
|
||||
case 0x2004: // <USP> three-per-em space
|
||||
case 0x2005: // <USP> four-per-em space
|
||||
case 0x2006: // <USP> six-per-em space
|
||||
case 0x2007: // <USP> figure space
|
||||
case 0x2008: // <USP> punctuation space
|
||||
case 0x2009: // <USP> thin space
|
||||
case 0x200a: // <USP> hair space
|
||||
case 0x202f: // <USP> narrow no-break space
|
||||
case 0x205f: // <USP> medium mathematical space
|
||||
case 0x3000: // <USP> 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<number>;
|
||||
|
||||
export interface LinesAndLineStarts {
|
||||
readonly lines: ReadonlyArray<string>;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
/// <reference path="./harness.ts" />
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
/// <reference path="./core.ts" />
|
||||
/// <reference path="./assert.ts" />
|
||||
/// <reference path="./utils.ts" />
|
||||
/// <reference path="./vfs.ts" />
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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",
|
||||
|
||||
2635
src/harness/vfs.ts
2635
src/harness/vfs.ts
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
/// <reference path="./harness.ts" />
|
||||
/// <reference path="./vpath.ts" />
|
||||
/// <reference path="./core.ts" />
|
||||
/// <reference path="./core/strings.ts" />
|
||||
/// <reference path="./utils.ts" />
|
||||
/// <reference path="./documents.ts" />
|
||||
/// <reference path="./vfs.ts" />
|
||||
@ -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<string> = [".ts", ".tsx"];
|
||||
|
||||
export function isTypeScript(path: string) {
|
||||
|
||||
@ -1,63 +1,437 @@
|
||||
// NOTE: This namespace re-exports all of the exports from the @typescript/vfs-path private package.
|
||||
|
||||
/// <reference path="./core/comparers.ts" />
|
||||
/// <reference path="./vfs.ts" />
|
||||
namespace vpath {
|
||||
const _vpath = require("@typescript/vfs-path") as typeof vpath;
|
||||
// tslint:disable:no-unnecessary-qualifier
|
||||
vpath.sep = _vpath.sep;
|
||||
vpath.normalizeSeparators = _vpath.normalizeSeparators;
|
||||
(<any>vpath).ValidationFlags = (<any>_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<string>) {
|
||||
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<string>) {
|
||||
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<string>, ignoreCase: boolean): string;
|
||||
export function basename(path: string, extensions?: string | ReadonlyArray<string>, 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<string>, 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<string>, ignoreCase: boolean): string;
|
||||
export function extname(path: string, extensions?: string | ReadonlyArray<string>, 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<string>, ignoreCase: boolean): string;
|
||||
export function changeExtension(path: string, ext: string, extensions?: string | ReadonlyArray<string>, 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<string> = [".ts", ".tsx"];
|
||||
|
||||
export function isTypeScript(path: string) {
|
||||
return extname(path, typeScriptExtensions, /*ignoreCase*/ false).length > 0;
|
||||
}
|
||||
|
||||
const javaScriptExtensions: ReadonlyArray<string> = [".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<string> = [".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.");
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user