ts-simple-ast
Version:
TypeScript compiler wrapper for AST navigation and code generation.
435 lines (433 loc) • 24.7 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const chai_1 = require("chai");
const ts = require("typescript");
const compiler_1 = require("./../../../compiler");
const testHelpers_1 = require("./../testHelpers");
const testHelpers_2 = require("./../../testHelpers");
const TsSimpleAst_1 = require("./../../../TsSimpleAst");
const utils_1 = require("./../../../utils");
describe("SourceFile", () => {
describe("copy", () => {
const fileText = " interface Identifier {} ";
const { sourceFile, tsSimpleAst } = testHelpers_1.getInfoFromText(fileText, { filePath: "Folder/File.ts" });
const relativeSourceFile = sourceFile.copy("../NewFolder/NewFile.ts");
const absoluteSourceFile = sourceFile.copy(utils_1.FileUtils.getStandardizedAbsolutePath("NewFile.ts"));
describe("tsSimpleAst", () => {
it("should include the copied source files", () => {
chai_1.expect(tsSimpleAst.getSourceFiles().length).to.equal(3);
});
});
describe("relative source file", () => {
it("should not be saved", () => {
chai_1.expect(relativeSourceFile.isSaved()).to.be.false;
});
it("should have have the same text", () => {
chai_1.expect(relativeSourceFile.getFullText()).to.equal(fileText);
});
it("should have the expected path", () => {
chai_1.expect(relativeSourceFile.getFilePath()).to.equal(utils_1.FileUtils.getStandardizedAbsolutePath("NewFolder/NewFile.ts"));
});
});
describe("absolute source file", () => {
it("should not be saved", () => {
chai_1.expect(absoluteSourceFile.isSaved()).to.be.false;
});
it("should have have the same text", () => {
chai_1.expect(absoluteSourceFile.getFullText()).to.equal(fileText);
});
it("should have the expected path", () => {
chai_1.expect(absoluteSourceFile.getFilePath()).to.equal(utils_1.FileUtils.getStandardizedAbsolutePath("NewFile.ts"));
});
});
});
describe("save", () => {
const fileText = " interface Identifier {} ";
const filePath = utils_1.FileUtils.getStandardizedAbsolutePath("/Folder/File.ts");
const host = testHelpers_2.getFileSystemHostWithFiles([]);
const { sourceFile } = testHelpers_1.getInfoFromText(fileText, { filePath, host });
it("should save the file", () => __awaiter(this, void 0, void 0, function* () {
chai_1.expect(sourceFile.isSaved()).to.be.false;
yield sourceFile.save();
chai_1.expect(sourceFile.isSaved()).to.be.true;
const writeLog = host.getWriteLog();
const entry = writeLog[0];
chai_1.expect(entry.filePath).to.equal(filePath);
chai_1.expect(entry.fileText).to.equal(fileText);
chai_1.expect(writeLog.length).to.equal(1);
chai_1.expect(host.getCreatedDirectories()).to.deep.equal([utils_1.FileUtils.getDirPath(filePath)]);
}));
});
describe("isSaved", () => {
const filePath = utils_1.FileUtils.getStandardizedAbsolutePath("/Folder/File.ts");
it("should not be saved after doing an action that will replace the tree", () => {
const host = testHelpers_2.getFileSystemHostWithFiles([]);
const { sourceFile } = testHelpers_1.getInfoFromText("class MyClass {}", { filePath, host });
chai_1.expect(sourceFile.isSaved()).to.be.false;
sourceFile.saveSync();
chai_1.expect(sourceFile.isSaved()).to.be.true;
sourceFile.addClass({ name: "NewClass" });
chai_1.expect(sourceFile.isSaved()).to.be.false;
});
it("should not be saved after doing an action that changes only the text", () => {
const host = testHelpers_2.getFileSystemHostWithFiles([]);
const { sourceFile } = testHelpers_1.getInfoFromText("class MyClass {}", { filePath, host });
chai_1.expect(sourceFile.isSaved()).to.be.false;
sourceFile.saveSync();
chai_1.expect(sourceFile.isSaved()).to.be.true;
sourceFile.getClasses()[0].rename("NewClassName");
chai_1.expect(sourceFile.isSaved()).to.be.false;
});
});
describe("saveSync", () => {
const fileText = " interface Identifier {} ";
const filePath = utils_1.FileUtils.getStandardizedAbsolutePath("/Folder/File.ts");
const host = testHelpers_2.getFileSystemHostWithFiles([]);
const { sourceFile } = testHelpers_1.getInfoFromText(fileText, { filePath, host });
it("should save the file", () => {
chai_1.expect(sourceFile.isSaved()).to.be.false;
sourceFile.saveSync();
chai_1.expect(sourceFile.isSaved()).to.be.true;
const writeLog = host.getSyncWriteLog();
const entry = writeLog[0];
chai_1.expect(entry.filePath).to.equal(filePath);
chai_1.expect(entry.fileText).to.equal(fileText);
chai_1.expect(writeLog.length).to.equal(1);
chai_1.expect(host.getCreatedDirectories()).to.deep.equal([utils_1.FileUtils.getDirPath(filePath)]);
});
});
describe("isDeclarationFile", () => {
it("should be a source file when the file name ends with .d.ts", () => {
const ast = new TsSimpleAst_1.TsSimpleAst();
const sourceFile = ast.addSourceFileFromText("MyFile.d.ts", "");
chai_1.expect(sourceFile.isDeclarationFile()).to.be.true;
});
it("should not be a source file when the file name ends with .ts", () => {
const ast = new TsSimpleAst_1.TsSimpleAst();
const sourceFile = ast.addSourceFileFromText("MyFile.ts", "");
chai_1.expect(sourceFile.isDeclarationFile()).to.be.false;
});
});
describe("insertImports", () => {
function doTest(startCode, index, structures, expectedCode) {
const { sourceFile } = testHelpers_1.getInfoFromText(startCode);
const result = sourceFile.insertImports(index, structures);
chai_1.expect(result.length).to.equal(structures.length);
chai_1.expect(sourceFile.getText()).to.equal(expectedCode);
}
it("should insert the different kinds of imports", () => {
doTest("", 0, [
{ moduleSpecifier: "./test" },
{ defaultImport: "identifier", moduleSpecifier: "./test" },
{ defaultImport: "identifier", namespaceImport: "name", moduleSpecifier: "./test" },
{ defaultImport: "identifier", namedImports: [{ name: "name" }, { name: "name", alias: "alias" }], moduleSpecifier: "./test" },
{ namedImports: [{ name: "name" }], moduleSpecifier: "./test" },
{ namespaceImport: "name", moduleSpecifier: "./test" }
], [
`import "./test";`,
`import identifier from "./test";`,
`import identifier, * as name from "./test";`,
`import identifier, {name, name as alias} from "./test";`,
`import {name} from "./test";`,
`import * as name from "./test";`
].join("\n") + "\n");
});
it("should throw when specifying a namespace import and named imports", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("");
chai_1.expect(() => {
sourceFile.insertImports(0, [{ namespaceImport: "name", namedImports: [{ name: "name" }], moduleSpecifier: "file" }]);
}).to.throw();
});
it("should insert at the beginning", () => {
doTest(`export class Class {}\n`, 0, [{ moduleSpecifier: "./test" }], `import "./test";\n\nexport class Class {}\n`);
});
it("should insert in the middle", () => {
doTest(`import "./file1";\nimport "./file3";\n`, 1, [{ moduleSpecifier: "./file2" }], `import "./file1";\nimport "./file2";\nimport "./file3";\n`);
});
it("should insert at the end", () => {
doTest(`export class Class {}\n`, 1, [{ moduleSpecifier: "./test" }], `export class Class {}\n\nimport "./test";\n`);
});
});
describe("insertImport", () => {
function doTest(startCode, index, structure, expectedCode) {
const { sourceFile } = testHelpers_1.getInfoFromText(startCode);
const result = sourceFile.insertImport(index, structure);
chai_1.expect(result).to.be.instanceOf(compiler_1.ImportDeclaration);
chai_1.expect(sourceFile.getText()).to.equal(expectedCode);
}
it("should insert at the specified position", () => {
doTest(`import "./file1";\nimport "./file3";\n`, 1, { moduleSpecifier: "./file2" }, `import "./file1";\nimport "./file2";\nimport "./file3";\n`);
});
});
describe("addImport", () => {
function doTest(startCode, structure, expectedCode) {
const { sourceFile } = testHelpers_1.getInfoFromText(startCode);
const result = sourceFile.addImport(structure);
chai_1.expect(result).to.be.instanceOf(compiler_1.ImportDeclaration);
chai_1.expect(sourceFile.getText()).to.equal(expectedCode);
}
it("should add at the last import if one exists", () => {
doTest(`import "./file1";\nimport "./file2";\n\nexport class MyClass {}\n`, { moduleSpecifier: "./file3" }, `import "./file1";\nimport "./file2";\nimport "./file3";\n\nexport class MyClass {}\n`);
});
it("should add at the start if no imports exists", () => {
doTest(`export class MyClass {}\n`, { moduleSpecifier: "./file" }, `import "./file";\n\nexport class MyClass {}\n`);
});
});
describe("addImports", () => {
function doTest(startCode, structures, expectedCode) {
const { sourceFile } = testHelpers_1.getInfoFromText(startCode);
const result = sourceFile.addImports(structures);
chai_1.expect(result.length).to.equal(structures.length);
chai_1.expect(sourceFile.getText()).to.equal(expectedCode);
}
it("should add at the last import if one exists", () => {
doTest(`import "./file1";\n\nexport class MyClass {}\n`, [{ moduleSpecifier: "./file2" }, { moduleSpecifier: "./file3" }], `import "./file1";\nimport "./file2";\nimport "./file3";\n\nexport class MyClass {}\n`);
});
it("should add at the start if no imports exists", () => {
doTest(`export class MyClass {}\n`, [{ moduleSpecifier: "./file1" }, { moduleSpecifier: "./file2" }], `import "./file1";\nimport "./file2";\n\nexport class MyClass {}\n`);
});
});
describe("getImports", () => {
it("should get the import declarations", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("import myImport from 'test'; import {next} from './test';");
chai_1.expect(sourceFile.getImports().length).to.equal(2);
chai_1.expect(sourceFile.getImports()[0]).to.be.instanceOf(compiler_1.ImportDeclaration);
});
});
describe("getImport", () => {
it("should get the import declaration", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("import myImport from 'test'; import {next} from './test';");
chai_1.expect(sourceFile.getImport(i => i.getDefaultImport() != null).getText()).to.equal("import myImport from 'test';");
});
});
describe("getImportOrThrow", () => {
it("should get the import declaration", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("import myImport from 'test'; import {next} from './test';");
chai_1.expect(sourceFile.getImportOrThrow(i => i.getDefaultImport() != null).getText()).to.equal("import myImport from 'test';");
});
it("should throw when not exists", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("");
chai_1.expect(() => sourceFile.getImportOrThrow(e => false)).to.throw();
});
});
describe("insertExports", () => {
function doTest(startCode, index, structures, expectedCode) {
const { sourceFile } = testHelpers_1.getInfoFromText(startCode);
const result = sourceFile.insertExports(index, structures);
chai_1.expect(result.length).to.equal(structures.length);
chai_1.expect(sourceFile.getText()).to.equal(expectedCode);
}
it("should insert the different kinds of exports", () => {
doTest("", 0, [
{ moduleSpecifier: "./test" },
{ namedExports: [{ name: "name" }, { name: "name", alias: "alias" }], moduleSpecifier: "./test" },
{ namedExports: [{ name: "name" }] },
{}
], [
`export * from "./test";`,
`export {name, name as alias} from "./test";`,
`export {name};`,
`export {};`
].join("\n") + "\n");
});
it("should insert at the beginning", () => {
doTest(`export class Class {}\n`, 0, [{ moduleSpecifier: "./test" }], `export * from "./test";\n\nexport class Class {}\n`);
});
it("should insert in the middle", () => {
doTest(`export * from "./file1";\nexport * from "./file3";\n`, 1, [{ moduleSpecifier: "./file2" }], `export * from "./file1";\nexport * from "./file2";\nexport * from "./file3";\n`);
});
it("should insert at the end", () => {
doTest(`export class Class {}\n`, 1, [{ moduleSpecifier: "./test" }], `export class Class {}\n\nexport * from "./test";\n`);
});
});
describe("insertExport", () => {
function doTest(startCode, index, structure, expectedCode) {
const { sourceFile } = testHelpers_1.getInfoFromText(startCode);
const result = sourceFile.insertExport(index, structure);
chai_1.expect(result).to.be.instanceOf(compiler_1.ExportDeclaration);
chai_1.expect(sourceFile.getText()).to.equal(expectedCode);
}
it("should insert at the specified position", () => {
doTest(`export * from "./file1";\nexport * from "./file3";\n`, 1, { moduleSpecifier: "./file2" }, `export * from "./file1";\nexport * from "./file2";\nexport * from "./file3";\n`);
});
});
describe("addExport", () => {
function doTest(startCode, structure, expectedCode) {
const { sourceFile } = testHelpers_1.getInfoFromText(startCode);
const result = sourceFile.addExport(structure);
chai_1.expect(result).to.be.instanceOf(compiler_1.ExportDeclaration);
chai_1.expect(sourceFile.getText()).to.equal(expectedCode);
}
it("should always add at the end of the file", () => {
doTest(`export class MyClass {}\n`, { moduleSpecifier: "./file" }, `export class MyClass {}\n\nexport * from "./file";\n`);
});
});
describe("addExports", () => {
function doTest(startCode, structures, expectedCode) {
const { sourceFile } = testHelpers_1.getInfoFromText(startCode);
const result = sourceFile.addExports(structures);
chai_1.expect(result.length).to.equal(structures.length);
chai_1.expect(sourceFile.getText()).to.equal(expectedCode);
}
it("should add multiple", () => {
doTest(`export class MyClass {}\n`, [{ moduleSpecifier: "./file1" }, { moduleSpecifier: "./file2" }], `export class MyClass {}\n\nexport * from "./file1";\nexport * from "./file2";\n`);
});
});
describe("getExports", () => {
it("should get the export declarations", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("export * from 'test'; export {next} from './test';");
chai_1.expect(sourceFile.getExports().length).to.equal(2);
chai_1.expect(sourceFile.getExports()[0]).to.be.instanceOf(compiler_1.ExportDeclaration);
});
});
describe("getExport", () => {
it("should get the export declaration", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("export * from 'test'; export {next} from './test';");
chai_1.expect(sourceFile.getExport(e => e.isNamespaceExport()).getText()).to.equal("export * from 'test';");
});
});
describe("getExportOrThrow", () => {
it("should get the export declaration", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("export * from 'test'; export {next} from './test';");
chai_1.expect(sourceFile.getExportOrThrow(e => e.isNamespaceExport()).getText()).to.equal("export * from 'test';");
});
it("should throw when not exists", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("");
chai_1.expect(() => sourceFile.getExportOrThrow(e => false)).to.throw();
});
});
describe("getDefaultExportSymbol", () => {
it("should return undefined when there's no default export", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("");
chai_1.expect(sourceFile.getDefaultExportSymbol()).to.be.undefined;
});
it("should return the default export symbol when one exists", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("export default class Identifier {}");
const defaultExportSymbol = sourceFile.getDefaultExportSymbol();
chai_1.expect(defaultExportSymbol.getName()).to.equal("default");
});
it("should return the default export symbol when default exported on a separate statement", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("class Identifier {}\nexport default Identifier;");
const defaultExportSymbol = sourceFile.getDefaultExportSymbol();
chai_1.expect(defaultExportSymbol.getName()).to.equal("default");
});
});
describe("getDefaultExportSymbolOrThrow", () => {
it("should throw when there's no default export", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("");
chai_1.expect(() => sourceFile.getDefaultExportSymbolOrThrow()).to.throw();
});
it("should return the default export symbol when one exists", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("export default class Identifier {}");
chai_1.expect(sourceFile.getDefaultExportSymbolOrThrow().getName()).to.equal("default");
});
});
describe("removeDefaultExport", () => {
it("should do nothing when there's no default export", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("");
sourceFile.removeDefaultExport();
chai_1.expect(sourceFile.getFullText()).to.equal("");
});
it("should return the default export symbol when one exists", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("export default class Identifier {}");
sourceFile.removeDefaultExport();
chai_1.expect(sourceFile.getFullText()).to.equal("class Identifier {}");
});
it("should return the default export symbol when default exported on a separate statement", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("namespace Identifier {}\nclass Identifier {}\nexport default Identifier;\n");
sourceFile.removeDefaultExport();
chai_1.expect(sourceFile.getFullText()).to.equal("namespace Identifier {}\nclass Identifier {}\n");
});
});
describe("getLanguageVariant", () => {
it("should return standard when in a ts file", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("");
chai_1.expect(sourceFile.getLanguageVariant()).to.equal(ts.LanguageVariant.Standard);
});
it("should return jsx when in a tsx file", () => {
const { sourceFile } = testHelpers_1.getInfoFromText("", { filePath: "file.tsx" });
chai_1.expect(sourceFile.getLanguageVariant()).to.equal(ts.LanguageVariant.JSX);
});
});
describe("emit", () => {
it("should emit the source file", () => {
const fileSystem = testHelpers_2.getFileSystemHostWithFiles([]);
const ast = new TsSimpleAst_1.TsSimpleAst({ compilerOptions: { noLib: true, outDir: "dist" } }, fileSystem);
const sourceFile = ast.addSourceFileFromText("file1.ts", "const num1 = 1;");
ast.addSourceFileFromText("file2.ts", "const num2 = 2;");
const result = sourceFile.emit();
chai_1.expect(result).to.be.instanceof(compiler_1.EmitResult);
const writeLog = fileSystem.getSyncWriteLog();
chai_1.expect(writeLog[0].filePath).to.equal("dist/file1.js");
chai_1.expect(writeLog[0].fileText).to.equal("var num1 = 1;\n");
chai_1.expect(writeLog.length).to.equal(1);
});
it("should create the directories when emitting", () => {
const fileSystem = testHelpers_2.getFileSystemHostWithFiles([]);
const ast = new TsSimpleAst_1.TsSimpleAst({ compilerOptions: { noLib: true, outDir: "dist/subdir" } }, fileSystem);
const sourceFile = ast.addSourceFileFromText("/test.ts", "const num1 = 1;");
ast.emit();
const directories = fileSystem.getCreatedDirectories();
chai_1.expect(directories).to.deep.equal(["dist", "dist/subdir"]);
});
});
describe("fill", () => {
function doTest(startingCode, structure, expectedCode) {
const { sourceFile } = testHelpers_1.getInfoFromText(startingCode);
sourceFile.fill(structure);
chai_1.expect(sourceFile.getText()).to.equal(expectedCode);
}
it("should not modify anything if the structure doesn't change anything", () => {
doTest("", {}, "");
});
it("should modify when changed", () => {
const structure = {
imports: [{ moduleSpecifier: "module" }],
exports: [{ moduleSpecifier: "export-module" }]
};
doTest("", structure, `import "module";\n\nexport * from "export-module";\n`);
});
});
describe("formatText", () => {
function doTest(startingCode, expectedCode) {
const { sourceFile } = testHelpers_1.getInfoFromText(startingCode);
sourceFile.formatText();
chai_1.expect(sourceFile.getText()).to.equal(expectedCode);
}
it("should format the text when it contains different spacing", () => {
doTest("class MyClass{}", "class MyClass {\n}\n");
});
it("should format the text when it contains multiple semi colons", () => {
doTest("var myTest: string;;;;", "var myTest: string;\n;\n;\n;\n");
});
it("should format the text when it contains syntax errors", () => {
// wow, it's impressive it does this
doTest("function myTest(}{{{{}}) {}", "function myTest({}, {}, {}, {}) { }\n");
});
it("should format the text in the documentation as described", () => {
doTest(`var myVariable : string | number;
function myFunction(param : MyClass){
return "";
}
`, `var myVariable: string | number;
function myFunction(param: MyClass) {
return "";
}
`);
});
it("should dispose of any previous descendants", () => {
const { sourceFile, firstChild } = testHelpers_1.getInfoFromText("function test {}");
sourceFile.formatText();
chai_1.expect(() => firstChild.compilerNode).to.throw();
});
});
});
//# sourceMappingURL=sourceFileTests.js.map