Skip to content

Commit

Permalink
feat: Add SourceFile getRelativePathToSourceFile and getRelativePathT…
Browse files Browse the repository at this point in the history
…oSourceFileAsModuleSpecifier.

Super long method name, but couldn't think of anything else that wasn't confusing.
  • Loading branch information
dsherret committed Jan 27, 2018
1 parent 3e1f16f commit 99e8585
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 1 deletion.
16 changes: 15 additions & 1 deletion docs/details/source-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ sourceFile.unindent(10, -1); // indent line containing position 10 (specify nega

This will indent and unindent based on your [manipulation settings](../manipulation/settings).

## Getting Exported Declarations
### Getting Exported Declarations

The exported declarations of a file can be retrieved via `.getExportedDeclarations()`.

Expand Down Expand Up @@ -209,3 +209,17 @@ Name: Class1
Name: Class2
Name: Interface1
```

### Relative File Paths

It might be useful to get the relative path from one source file to another.

```ts
const relativePath = sourceFileFrom.getRelativePathToSourceFile(sourceFileTo);
```

Or to get the module specifier text from one source file to another.

```ts
const moduleSpecifier = sourceFileFrom.getRelativePathToSourceFileAsModuleSpecifier(sourceFileTo);
```
17 changes: 17 additions & 0 deletions src/compiler/file/SourceFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,23 @@ export class SourceFile extends SourceFileBase<ts.SourceFile> {
return this._refreshFromFileSystemInternal(fileReadResult);
}

/**
* Gets the relative path to another source file.
* @param sourceFile - Source file.
*/
getRelativePathToSourceFile(sourceFile: SourceFile) {
return FileUtils.getRelativePathTo(this.getFilePath(), sourceFile.getFilePath());
}

/**
* Gets the relative path to the specified source file as a module specifier.
* @param sourceFile - Source file.
*/
getRelativePathToSourceFileAsModuleSpecifier(sourceFile: SourceFile) {
const relativePath = this.getRelativePathToSourceFile(sourceFile);
return "./" + relativePath.replace(/((\.d\.ts$)|(\.[^/.]+$))/i, "").replace(/\/index$/i, "");
}

private _refreshFromFileSystemInternal(fileReadResult: string | false): FileSystemRefreshResult {
if (fileReadResult === false) {
this.forget();
Expand Down
60 changes: 60 additions & 0 deletions src/tests/compiler/file/sourceFileTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -934,4 +934,64 @@ function myFunction(param: MyClass) {
expect(didThrow).to.be.true;
});
});

describe(nameof<SourceFile>(s => s.getRelativePathToSourceFile), () => {
function doTest(from: string, to: string, expected: string) {
const ast = new TsSimpleAst({ useVirtualFileSystem: true });
const fromFile = ast.createSourceFile(from);
const toFile = ast.createSourceFile(to);
expect(fromFile.getRelativePathToSourceFile(toFile)).to.equal(expected);
}

// most of these tests are in fileUtilsTests

it("should get the relative path to a source file in a different directory", () => {
doTest("/dir/from.ts", "/dir2/to.ts", "../dir2/to.ts");
});
});

describe(nameof<SourceFile>(s => s.getRelativePathToSourceFileAsModuleSpecifier), () => {
function doTest(from: string, to: string, expected: string) {
const ast = new TsSimpleAst({ useVirtualFileSystem: true });
const fromFile = ast.createSourceFile(from);
const toFile = from === to ? fromFile : ast.createSourceFile(to);
expect(fromFile.getRelativePathToSourceFileAsModuleSpecifier(toFile)).to.equal(expected);
}

it("should get the module specifier to a source file in a different directory", () => {
doTest("/dir/from.ts", "/dir2/to.ts", "./../dir2/to");
});

it("should get the module specifier to a source file in the same directory", () => {
doTest("/dir/from.ts", "/dir/to.ts", "./to");
});

it("should get the module specifier to the same source file", () => {
doTest("/dir/file.ts", "/dir/file.ts", "./file");
});

it("should get the module specifier to a definition file", () => {
doTest("/dir/from.ts", "/dir2/to.d.ts", "./../dir2/to");
});

it("should get the module specifier to a definition file that doing use a lower case extension", () => {
doTest("/dir/from.ts", "/dir2/to.D.TS", "./../dir2/to");
});

it("should use an implicit index when specifying the index file in a different directory", () => {
doTest("/dir/file.ts", "/dir2/index.ts", "./dir2");
});

it("should use an implicit index when specifying the index file in a different directory that has different casing", () => {
doTest("/dir/file.ts", "/dir2/INDEX.ts", "./dir2");
});

it("should use an implicit index when specifying the index file of a definition file in a different directory", () => {
doTest("/dir/file.ts", "/dir2/index.d.ts", "./dir2");
});

it("should use an implicit index when specifying the index file in the same directory", () => {
doTest("/dir/file.ts", "/dir/index.ts", ".");
});
});
});
26 changes: 26 additions & 0 deletions src/tests/utils/fileUtilsTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,30 @@ describe(nameof(FileUtils), () => {
expect(FileUtils.filePathMatches("V:/dir/test.ts", "ir/test.ts")).to.be.false;
});
});

describe(nameof(FileUtils.getRelativePathTo), () => {
function doTest(from: string, to: string, expected: string) {
expect(FileUtils.getRelativePathTo(from, to)).to.equal(expected);
}

it("should get the relative path when the file is in the parent directory", () => {
doTest("V:/testing/this/out/from.ts", "V:/testing/this/to.ts", "../to.ts");
});

it("should get the relative path when the file is in a child directory", () => {
doTest("V:/testing/this/from.ts", "V:/testing/this/out/to.ts", "out/to.ts");
});

it("should get the relative path when the files are in different child directories", () => {
doTest("V:/testing/this/child1/from.ts", "V:/testing/this/child2/to.ts", "../child2/to.ts");
});

it("should get the relative path when the files are in the same directory", () => {
doTest("V:/testing/this/out/from.ts", "V:/testing/this/out/to.ts", "to.ts");
});

it("should get the relative path when the files are the same", () => {
doTest("V:/testing/this/out/to.ts", "V:/testing/this/out/to.ts", "to.ts");
});
});
});
10 changes: 10 additions & 0 deletions src/utils/FileUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,14 @@ export class FileUtils {
return text;
return bom + text;
}

/**
* Gets the relative path from one absolute path to another.
* @param absolutePathFrom - Absolute path from.
* @param absolutePathTo - Absolute path to.
*/
static getRelativePathTo(absolutePathFrom: string, absolutePathTo: string) {
const relativePath = path.relative(path.dirname(absolutePathFrom), path.dirname(absolutePathTo));
return FileUtils.standardizeSlashes(path.join(relativePath, path.basename(absolutePathTo)));
}
}

0 comments on commit 99e8585

Please sign in to comment.