Skip to content

Commit

Permalink
feat: #273 - Add overwrite option to createSourceFile.
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Mar 25, 2018
1 parent ae22f29 commit ddcd03e
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 24 deletions.
9 changes: 9 additions & 0 deletions docs/setup/adding-source-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ const sourceFile = project.createSourceFile("path/to/myNewFile.ts", fileText);

Note: The file will not be created and saved to the file system until calling `.save()` on the source file.

### Options

`createSourceFile` will throw an error if the file already exists.
To not throw an error, set the `overwrite` option to true.

```ts
const sourceFile = project.createSourceFile("path/to/myNewFile.ts", "", { overwrite: true });
```

### Note

Adding source files to the AST from a structure or text will act like any other source file, but they will not be saved to the disk unless you ask it to be.
Expand Down
14 changes: 10 additions & 4 deletions src/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export interface Options {
useVirtualFileSystem?: boolean;
}

export interface CreateSourceFileOptions {
overwrite?: boolean;
}

/**
* Project that holds source files.
*/
Expand Down Expand Up @@ -260,20 +264,22 @@ export class Project {
* Note: The file will not be created and saved to the file system until .save() is called on the source file.
* @param filePath - File path of the source file.
* @param sourceFileText - Text of the source file.
* @param options - Options.
* @throws - InvalidOperationError if a source file already exists at the provided file path.
*/
createSourceFile(filePath: string, sourceFileText: string): SourceFile;
createSourceFile(filePath: string, sourceFileText: string, options?: CreateSourceFileOptions): SourceFile;
/**
* Creates a source file at the specified file path with the specified text.
*
* Note: The file will not be created and saved to the file system until .save() is called on the source file.
* @param filePath - File path of the source file.
* @param structure - Structure that represents the source file.
* @param options - Options.
* @throws - InvalidOperationError if a source file already exists at the provided file path.
*/
createSourceFile(filePath: string, structure: SourceFileStructure): SourceFile;
createSourceFile(filePath: string, structureOrText?: SourceFileStructure | string): SourceFile {
return this.global.compilerFactory.createSourceFile(filePath, structureOrText);
createSourceFile(filePath: string, structure: SourceFileStructure, options?: CreateSourceFileOptions): SourceFile;
createSourceFile(filePath: string, structureOrText?: SourceFileStructure | string, options?: CreateSourceFileOptions): SourceFile {
return this.global.compilerFactory.createSourceFile(filePath, structureOrText || "", options || {});
}

/**
Expand Down
5 changes: 1 addition & 4 deletions src/compiler/file/SourceFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,8 @@ export class SourceFile extends SourceFileBase<ts.SourceFile> {
return copiedSourceFile;

function getCopiedSourceFile(currentFile: SourceFile) {
if (overwrite)
return currentFile.global.compilerFactory.createOrOverwriteSourceFileFromText(filePath, currentFile.getFullText());

try {
return currentFile.global.compilerFactory.createSourceFileFromText(filePath, currentFile.getFullText());
return currentFile.global.compilerFactory.createSourceFileFromText(filePath, currentFile.getFullText(), { overwrite });
} catch (err) {
if (err instanceof errors.InvalidOperationError)
throw new errors.InvalidOperationError(`Did you mean to provide the overwrite option? ` + err.message);
Expand Down
21 changes: 10 additions & 11 deletions src/factories/CompilerFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {SourceFile, Node, SymbolDisplayPart, Symbol, Type, TypeParameter, Signat
import * as errors from "../errors";
import {SourceFileStructure} from "../structures";
import {KeyValueCache, WeakCache, FileUtils, EventContainer, createHashSet, ArrayUtils, createCompilerSourceFile} from "../utils";
import {CreateSourceFileOptions} from "../Project";
import {GlobalContainer} from "../GlobalContainer";
import {Directory} from "../fileSystem";
import {createTempSourceFile} from "./createTempSourceFile";
Expand Down Expand Up @@ -78,12 +79,13 @@ export class CompilerFactory {
* Adds a source file by structure or text.
* @param filePath - File path.
* @param structureOrText - Structure or text.
* @param options - Options.
*/
createSourceFile(filePath: string, structureOrText?: string | SourceFileStructure) {
createSourceFile(filePath: string, structureOrText: string | SourceFileStructure, options: CreateSourceFileOptions) {
if (structureOrText == null || typeof structureOrText === "string")
return this.createSourceFileFromText(filePath, structureOrText || "");
return this.createSourceFileFromText(filePath, structureOrText || "", options);

const sourceFile = this.createSourceFileFromText(filePath, "");
const sourceFile = this.createSourceFileFromText(filePath, "", options);
sourceFile.fill(structureOrText);
return sourceFile;
}
Expand All @@ -93,10 +95,13 @@ export class CompilerFactory {
* Adds it to the cache.
* @param filePath - File path for the source file.
* @param sourceText - Text to create the source file with.
* @param options - Options.
* @throws InvalidOperationError if the file exists.
*/
createSourceFileFromText(filePath: string, sourceText: string) {
createSourceFileFromText(filePath: string, sourceText: string, options: CreateSourceFileOptions) {
filePath = this.global.fileSystemWrapper.getStandardizedAbsolutePath(filePath);
if (options != null && options.overwrite === true)
return this.createOrOverwriteSourceFileFromText(filePath, sourceText);
this.throwIfFileExists(filePath);
return this.getSourceFileFromText(filePath, sourceText);
}
Expand All @@ -113,13 +118,7 @@ export class CompilerFactory {
throw new errors.InvalidOperationError(`${prefixMessage}A source file already exists at the provided file path: ${filePath}`);
}

/**
* Creates or overwrites a source file and text.
* Adds it to the cache.
* @param filePath - File path for the source file.
* @param sourceText - Text to create the source file with.
*/
createOrOverwriteSourceFileFromText(filePath: string, sourceText: string) {
private createOrOverwriteSourceFileFromText(filePath: string, sourceText: string) {
filePath = this.global.fileSystemWrapper.getStandardizedAbsolutePath(filePath);
const existingSourceFile = this.addOrGetSourceFileFromFilePath(filePath);
if (existingSourceFile != null) {
Expand Down
2 changes: 1 addition & 1 deletion src/factories/createTempSourceFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ export function createTempSourceFile(filePath: string, sourceText: string, opts:
const globalContainer = new GlobalContainer(new FileSystemWrapper(new VirtualFileSystemHost()), compilerOptions, { createLanguageService });
if (opts.manipulationSettings != null)
globalContainer.manipulationSettings.set(opts.manipulationSettings);
return globalContainer.compilerFactory.createSourceFileFromText(filePath, sourceText);
return globalContainer.compilerFactory.createSourceFileFromText(filePath, sourceText, {});
}
11 changes: 7 additions & 4 deletions src/fileSystem/Directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as errors from "../errors";
import {ArrayUtils, FileUtils} from "../utils";
import {SourceFileStructure} from "../structures";
import {GlobalContainer} from "../GlobalContainer";
import {CreateSourceFileOptions} from "../Project";
import {DirectoryEmitResult} from "./DirectoryEmitResult";

export class Directory {
Expand Down Expand Up @@ -261,21 +262,23 @@ export class Directory {
* Note: The file will not be created and saved to the file system until .save() is called on the source file.
* @param relativeFilePath - Relative file path of the source file to create.
* @param sourceFileText - Text of the source file.
* @param options - Options.
* @throws - InvalidOperationError if a source file already exists at the provided file name.
*/
createSourceFile(relativeFilePath: string, sourceFileText: string): SourceFile;
createSourceFile(relativeFilePath: string, sourceFileText: string, options?: CreateSourceFileOptions): SourceFile;
/**
* Creates a source file in the AST, relative to this directory.
*
* Note: The file will not be created and saved to the file system until .save() is called on the source file.
* @param relativeFilePath - Relative file path of the source file to create.
* @param structure - Structure that represents the source file.
* @param options - Options.
* @throws - InvalidOperationError if a source file already exists at the provided file name.
*/
createSourceFile(relativeFilePath: string, structure: SourceFileStructure): SourceFile;
createSourceFile(relativeFilePath: string, structureOrText?: string | SourceFileStructure) {
createSourceFile(relativeFilePath: string, structure: SourceFileStructure, options?: CreateSourceFileOptions): SourceFile;
createSourceFile(relativeFilePath: string, structureOrText?: string | SourceFileStructure, options?: CreateSourceFileOptions) {
const filePath = this.global.fileSystemWrapper.getStandardizedAbsolutePath(relativeFilePath, this.getPath());
return this.global.compilerFactory.createSourceFile(filePath, structureOrText);
return this.global.compilerFactory.createSourceFile(filePath, structureOrText || "", options || {});
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/tests/fileSystem/directoryTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,14 @@ describe(nameof(Directory), () => {
const directory = project.addExistingDirectory("/");
expect(() => directory.createSourceFile("file.ts", "")).to.throw(errors.InvalidOperationError);
});

it("should note throw an exception if creating a source file at an existing path on the disk and providing the overwrite option", () => {
const project = getProject([{ filePath: "/file.ts", text: "" }], ["/"]);
const directory = project.addExistingDirectory("/");
const fileText = "class Identifier {}";
const file = directory.createSourceFile("file.ts", fileText, { overwrite: true });
expect(file.getFullText()).to.equal(fileText);
});
});

describe(nameof<Directory>(d => d.addSourceFileIfExists), () => {
Expand Down
10 changes: 10 additions & 0 deletions src/tests/projectTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,16 @@ describe(nameof(Project), () => {
}).to.throw(errors.InvalidOperationError, `A source file already exists at the provided file path: /file.ts`);
});

it("should not throw an exception if creating a source file at an existing path when providing the overwrite option", () => {
const project = new Project({ useVirtualFileSystem: true });
const file1 = project.createSourceFile("file.ts", "");
const newFileText = "class Identifier {}";
const file2 = project.createSourceFile("file.ts", newFileText, { overwrite: true });
expect(file1.getFullText()).to.equal(newFileText);
expect(file2.getFullText()).to.equal(newFileText);
expect(file1).to.equal(file2);
});

it("should throw an exception if creating a source file at an existing path on the disk", () => {
const fileSystem = testHelpers.getFileSystemHostWithFiles([{ filePath: "file.ts", text: "" }]);
const project = new Project(undefined, fileSystem);
Expand Down

0 comments on commit ddcd03e

Please sign in to comment.