Reorganize project structure.

This commit is contained in:
Ron Buckton 2018-04-17 17:49:51 -07:00
parent 1cbe930e06
commit 154f09bee9
54 changed files with 4733 additions and 7539 deletions

1
.gitignore vendored
View File

@ -46,7 +46,6 @@ scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js
scripts/generateLocalizedDiagnosticMessages.js
scripts/*.js.map
scripts/typings/
scripts/*/dist
coverage/
internal/
**/.DS_Store

View File

@ -3,6 +3,7 @@ language: node_js
node_js:
- 'stable'
- '8'
- '6'
sudo: false

View File

@ -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"]);
});

View File

@ -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"));

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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",

View File

@ -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"]);

View File

@ -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"
}
}

View File

@ -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;
}

View File

@ -1,3 +0,0 @@
export * from "@typescript/vfs-core";
export * from "./strings";
export * from "./crypto";

View File

@ -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;
}

View File

@ -1,15 +0,0 @@
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"outDir": "dist",
"types": ["node"],
"declaration": true,
"sourceMap": true,
"strict": true,
"preserveConstEnums": true
},
"include": [
"src/**/*"
]
}

View File

@ -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"]);

View File

@ -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"
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -1 +0,0 @@
export function identity<T>(v: T): T { return v; }

View File

@ -1,3 +0,0 @@
export * from "./functions";
export * from "./comparers";
export * from "./collections";

View File

@ -1,15 +0,0 @@
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"outDir": "dist",
"types": ["node"],
"declaration": true,
"sourceMap": true,
"strict": true,
"preserveConstEnums": true
},
"include": [
"src/**/*"
]
}

View File

@ -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"]);

View File

@ -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"
}
}

View File

@ -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;
}
}

View File

@ -1,15 +0,0 @@
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"outDir": "dist",
"types": ["node"],
"declaration": true,
"sourceMap": true,
"strict": true,
"preserveConstEnums": true
},
"include": [
"src/**/*"
]
}

View File

@ -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"]);

View File

@ -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"
}
}

View File

@ -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.");
}

View File

@ -1,15 +0,0 @@
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"outDir": "dist",
"types": ["node"],
"declaration": true,
"sourceMap": true,
"strict": true,
"preserveConstEnums": true
},
"include": [
"src/**/*"
]
}

View File

@ -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"]);

View File

@ -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"
}
}

View File

@ -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

View File

@ -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

View File

@ -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";

View File

@ -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;
}

View File

@ -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

View File

@ -1,4 +0,0 @@
import { install } from "source-map-support";
install();
import "./fileSystemTests";

View File

@ -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 };
}

View File

@ -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"

View File

@ -1,15 +0,0 @@
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"outDir": "dist",
"types": ["node", "mocha"],
"declaration": true,
"sourceMap": true,
"strict": true,
"preserveConstEnums": true
},
"include": [
"src/**/*"
]
}

View File

@ -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)}`);
}
};
});

View File

@ -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" />

View 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);
}
}
}

View 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();
}
}

View File

@ -0,0 +1,3 @@
namespace core {
export function identity<T>(v: T): T { return v; }
}

134
src/harness/core/strings.ts Normal file
View 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;
}
}

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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",

File diff suppressed because it is too large Load Diff

View File

@ -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) {

View File

@ -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.");
}
}