Comparing version 1.0.61 to 1.0.62
"use strict"; | ||
var ts = require("typescript"); | ||
var ts = require('typescript'); | ||
var configuration_1 = require('./configuration'); | ||
var traverse_ast_1 = require('./traverse-ast'); | ||
var mutable_source_code_1 = require('./mutable-source-code'); | ||
var transpiler_context_1 = require("./transpiler-context"); | ||
var chainable_hosts_1 = require("./chainable-hosts"); | ||
var transformer_1 = require("./transformer"); | ||
var transpiler_context_1 = require('./transpiler-context'); | ||
var chainable_hosts_1 = require('./chainable-hosts'); | ||
var transformer_1 = require('./transformer'); | ||
function applyVisitor(source, visitor) { | ||
var ast = ts.createSourceFile("test.ts", source, configuration_1.defaultCompilerOptions.target, true); | ||
var ast = ts.createSourceFile('test.ts', source, configuration_1.defaultCompilerOptions.target, true); | ||
return applyVisitorOnAst(ast, visitor); | ||
@@ -12,0 +12,0 @@ } |
@@ -8,5 +8,5 @@ "use strict"; | ||
var ts = require('typescript'); | ||
var transformer_1 = require("./transformer"); | ||
var hosts_base_1 = require("./hosts-base"); | ||
var configuration_1 = require("./configuration"); | ||
var transformer_1 = require('./transformer'); | ||
var hosts_base_1 = require('./hosts-base'); | ||
var configuration_1 = require('./configuration'); | ||
var normalizePath = ts['normalizePath']; | ||
@@ -13,0 +13,0 @@ var getDirectoryPath = ts['getDirectoryPath']; |
@@ -21,3 +21,2 @@ "use strict"; | ||
HostBase.prototype.writeFile = function (name, text, writeByteOrderMark) { }; | ||
/// | ||
HostBase.prototype.useCaseSensitiveFileNames = function () { | ||
@@ -30,9 +29,9 @@ return false; | ||
HostBase.prototype.getCurrentDirectory = function () { | ||
return ""; | ||
return ''; | ||
}; | ||
HostBase.prototype.getNewLine = function () { | ||
return "\n"; | ||
return '\n'; | ||
}; | ||
HostBase.prototype.getDefaultLibFileName = function (options) { | ||
return "lib.d.ts"; | ||
return 'lib.d.ts'; | ||
}; | ||
@@ -39,0 +38,0 @@ HostBase.prototype.getCancellationToken = function () { |
@@ -1,2 +0,1 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
"use strict"; | ||
@@ -9,4 +8,4 @@ var __extends = (this && this.__extends) || function (d, b) { | ||
var ts = require('typescript'); | ||
var hosts_base_1 = require("./hosts-base"); | ||
var configuration_1 = require("./configuration"); | ||
var hosts_base_1 = require('./hosts-base'); | ||
var configuration_1 = require('./configuration'); | ||
function fileExtensionIs(path, extension) { | ||
@@ -13,0 +12,0 @@ var pathLen = path.length; |
@@ -1,2 +0,2 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
/// <reference path='../typings/main.d.ts' /> | ||
"use strict"; | ||
@@ -6,10 +6,10 @@ var transpile_1 = require('./transpile'); | ||
exports.validateAll = transpile_1.validateAll; | ||
var apply_visitor_1 = require("./apply-visitor"); | ||
var apply_visitor_1 = require('./apply-visitor'); | ||
exports.applyVisitor = apply_visitor_1.applyVisitor; | ||
exports.applyVisitorOnAst = apply_visitor_1.applyVisitorOnAst; | ||
exports.applyVisitorOnHostedSource = apply_visitor_1.applyVisitorOnHostedSource; | ||
var hosts_1 = require("./hosts"); | ||
var hosts_1 = require('./hosts'); | ||
exports.MultipleFilesHost = hosts_1.MultipleFilesHost; | ||
var traverse_ast_1 = require("./traverse-ast"); | ||
var traverse_ast_1 = require('./traverse-ast'); | ||
exports.traverseAst = traverse_ast_1.traverseAst; | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,1 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
"use strict"; | ||
@@ -8,4 +7,4 @@ var __extends = (this && this.__extends) || function (d, b) { | ||
}; | ||
var ts = require('typescript'); | ||
var source_map_1 = require('source-map'); | ||
var ts = require('typescript'); | ||
var MagicString = require('magic-string'); | ||
@@ -12,0 +11,0 @@ var MappedAction = (function () { |
"use strict"; | ||
var transpiler_context_1 = require("./transpiler-context"); | ||
var transpiler_context_1 = require('./transpiler-context'); | ||
var traverse_ast_1 = require('./traverse-ast'); | ||
var mutable_source_code_1 = require("./mutable-source-code"); | ||
var mutable_source_code_1 = require('./mutable-source-code'); | ||
var VisitorBasedTransformer = (function () { | ||
@@ -6,0 +6,0 @@ function VisitorBasedTransformer(visitors, languageServiceProvider) { |
@@ -1,13 +0,12 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
"use strict"; | ||
var ts = require('typescript'); | ||
var hosts_1 = require('./hosts'); | ||
var traverse_ast_1 = require('./traverse-ast'); | ||
var mutable_source_code_1 = require('./mutable-source-code'); | ||
var ts = require('typescript'); | ||
var transpiler_context_1 = require("./transpiler-context"); | ||
var configuration_1 = require("./configuration"); | ||
var chainable_hosts_1 = require("./chainable-hosts"); | ||
var chainable_hosts_2 = require("./chainable-hosts"); | ||
var hosts_base_1 = require("./hosts-base"); | ||
var chainable_hosts_3 = require("./chainable-hosts"); | ||
var transpiler_context_1 = require('./transpiler-context'); | ||
var configuration_1 = require('./configuration'); | ||
var chainable_hosts_1 = require('./chainable-hosts'); | ||
var chainable_hosts_2 = require('./chainable-hosts'); | ||
var hosts_base_1 = require('./hosts-base'); | ||
var chainable_hosts_3 = require('./chainable-hosts'); | ||
function getParserErrors(sourceFile) { | ||
@@ -14,0 +13,0 @@ // We're accessing here an internal property. It would be more legit to access it through |
@@ -1,7 +0,6 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
"use strict"; | ||
var mutable_source_code_1 = require('./mutable-source-code'); | ||
var mutable_source_code_2 = require("./mutable-source-code"); | ||
var mutable_source_code_3 = require("./mutable-source-code"); | ||
var mutable_source_code_4 = require("./mutable-source-code"); | ||
var mutable_source_code_2 = require('./mutable-source-code'); | ||
var mutable_source_code_3 = require('./mutable-source-code'); | ||
var mutable_source_code_4 = require('./mutable-source-code'); | ||
var TranspilerContext = (function () { | ||
@@ -8,0 +7,0 @@ function TranspilerContext(_fileName, langServiceProvider) { |
@@ -1,11 +0,10 @@ | ||
import * as ts from 'typescript'; | ||
import {RawSourceMap, Visitor} from "./index"; | ||
import {RawSourceMap, Visitor} from './index'; | ||
export interface HostBase extends ts.CompilerHost {} | ||
export class HostBase implements HostBase {} | ||
export interface HostBase extends ts.CompilerHost { } | ||
export class HostBase implements HostBase { } | ||
export class ChainableHost extends HostBase { | ||
setSource(source: ts.CompilerHost): void; | ||
setSource(source: ts.CompilerHost): void; | ||
} | ||
@@ -16,30 +15,29 @@ | ||
export class AstCacheHost extends ChainableHost { | ||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile; | ||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile; | ||
} | ||
export class TransformationHost extends ChainableHost { | ||
constructor(visitors: Visitor[], languageServiceProvider?: () => ts.LanguageService); | ||
getSourceMap(fileName: string): RawSourceMap; | ||
translateDiagnostic(diagnostic: ts.Diagnostic): ts.Diagnostic; | ||
constructor(visitors: Visitor[], languageServiceProvider?: () => ts.LanguageService); | ||
getSourceMap(fileName: string): RawSourceMap; | ||
translateDiagnostic(diagnostic: ts.Diagnostic): ts.Diagnostic; | ||
} | ||
export interface SemanticHost extends ts.LanguageServiceHost, ts.CompilerHost, ts.DocumentRegistry { | ||
getCancellationToken(): ts.CancellationToken; | ||
getNewLine(): string; | ||
useCaseSensitiveFileNames(); | ||
getCancellationToken(): ts.CancellationToken; | ||
getNewLine(): string; | ||
useCaseSensitiveFileNames(); | ||
} | ||
export class SemanticHost extends ChainableHost { | ||
constructor(files: string[], compilerOptions?: ts.CompilerOptions); | ||
constructor(files: string[], compilerOptions?: ts.CompilerOptions); | ||
} | ||
export class MultipleFilesHost extends HostBase { | ||
constructor(resolutionHosts: ts.ModuleResolutionHost[], compilerOptions?: ts.CompilerOptions) | ||
getSyntacticErrors(): ts.Diagnostic[]; | ||
constructor(resolutionHosts: ts.ModuleResolutionHost[], compilerOptions?: ts.CompilerOptions) | ||
getSyntacticErrors(): ts.Diagnostic[]; | ||
} | ||
export class SingleFileHost extends HostBase { | ||
constructor(ast: ts.SourceFile); | ||
public output: string; | ||
public sourceMap: RawSourceMap; | ||
constructor(ast: ts.SourceFile); | ||
public output: string; | ||
public sourceMap: RawSourceMap; | ||
} | ||
@@ -1,20 +0,20 @@ | ||
import * as ts from "typescript"; | ||
import * as ts from 'typescript'; | ||
interface StartOfSourceMap { | ||
file?: string; | ||
sourceRoot?: string; | ||
file?: string; | ||
sourceRoot?: string; | ||
} | ||
export interface RawSourceMap extends StartOfSourceMap { | ||
version: string; | ||
sources: Array<string>; | ||
names: Array<string>; | ||
sourcesContent?: string; | ||
mappings: string; | ||
version: string; | ||
sources: Array<string>; | ||
names: Array<string>; | ||
sourcesContent?: string; | ||
mappings: string; | ||
} | ||
export interface Replacement { | ||
start: number; | ||
end: number; | ||
str: string; | ||
start: number; | ||
end: number; | ||
str: string; | ||
} | ||
@@ -36,10 +36,10 @@ | ||
export interface VisitorContext { | ||
fileName: string; | ||
halted: boolean; | ||
insertLine(position: number, str: string): void; | ||
replace(start: number, end: number, str: string): void; | ||
fastAppend(str: string): void; | ||
fastRewrite(start: number, str: string): void; | ||
reportDiag(node: ts.Node, category: ts.DiagnosticCategory, message: string, halt?: boolean): void; | ||
getLanguageService(): ts.LanguageService; | ||
fileName: string; | ||
halted: boolean; | ||
insertLine(position: number, str: string): void; | ||
replace(start: number, end: number, str: string): void; | ||
fastAppend(str: string): void; | ||
fastRewrite(start: number, str: string): void; | ||
reportDiag(node: ts.Node, category: ts.DiagnosticCategory, message: string, halt?: boolean): void; | ||
getLanguageService(): ts.LanguageService; | ||
} | ||
@@ -49,3 +49,3 @@ | ||
filter(node: ts.Node): boolean; | ||
visit(node: ts.Node, context: VisitorContext, traverse: (...visitors: Visitor[])=> void): void; | ||
visit(node: ts.Node, context: VisitorContext, traverse: (...visitors: Visitor[]) => void): void; | ||
} | ||
@@ -56,5 +56,5 @@ | ||
export interface ApplyVisitorResult { | ||
file: ts.SourceFile, | ||
code: string; | ||
diags: ts.Diagnostic[]; | ||
file: ts.SourceFile, | ||
code: string; | ||
diags: ts.Diagnostic[]; | ||
} | ||
@@ -69,5 +69,5 @@ | ||
export interface ValidatorConfig { | ||
resolutionHosts?: ts.ModuleResolutionHost[]; | ||
visitors?: Visitor[]; | ||
mutators?: Visitor[]; | ||
resolutionHosts?: ts.ModuleResolutionHost[]; | ||
visitors?: Visitor[]; | ||
mutators?: Visitor[]; | ||
} | ||
@@ -74,0 +74,0 @@ |
{ | ||
"private": false, | ||
"name": "tspoon", | ||
"version": "1.0.61", | ||
"version": "1.0.62", | ||
"description": "see README.md", | ||
@@ -12,3 +12,3 @@ "main": "./dist/src/index.js", | ||
"scripts": { | ||
"clean": "rm -rf dist && mkdir dist", | ||
"clean": "rimraf dist && mkdir dist", | ||
"pretest": "npm run build", | ||
@@ -54,4 +54,4 @@ "test": "npm run test:node", | ||
"raw-loader": "0.5.1", | ||
"rimraf": "2.5.2", | ||
"source-map-support": "0.4.0", | ||
"tsd": "0.6.5", | ||
"typings": "0.7.12", | ||
@@ -62,3 +62,3 @@ "webpack": "1.12.14", | ||
"dependencies": { | ||
"lodash": "4.7.0", | ||
"lodash": "4.8.2", | ||
"magic-string": "0.10.2", | ||
@@ -65,0 +65,0 @@ "source-map": "0.5.3", |
@@ -1,11 +0,10 @@ | ||
import * as ts from "typescript"; | ||
import * as ts from 'typescript'; | ||
import {defaultCompilerOptions} from './configuration'; | ||
import {traverseAst} from './traverse-ast'; | ||
import {Visitor} from './visitor'; | ||
import {Action, MutableSourceCode} from './mutable-source-code'; | ||
import {TranspilerContext} from './transpiler-context'; | ||
import {SemanticHost} from './chainable-hosts'; | ||
import {VisitorBasedTransformer, CodeTransformer} from './transformer'; | ||
import { defaultCompilerOptions } from './configuration'; | ||
import { traverseAst } from './traverse-ast'; | ||
import { Visitor } from "./visitor"; | ||
import { Action, MutableSourceCode } from './mutable-source-code'; | ||
import { TranspilerContext } from "./transpiler-context"; | ||
import {SemanticHost} from "./chainable-hosts"; | ||
import {VisitorBasedTransformer, CodeTransformer} from "./transformer"; | ||
export interface ApplyVisitorResult { | ||
@@ -20,3 +19,3 @@ file: ts.SourceFile, | ||
const ast = ts.createSourceFile("test.ts", source, defaultCompilerOptions.target, true); | ||
const ast = ts.createSourceFile('test.ts', source, defaultCompilerOptions.target, true); | ||
return applyVisitorOnAst(ast, visitor); | ||
@@ -26,14 +25,13 @@ } | ||
export function applyVisitorOnHostedSource(file: string, visitors: Visitor[], host: ts.CompilerHost): string { | ||
const langService = host instanceof SemanticHost ? ts.createLanguageService(host, host) : null; | ||
const transformer: CodeTransformer = new VisitorBasedTransformer(visitors, () => langService); | ||
const ast: ts.SourceFile = host.getSourceFile(file, defaultCompilerOptions.target); | ||
if(ast) { | ||
const mutableSourceCode: MutableSourceCode = transformer.transform(ast); | ||
return mutableSourceCode.code; | ||
} else { | ||
return null; | ||
} | ||
const langService = host instanceof SemanticHost ? ts.createLanguageService(host, host) : null; | ||
const transformer: CodeTransformer = new VisitorBasedTransformer(visitors, () => langService); | ||
const ast: ts.SourceFile = host.getSourceFile(file, defaultCompilerOptions.target); | ||
if (ast) { | ||
const mutableSourceCode: MutableSourceCode = transformer.transform(ast); | ||
return mutableSourceCode.code; | ||
} else { | ||
return null; | ||
} | ||
} | ||
export function applyVisitorOnAst(ast: ts.SourceFile, visitor: Visitor): ApplyVisitorResult { | ||
@@ -40,0 +38,0 @@ |
@@ -1,2 +0,1 @@ | ||
export default function binarySearch(array: number[], value: number): number { | ||
@@ -3,0 +2,0 @@ let low = 0; |
@@ -1,10 +0,10 @@ | ||
import {chainHosts} from "./hosts-base"; | ||
import * as ts from 'typescript'; | ||
import {CodeTransformer, VisitorBasedTransformer} from "./transformer"; | ||
import {ChainableHost} from "./hosts-base"; | ||
import {MutableSourceCode} from "./mutable-source-code"; | ||
import {chainHosts} from './hosts-base'; | ||
import {CodeTransformer, VisitorBasedTransformer} from './transformer'; | ||
import {ChainableHost} from './hosts-base'; | ||
import {MutableSourceCode} from './mutable-source-code'; | ||
import RawSourceMap = SourceMap.RawSourceMap; | ||
import {defaultCompilerOptions} from "./configuration"; | ||
import {MultipleFilesHost} from "./hosts"; | ||
import {Visitor} from "./visitor"; | ||
import {defaultCompilerOptions} from './configuration'; | ||
import {MultipleFilesHost} from './hosts'; | ||
import {Visitor} from './visitor'; | ||
@@ -16,14 +16,14 @@ const normalizePath: { (path: string): string } = ts['normalizePath']; | ||
export class AstCacheHost extends ChainableHost { | ||
private cache: { [fileName: string]: ts.SourceFile } = {}; | ||
private cache: { [fileName: string]: ts.SourceFile } = {}; | ||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile { | ||
const cachedAst: ts.SourceFile = this.cache[fileName]; | ||
if(!cachedAst) { | ||
const ast = this.source.getSourceFile(fileName, languageVersion, onError); | ||
this.cache[fileName] = ast; | ||
return ast; | ||
} else { | ||
return cachedAst; | ||
} | ||
} | ||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile { | ||
const cachedAst: ts.SourceFile = this.cache[fileName]; | ||
if (!cachedAst) { | ||
const ast = this.source.getSourceFile(fileName, languageVersion, onError); | ||
this.cache[fileName] = ast; | ||
return ast; | ||
} else { | ||
return cachedAst; | ||
} | ||
} | ||
@@ -33,101 +33,101 @@ } | ||
export class TransformationHost extends ChainableHost { | ||
private transformations: { [fileName: string]: MutableSourceCode } = {}; | ||
private transformer: CodeTransformer; | ||
private transformations: { [fileName: string]: MutableSourceCode } = {}; | ||
private transformer: CodeTransformer; | ||
constructor(visitors: Visitor[], languageServiceProvider: () => ts.LanguageService = () => null) { | ||
super(); | ||
this.transformer = new VisitorBasedTransformer(visitors, languageServiceProvider); | ||
} | ||
constructor(visitors: Visitor[], languageServiceProvider: () => ts.LanguageService = () => null) { | ||
super(); | ||
this.transformer = new VisitorBasedTransformer(visitors, languageServiceProvider); | ||
} | ||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile { | ||
const ast: ts.SourceFile = super.getSourceFile(fileName, languageVersion, onError); | ||
if(ast) { | ||
const transformation = this.transformer.transform(ast); | ||
this.transformations[ast.fileName] = transformation; | ||
return transformation.ast; | ||
} else { | ||
return null; | ||
} | ||
} | ||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile { | ||
const ast: ts.SourceFile = super.getSourceFile(fileName, languageVersion, onError); | ||
if (ast) { | ||
const transformation = this.transformer.transform(ast); | ||
this.transformations[ast.fileName] = transformation; | ||
return transformation.ast; | ||
} else { | ||
return null; | ||
} | ||
} | ||
getSourceMap(fileName: string): RawSourceMap { | ||
const transformation: MutableSourceCode = this.transformations[fileName]; | ||
if(transformation) { | ||
return transformation.sourceMap; | ||
} else { | ||
return null; | ||
} | ||
} | ||
getSourceMap(fileName: string): RawSourceMap { | ||
const transformation: MutableSourceCode = this.transformations[fileName]; | ||
if (transformation) { | ||
return transformation.sourceMap; | ||
} else { | ||
return null; | ||
} | ||
} | ||
translateDiagnostic(diagnostic: ts.Diagnostic): ts.Diagnostic { | ||
const transformation = this.transformations[diagnostic.file.fileName]; | ||
return transformation ? transformation.translateDiagnostic(diagnostic) : diagnostic; | ||
} | ||
translateDiagnostic(diagnostic: ts.Diagnostic): ts.Diagnostic { | ||
const transformation = this.transformations[diagnostic.file.fileName]; | ||
return transformation ? transformation.translateDiagnostic(diagnostic) : diagnostic; | ||
} | ||
} | ||
export class SemanticHost extends ChainableHost implements ts.LanguageServiceHost, ts.CompilerHost, ts.DocumentRegistry { | ||
constructor( | ||
private files: string[], | ||
private compilerOptions: ts.CompilerOptions = defaultCompilerOptions | ||
) { | ||
super(); | ||
} | ||
constructor( | ||
private files: string[], | ||
private compilerOptions: ts.CompilerOptions = defaultCompilerOptions | ||
) { | ||
super(); | ||
} | ||
getProjectVersion():string { | ||
return null; | ||
} | ||
getProjectVersion(): string { | ||
return null; | ||
} | ||
getScriptFileNames():string[]{ | ||
return this.files.slice(); | ||
} | ||
getScriptFileNames(): string[] { | ||
return this.files.slice(); | ||
} | ||
getScriptVersion(fileName:string):string{ | ||
return null; | ||
} | ||
getScriptVersion(fileName: string): string { | ||
return null; | ||
} | ||
getScriptSnapshot(fileName:string):ts.IScriptSnapshot{ | ||
return ts.ScriptSnapshot.fromString(this.readFile(fileName)); | ||
} | ||
getScriptSnapshot(fileName: string): ts.IScriptSnapshot { | ||
return ts.ScriptSnapshot.fromString(this.readFile(fileName)); | ||
} | ||
getLocalizedDiagnosticMessages():any{ | ||
return null; | ||
} | ||
getLocalizedDiagnosticMessages(): any { | ||
return null; | ||
} | ||
getCompilationSettings():ts.CompilerOptions{ | ||
return this.compilerOptions; | ||
} | ||
getCompilationSettings(): ts.CompilerOptions { | ||
return this.compilerOptions; | ||
} | ||
log(s:string):void { | ||
} | ||
log(s: string): void { | ||
} | ||
trace(s:string):void { | ||
} | ||
trace(s: string): void { | ||
} | ||
error(s:string):void { | ||
} | ||
error(s: string): void { | ||
} | ||
resolveModuleNames(moduleNames:string[], containingFile:string):ts.ResolvedModule[]{ | ||
const containingDir: string = getDirectoryPath(containingFile); | ||
return moduleNames.map((moduleName: string) => { | ||
const resolvedBase: string = normalizePath(combinePaths(containingDir, moduleName)); | ||
return { | ||
resolvedFileName: this.tryResolveFileName(resolvedBase + '.tsx') || this.tryResolveFileName(resolvedBase + '.ts'), | ||
isExternalLibraryImport: false | ||
} | ||
}); | ||
} | ||
resolveModuleNames(moduleNames: string[], containingFile: string): ts.ResolvedModule[] { | ||
const containingDir: string = getDirectoryPath(containingFile); | ||
return moduleNames.map((moduleName: string) => { | ||
const resolvedBase: string = normalizePath(combinePaths(containingDir, moduleName)); | ||
return { | ||
resolvedFileName: this.tryResolveFileName(resolvedBase + '.tsx') || this.tryResolveFileName(resolvedBase + '.ts'), | ||
isExternalLibraryImport: false | ||
} | ||
}); | ||
} | ||
private tryResolveFileName(candidate: string): string { | ||
return this.source.fileExists(candidate) ? candidate : null; | ||
} | ||
private tryResolveFileName(candidate: string): string { | ||
return this.source.fileExists(candidate) ? candidate : null; | ||
} | ||
directoryExists(directoryName:string):boolean{ | ||
return null; | ||
} | ||
directoryExists(directoryName: string): boolean { | ||
return null; | ||
} | ||
acquireDocument(fileName: string, compilationSettings: ts.CompilerOptions, scriptSnapshot: ts.IScriptSnapshot, version: string): ts.SourceFile { | ||
return this.source.getSourceFile(fileName, compilationSettings.target); | ||
} | ||
acquireDocument(fileName: string, compilationSettings: ts.CompilerOptions, scriptSnapshot: ts.IScriptSnapshot, version: string): ts.SourceFile { | ||
return this.source.getSourceFile(fileName, compilationSettings.target); | ||
} | ||
/** | ||
@@ -145,5 +145,5 @@ * Request an updated version of an already existing SourceFile with a given fileName | ||
*/ | ||
updateDocument(fileName: string, compilationSettings: ts.CompilerOptions, scriptSnapshot: ts.IScriptSnapshot, version: string): ts.SourceFile { | ||
return this.source.getSourceFile(fileName, compilationSettings.target); | ||
} | ||
updateDocument(fileName: string, compilationSettings: ts.CompilerOptions, scriptSnapshot: ts.IScriptSnapshot, version: string): ts.SourceFile { | ||
return this.source.getSourceFile(fileName, compilationSettings.target); | ||
} | ||
/** | ||
@@ -158,9 +158,8 @@ * Informs the DocumentRegistry that a file is not needed any longer. | ||
*/ | ||
releaseDocument(fileName: string, compilationSettings: ts.CompilerOptions): void { | ||
releaseDocument(fileName: string, compilationSettings: ts.CompilerOptions): void { | ||
} | ||
reportStats(): string { | ||
return ''; | ||
} | ||
} | ||
reportStats(): string { | ||
return ''; | ||
} | ||
} | ||
import * as ts from 'typescript'; | ||
import { Visitor } from "./visitor"; | ||
import {Visitor} from './visitor'; | ||
export const defaultCompilerOptions: ts.CompilerOptions = { | ||
module: ts.ModuleKind.CommonJS, | ||
jsx: ts.JsxEmit.React, | ||
target: ts.ScriptTarget.ES5, | ||
experimentalDecorators: true, | ||
noEmitHelpers: true, | ||
sourceMap: true, | ||
preserveConstEnums : true, | ||
inlineSources : true, | ||
emitDecoratorMetadata: false | ||
module: ts.ModuleKind.CommonJS, | ||
jsx: ts.JsxEmit.React, | ||
target: ts.ScriptTarget.ES5, | ||
experimentalDecorators: true, | ||
noEmitHelpers: true, | ||
sourceMap: true, | ||
preserveConstEnums: true, | ||
inlineSources: true, | ||
emitDecoratorMetadata: false | ||
}; | ||
@@ -5,44 +5,41 @@ import * as ts from 'typescript'; | ||
// Most likely to be overridded | ||
// Most likely to be overridded | ||
fileExists(fileName: string): boolean { | ||
return false; | ||
} | ||
fileExists(fileName: string): boolean { | ||
return false; | ||
} | ||
readFile(fileName: string): string { | ||
return null; | ||
} | ||
readFile(fileName: string): string { | ||
return null; | ||
} | ||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile { | ||
return null; | ||
} | ||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile { | ||
return null; | ||
} | ||
writeFile(name:string, text:string, writeByteOrderMark: boolean) {} | ||
writeFile(name: string, text: string, writeByteOrderMark: boolean) { } | ||
useCaseSensitiveFileNames() { | ||
return false; | ||
} | ||
/// | ||
getCanonicalFileName(fileName: string) { | ||
return fileName; | ||
} | ||
useCaseSensitiveFileNames() { | ||
return false; | ||
} | ||
getCurrentDirectory(): string { | ||
return ''; | ||
} | ||
getCanonicalFileName(fileName: string) { | ||
return fileName; | ||
} | ||
getNewLine(): string { | ||
return '\n'; | ||
} | ||
getCurrentDirectory(): string { | ||
return ""; | ||
} | ||
getDefaultLibFileName(options: ts.CompilerOptions): string { | ||
return 'lib.d.ts'; | ||
} | ||
getNewLine(): string { | ||
return "\n"; | ||
} | ||
getDefaultLibFileName(options:ts.CompilerOptions): string { | ||
return "lib.d.ts"; | ||
} | ||
getCancellationToken(): ts.CancellationToken { | ||
return null; | ||
} | ||
getCancellationToken(): ts.CancellationToken { | ||
return null; | ||
} | ||
} | ||
@@ -52,34 +49,34 @@ | ||
export class ChainableHost extends HostBase { | ||
protected source: ts.CompilerHost = null; | ||
protected source: ts.CompilerHost = null; | ||
setSource(source: ts.CompilerHost): void { | ||
if(this.source === null) { | ||
this.source = source; | ||
} else { | ||
throw new Error(`A chainable host can be connected to a source only once. It looks like you're trying to include the same instance in multiple chains.`); | ||
} | ||
} | ||
setSource(source: ts.CompilerHost): void { | ||
if (this.source === null) { | ||
this.source = source; | ||
} else { | ||
throw new Error(`A chainable host can be connected to a source only once. It looks like you're trying to include the same instance in multiple chains.`); | ||
} | ||
} | ||
fileExists(fileName: string): boolean { | ||
return this.source.fileExists(fileName); | ||
} | ||
fileExists(fileName: string): boolean { | ||
return this.source.fileExists(fileName); | ||
} | ||
readFile(fileName: string): string { | ||
return this.source.readFile(fileName); | ||
} | ||
readFile(fileName: string): string { | ||
return this.source.readFile(fileName); | ||
} | ||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile { | ||
return this.source.getSourceFile(fileName, languageVersion, onError); | ||
} | ||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile { | ||
return this.source.getSourceFile(fileName, languageVersion, onError); | ||
} | ||
writeFile(name:string, text:string, writeByteOrderMark: boolean) { | ||
this.source.writeFile(name, text, writeByteOrderMark); | ||
} | ||
writeFile(name: string, text: string, writeByteOrderMark: boolean) { | ||
this.source.writeFile(name, text, writeByteOrderMark); | ||
} | ||
} | ||
export function chainHosts(host0: ts.CompilerHost, ...chainableHosts: ChainableHost[]): ts.CompilerHost { | ||
return chainableHosts.reduce((acc: ts.CompilerHost, chainableHost: ChainableHost) => { | ||
chainableHost.setSource(acc); | ||
return chainableHost; | ||
}, host0 as ChainableHost); | ||
return chainableHosts.reduce((acc: ts.CompilerHost, chainableHost: ChainableHost) => { | ||
chainableHost.setSource(acc); | ||
return chainableHost; | ||
}, host0 as ChainableHost); | ||
} |
159
src/hosts.ts
@@ -1,107 +0,100 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
import * as ts from 'typescript'; | ||
import {HostBase} from "./hosts-base"; | ||
import {defaultCompilerOptions} from "./configuration"; | ||
import {HostBase} from './hosts-base'; | ||
import {defaultCompilerOptions} from './configuration'; | ||
function fileExtensionIs(path: string, extension: string): boolean { | ||
let pathLen = path.length; | ||
let extLen = extension.length; | ||
return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension; | ||
let pathLen = path.length; | ||
let extLen = extension.length; | ||
return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension; | ||
} | ||
export class MultipleFilesHost extends HostBase implements ts.CompilerHost { | ||
private syntacticErrors: ts.Diagnostic[] = []; | ||
private syntacticErrors: ts.Diagnostic[] = []; | ||
constructor( | ||
private _resolutionHosts: ts.ModuleResolutionHost[], | ||
private _compilerOptions: ts.CompilerOptions = defaultCompilerOptions | ||
) { | ||
super(); | ||
} | ||
constructor( | ||
private _resolutionHosts: ts.ModuleResolutionHost[], | ||
private _compilerOptions: ts.CompilerOptions = defaultCompilerOptions | ||
) { | ||
super(); | ||
} | ||
fileExists(fileName: string): boolean{ | ||
return this._resolutionHosts.some(host => host.fileExists(fileName)); | ||
} | ||
fileExists(fileName: string): boolean { | ||
return this._resolutionHosts.some(host => host.fileExists(fileName)); | ||
} | ||
readFile(fileName: string): string { | ||
return this._resolutionHosts.reduce<string>( | ||
(acc:string, host: ts.ModuleResolutionHost) => (!acc && host.fileExists(fileName)) | ||
? host.readFile(fileName) | ||
: acc, | ||
null); | ||
} | ||
readFile(fileName: string): string { | ||
return this._resolutionHosts.reduce<string>( | ||
(acc: string, host: ts.ModuleResolutionHost) => (!acc && host.fileExists(fileName)) | ||
? host.readFile(fileName) | ||
: acc, | ||
null); | ||
} | ||
getSourceFile(fileName: string): ts.SourceFile { | ||
const source = this.readFile(fileName); | ||
if(source) { | ||
const ast: ts.SourceFile = ts.createSourceFile(fileName, source, this._compilerOptions.target, true); | ||
const syntacticErors = this.getParserErrors(ast); | ||
if(syntacticErors.length>0) { | ||
this.syntacticErrors.push(...syntacticErors); | ||
return null; | ||
} else { | ||
return ast; | ||
} | ||
} else { | ||
return null; | ||
} | ||
} | ||
getSourceFile(fileName: string): ts.SourceFile { | ||
const source = this.readFile(fileName); | ||
if (source) { | ||
const ast: ts.SourceFile = ts.createSourceFile(fileName, source, this._compilerOptions.target, true); | ||
const syntacticErors = this.getParserErrors(ast); | ||
if (syntacticErors.length > 0) { | ||
this.syntacticErrors.push(...syntacticErors); | ||
return null; | ||
} else { | ||
return ast; | ||
} | ||
} else { | ||
return null; | ||
} | ||
} | ||
getSyntacticErrors(): ts.Diagnostic[] { | ||
return this.syntacticErrors; | ||
} | ||
getSyntacticErrors(): ts.Diagnostic[] { | ||
return this.syntacticErrors; | ||
} | ||
private getParserErrors(sourceFile: ts.SourceFile): ts.Diagnostic[] { | ||
// We're accessing here an internal property. It would be more legit to access it through | ||
// ts.Program.getSyntacticDiagsnostics(), but we want to bail out ASAP. | ||
return sourceFile['parseDiagnostics']; | ||
} | ||
private getParserErrors(sourceFile: ts.SourceFile): ts.Diagnostic[] { | ||
// We're accessing here an internal property. It would be more legit to access it through | ||
// ts.Program.getSyntacticDiagsnostics(), but we want to bail out ASAP. | ||
return sourceFile['parseDiagnostics']; | ||
} | ||
} | ||
export class SingleFileHost extends HostBase implements ts.CompilerHost { | ||
private _output: string = ''; | ||
private _map: string = null; | ||
private _output: string = ''; | ||
private _map: string = null; | ||
constructor(private _ast: ts.SourceFile) { | ||
super(); | ||
} | ||
constructor(private _ast: ts.SourceFile) { | ||
super(); | ||
} | ||
public get output(): string { | ||
return this._output; | ||
} | ||
public get output(): string { | ||
return this._output; | ||
} | ||
public get sourceMap(): SourceMap.RawSourceMap { | ||
return JSON.parse(this._map); | ||
} | ||
public get sourceMap(): SourceMap.RawSourceMap { | ||
return JSON.parse(this._map); | ||
} | ||
fileExists(fileName: string): boolean{ | ||
return fileName === this._ast.fileName; | ||
} | ||
fileExists(fileName: string): boolean { | ||
return fileName === this._ast.fileName; | ||
} | ||
readFile(fileName: string): string{ | ||
if(fileName === this._ast.fileName) { | ||
return this._ast.text; | ||
} | ||
} | ||
readFile(fileName: string): string { | ||
if (fileName === this._ast.fileName) { | ||
return this._ast.text; | ||
} | ||
} | ||
getSourceFile(fileName: string): ts.SourceFile { | ||
if(fileName === this._ast.fileName) { | ||
return this._ast; | ||
} | ||
} | ||
getSourceFile(fileName: string): ts.SourceFile { | ||
if (fileName === this._ast.fileName) { | ||
return this._ast; | ||
} | ||
} | ||
writeFile(name:string, text:string, writeByteOrderMark: boolean) { | ||
if(fileExtensionIs(name, 'map')) { | ||
this._map = text; | ||
} else { | ||
this._output = text; | ||
} | ||
} | ||
writeFile(name: string, text: string, writeByteOrderMark: boolean) { | ||
if (fileExtensionIs(name, 'map')) { | ||
this._map = text; | ||
} else { | ||
this._output = text; | ||
} | ||
} | ||
} | ||
@@ -1,8 +0,7 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
/// <reference path='../typings/main.d.ts' /> | ||
export { transpile, TranspilerOutput, TranspilerConfig, ValidatorConfig, validateAll } from './transpile'; | ||
export { Visitor, VisitorContext } from "./visitor"; | ||
export { applyVisitor, applyVisitorOnAst, applyVisitorOnHostedSource } from "./apply-visitor"; | ||
export { MultipleFilesHost } from "./hosts"; | ||
export { traverseAst } from "./traverse-ast"; | ||
export {transpile, TranspilerOutput, TranspilerConfig, ValidatorConfig, validateAll} from './transpile'; | ||
export {Visitor, VisitorContext} from './visitor'; | ||
export {applyVisitor, applyVisitorOnAst, applyVisitorOnHostedSource} from './apply-visitor'; | ||
export {MultipleFilesHost} from './hosts'; | ||
export {traverseAst} from './traverse-ast'; |
@@ -1,17 +0,14 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
import { RawSourceMap, SourceMapConsumer, SourceMapGenerator, MappedPosition } from 'source-map'; | ||
import * as ts from 'typescript'; | ||
import {RawSourceMap, SourceMapConsumer, SourceMapGenerator, MappedPosition} from 'source-map'; | ||
import * as traverse from './traverse-ast'; | ||
import MagicString = require('magic-string'); | ||
import binarySearch from "./binary-search"; | ||
import binarySearch from './binary-search'; | ||
export abstract class MappedAction { | ||
abstract execute(ast: ts.SourceFile, magicString: MagicString): ts.SourceFile; | ||
abstract getStart(): number; | ||
abstract execute(ast: ts.SourceFile, magicString: MagicString): ts.SourceFile; | ||
abstract getStart(): number; | ||
} | ||
export abstract class FastAction { | ||
abstract execute(ast: ts.SourceFile): ts.SourceFile; | ||
abstract execute(ast: ts.SourceFile): ts.SourceFile; | ||
} | ||
@@ -22,57 +19,57 @@ | ||
export class ReplaceAction extends MappedAction { | ||
constructor(private start: number, private end: number, private str: string) { | ||
super(); | ||
} | ||
execute(ast: ts.SourceFile, magicString: MagicString): ts.SourceFile { | ||
magicString.overwrite(this.start, this.end, this.str); | ||
const textSpan: ts.TextSpan = ts.createTextSpanFromBounds(this.start, this.end); | ||
const textChangeRange: ts.TextChangeRange = ts.createTextChangeRange(textSpan, this.str.length); | ||
return ast.update(magicString.toString(), textChangeRange); | ||
} | ||
constructor(private start: number, private end: number, private str: string) { | ||
super(); | ||
} | ||
execute(ast: ts.SourceFile, magicString: MagicString): ts.SourceFile { | ||
magicString.overwrite(this.start, this.end, this.str); | ||
const textSpan: ts.TextSpan = ts.createTextSpanFromBounds(this.start, this.end); | ||
const textChangeRange: ts.TextChangeRange = ts.createTextChangeRange(textSpan, this.str.length); | ||
return ast.update(magicString.toString(), textChangeRange); | ||
} | ||
getStart(): number { | ||
return this.start; | ||
} | ||
getStart(): number { | ||
return this.start; | ||
} | ||
} | ||
export class InsertAction extends MappedAction { | ||
constructor(private start: number, private str: string) { | ||
super(); | ||
} | ||
execute(ast: ts.SourceFile, magicString: MagicString): ts.SourceFile { | ||
magicString.insert(this.start, this.str); | ||
const textSpan: ts.TextSpan = ts.createTextSpanFromBounds(this.start, this.start); | ||
const textChangeRange: ts.TextChangeRange = ts.createTextChangeRange(textSpan, this.str.length); | ||
return ast.update(magicString.toString(), textChangeRange); | ||
} | ||
constructor(private start: number, private str: string) { | ||
super(); | ||
} | ||
execute(ast: ts.SourceFile, magicString: MagicString): ts.SourceFile { | ||
magicString.insert(this.start, this.str); | ||
const textSpan: ts.TextSpan = ts.createTextSpanFromBounds(this.start, this.start); | ||
const textChangeRange: ts.TextChangeRange = ts.createTextChangeRange(textSpan, this.str.length); | ||
return ast.update(magicString.toString(), textChangeRange); | ||
} | ||
getStart(): number { | ||
return this.start; | ||
} | ||
getStart(): number { | ||
return this.start; | ||
} | ||
} | ||
export class FastAppendAction extends FastAction { | ||
constructor(private str: string) { | ||
super(); | ||
} | ||
constructor(private str: string) { | ||
super(); | ||
} | ||
execute(ast: ts.SourceFile): ts.SourceFile { | ||
const start = ast.text.length - 1; | ||
const textSpan: ts.TextSpan = ts.createTextSpanFromBounds(start, start); | ||
const textChangeRange: ts.TextChangeRange = ts.createTextChangeRange(textSpan, this.str.length); | ||
return ast.update(ast.text + this.str, textChangeRange); | ||
} | ||
execute(ast: ts.SourceFile): ts.SourceFile { | ||
const start = ast.text.length - 1; | ||
const textSpan: ts.TextSpan = ts.createTextSpanFromBounds(start, start); | ||
const textChangeRange: ts.TextChangeRange = ts.createTextChangeRange(textSpan, this.str.length); | ||
return ast.update(ast.text + this.str, textChangeRange); | ||
} | ||
} | ||
export class FastRewriteAction extends FastAction { | ||
constructor(private start: number, private str: string) { | ||
super(); | ||
} | ||
constructor(private start: number, private str: string) { | ||
super(); | ||
} | ||
execute(ast: ts.SourceFile): ts.SourceFile { | ||
const textSpan: ts.TextSpan = ts.createTextSpanFromBounds(this.start, this.start + this.str.length); | ||
const textChangeRange: ts.TextChangeRange = ts.createTextChangeRange(textSpan, this.str.length); | ||
const newText = ast.text.slice(0,this.start) + this.str + ast.text.slice(this.start + this.str.length); | ||
return ast.update(newText, textChangeRange); | ||
} | ||
execute(ast: ts.SourceFile): ts.SourceFile { | ||
const textSpan: ts.TextSpan = ts.createTextSpanFromBounds(this.start, this.start + this.str.length); | ||
const textChangeRange: ts.TextChangeRange = ts.createTextChangeRange(textSpan, this.str.length); | ||
const newText = ast.text.slice(0, this.start) + this.str + ast.text.slice(this.start + this.str.length); | ||
return ast.update(newText, textChangeRange); | ||
} | ||
} | ||
@@ -84,94 +81,93 @@ | ||
private _ast: ts.SourceFile; | ||
private magicString: MagicString; | ||
private originalText: string; | ||
private origLineStarts: number[]; | ||
private _sourceMap: RawSourceMap; | ||
private _ast: ts.SourceFile; | ||
private magicString: MagicString; | ||
private originalText: string; | ||
private origLineStarts: number[]; | ||
private _sourceMap: RawSourceMap; | ||
constructor(ast: ts.SourceFile) { | ||
this._ast = ast; | ||
this.originalText = ast.text; | ||
this.origLineStarts = ast.getLineStarts(); | ||
} | ||
constructor(ast: ts.SourceFile) { | ||
this._ast = ast; | ||
this.originalText = ast.text; | ||
this.origLineStarts = ast.getLineStarts(); | ||
} | ||
get ast(): ts.SourceFile { | ||
return this._ast; | ||
} | ||
get ast(): ts.SourceFile { | ||
return this._ast; | ||
} | ||
execute(actionList: Array<Action>): void { | ||
const fastActions = <FastAction[]>actionList.filter(action => action instanceof FastAction); | ||
fastActions.forEach((action: FastAction) => { | ||
this._ast = action.execute(this._ast); | ||
}); | ||
this.magicString = new MagicString(this._ast.text); | ||
execute(actionList: Array<Action>): void { | ||
const fastActions = <FastAction[]>actionList.filter(action => action instanceof FastAction); | ||
fastActions.forEach((action: FastAction) => { | ||
this._ast = action.execute(this._ast); | ||
}); | ||
this.magicString = new MagicString(this._ast.text); | ||
const sortedActions = actionList | ||
.filter(action => action instanceof MappedAction) | ||
.sort(compareActions); | ||
sortedActions.forEach((action: Action) => { | ||
this._ast = action.execute(this._ast, this.magicString); | ||
}); | ||
} | ||
const sortedActions = actionList | ||
.filter(action => action instanceof MappedAction) | ||
.sort(compareActions); | ||
sortedActions.forEach((action: Action) => { | ||
this._ast = action.execute(this._ast, this.magicString); | ||
}); | ||
} | ||
get sourceMap(): RawSourceMap { | ||
if(!this.magicString) { | ||
this.magicString = new MagicString(this._ast.text); | ||
} | ||
if(!this._sourceMap) { | ||
this._sourceMap = this.magicString.generateMap({ source: this._ast.fileName, hires: true }); | ||
} | ||
return this._sourceMap; | ||
} | ||
get sourceMap(): RawSourceMap { | ||
if (!this.magicString) { | ||
this.magicString = new MagicString(this._ast.text); | ||
} | ||
if (!this._sourceMap) { | ||
this._sourceMap = this.magicString.generateMap({ source: this._ast.fileName, hires: true }); | ||
} | ||
return this._sourceMap; | ||
} | ||
get code(): string { | ||
return this._ast.text; | ||
} | ||
get code(): string { | ||
return this._ast.text; | ||
} | ||
translateMap(from: RawSourceMap): RawSourceMap { | ||
const originalText = this.originalText; | ||
const intermediateAst = this._ast; | ||
translateMap(from: RawSourceMap): RawSourceMap { | ||
const originalText = this.originalText; | ||
const intermediateAst = this._ast; | ||
const mapConsumer = new SourceMapConsumer(this.sourceMap); | ||
const mapConsumer = new SourceMapConsumer(this.sourceMap); | ||
var fromSMC = new SourceMapConsumer(from); | ||
var resultMap = new SourceMapGenerator(); | ||
resultMap.setSourceContent(intermediateAst.fileName, originalText); | ||
var fromSMC = new SourceMapConsumer(from); | ||
var resultMap = new SourceMapGenerator(); | ||
resultMap.setSourceContent(intermediateAst.fileName, originalText); | ||
fromSMC.eachMapping(mapping => { | ||
var originalPosition: MappedPosition = mapConsumer.originalPositionFor({ line: mapping.originalLine, column: mapping.originalColumn }); | ||
if(originalPosition.line != null) { | ||
resultMap.addMapping({ | ||
source: intermediateAst.fileName, | ||
name: mapping.name, | ||
generated: { | ||
line: mapping.generatedLine, | ||
column: mapping.generatedColumn | ||
}, | ||
original: originalPosition | ||
}); | ||
} | ||
}); | ||
this._sourceMap = <RawSourceMap>resultMap.toJSON(); | ||
return resultMap.toJSON(); | ||
} | ||
fromSMC.eachMapping(mapping => { | ||
var originalPosition: MappedPosition = mapConsumer.originalPositionFor({ line: mapping.originalLine, column: mapping.originalColumn }); | ||
if (originalPosition.line != null) { | ||
resultMap.addMapping({ | ||
source: intermediateAst.fileName, | ||
name: mapping.name, | ||
generated: { | ||
line: mapping.generatedLine, | ||
column: mapping.generatedColumn | ||
}, | ||
original: originalPosition | ||
}); | ||
} | ||
}); | ||
this._sourceMap = <RawSourceMap>resultMap.toJSON(); | ||
return resultMap.toJSON(); | ||
} | ||
translateDiagnostic(diag: ts.Diagnostic): ts.Diagnostic { | ||
const sourceMap: RawSourceMap = this.sourceMap; | ||
const cosumer: SourceMapConsumer = new SourceMapConsumer(sourceMap); | ||
const start: ts.LineAndCharacter = diag.file.getLineAndCharacterOfPosition(diag.start); | ||
const startPos: MappedPosition = cosumer.originalPositionFor({ line: start.line + 1, column: start.character }); | ||
if(startPos.line === null) { | ||
return diag; | ||
} else { | ||
return { | ||
file: diag.file, | ||
start: diag.file.getPositionOfLineAndCharacter(startPos.line -1, startPos.column), | ||
length: diag.length, | ||
messageText: diag.messageText, | ||
category: diag.category, | ||
code: diag.code | ||
}; | ||
} | ||
} | ||
translateDiagnostic(diag: ts.Diagnostic): ts.Diagnostic { | ||
const sourceMap: RawSourceMap = this.sourceMap; | ||
const cosumer: SourceMapConsumer = new SourceMapConsumer(sourceMap); | ||
const start: ts.LineAndCharacter = diag.file.getLineAndCharacterOfPosition(diag.start); | ||
const startPos: MappedPosition = cosumer.originalPositionFor({ line: start.line + 1, column: start.character }); | ||
if (startPos.line === null) { | ||
return diag; | ||
} else { | ||
return { | ||
file: diag.file, | ||
start: diag.file.getPositionOfLineAndCharacter(startPos.line - 1, startPos.column), | ||
length: diag.length, | ||
messageText: diag.messageText, | ||
category: diag.category, | ||
code: diag.code | ||
}; | ||
} | ||
} | ||
} | ||
import * as ts from 'typescript'; | ||
import {Visitor} from "./visitor"; | ||
import {TranspilerContext} from "./transpiler-context"; | ||
import { traverseAst } from './traverse-ast'; | ||
import {MutableSourceCode} from "./mutable-source-code"; | ||
import {Visitor} from './visitor'; | ||
import {TranspilerContext} from './transpiler-context'; | ||
import {traverseAst} from './traverse-ast'; | ||
import {MutableSourceCode} from './mutable-source-code'; | ||
export interface CodeTransformer { | ||
transform(ast: ts.SourceFile): MutableSourceCode; | ||
transform(ast: ts.SourceFile): MutableSourceCode; | ||
} | ||
export class VisitorBasedTransformer implements CodeTransformer { | ||
constructor(private visitors: Visitor[], private languageServiceProvider: () => ts.LanguageService) {} | ||
constructor(private visitors: Visitor[], private languageServiceProvider: () => ts.LanguageService) { } | ||
transform(ast: ts.SourceFile): MutableSourceCode { | ||
const context: TranspilerContext = new TranspilerContext(ast.fileName, this.languageServiceProvider); | ||
this.visitors.forEach((visitor) => { | ||
context.halted || traverseAst(ast, visitor, context); | ||
}); | ||
transform(ast: ts.SourceFile): MutableSourceCode { | ||
const context: TranspilerContext = new TranspilerContext(ast.fileName, this.languageServiceProvider); | ||
this.visitors.forEach((visitor) => { | ||
context.halted || traverseAst(ast, visitor, context); | ||
}); | ||
if(context.halted) { | ||
return null; | ||
} else { | ||
const mutable = new MutableSourceCode(ast); | ||
mutable.execute(context.actions); | ||
return mutable; | ||
} | ||
} | ||
if (context.halted) { | ||
return null; | ||
} else { | ||
const mutable = new MutableSourceCode(ast); | ||
mutable.execute(context.actions); | ||
return mutable; | ||
} | ||
} | ||
} | ||
@@ -1,15 +0,13 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
import { SingleFileHost, MultipleFilesHost } from './hosts'; | ||
import { traverseAst } from './traverse-ast'; | ||
import { MutableSourceCode } from './mutable-source-code'; | ||
import { RawSourceMap } from 'source-map'; | ||
import { Visitor } from './visitor'; | ||
import * as ts from 'typescript'; | ||
import { TranspilerContext } from "./transpiler-context"; | ||
import { defaultCompilerOptions } from "./configuration"; | ||
import {SemanticHost} from "./chainable-hosts"; | ||
import {TransformationHost} from "./chainable-hosts"; | ||
import {chainHosts} from "./hosts-base"; | ||
import {AstCacheHost} from "./chainable-hosts"; | ||
import {SingleFileHost, MultipleFilesHost} from './hosts'; | ||
import {traverseAst} from './traverse-ast'; | ||
import {MutableSourceCode} from './mutable-source-code'; | ||
import {RawSourceMap} from 'source-map'; | ||
import {Visitor} from './visitor'; | ||
import {TranspilerContext} from './transpiler-context'; | ||
import {defaultCompilerOptions} from './configuration'; | ||
import {SemanticHost} from './chainable-hosts'; | ||
import {TransformationHost} from './chainable-hosts'; | ||
import {chainHosts} from './hosts-base'; | ||
import {AstCacheHost} from './chainable-hosts'; | ||
@@ -23,33 +21,33 @@ /** | ||
*/ | ||
code: string, | ||
code: string, | ||
/** | ||
* a raw sourcemap object representing all changes made from the supplied source to the transpiled code (visitors and typescript alike) | ||
*/ | ||
sourceMap: RawSourceMap, | ||
sourceMap: RawSourceMap, | ||
/** | ||
* diagnostics produced by Typescript or the visitors | ||
*/ | ||
diags: ts.Diagnostic[], | ||
diags: ts.Diagnostic[], | ||
/** | ||
* did the transpilation fail | ||
*/ | ||
halted: boolean | ||
halted: boolean | ||
} | ||
export interface TranspilerConfig { | ||
sourceFileName: string; | ||
compilerOptions?: ts.CompilerOptions; | ||
visitors: Visitor[]; | ||
sourceFileName: string; | ||
compilerOptions?: ts.CompilerOptions; | ||
visitors: Visitor[]; | ||
} | ||
export interface ValidatorConfig { | ||
resolutionHosts?: ts.ModuleResolutionHost[]; | ||
visitors?: Visitor[]; | ||
mutators?: Visitor[]; | ||
resolutionHosts?: ts.ModuleResolutionHost[]; | ||
visitors?: Visitor[]; | ||
mutators?: Visitor[]; | ||
} | ||
function getParserErrors(sourceFile: ts.SourceFile): ts.Diagnostic[] { | ||
// We're accessing here an internal property. It would be more legit to access it through | ||
// ts.Program.getSyntacticDiagsnostics(), but we want to bail out ASAP. | ||
return sourceFile['parseDiagnostics']; | ||
// We're accessing here an internal property. It would be more legit to access it through | ||
// ts.Program.getSyntacticDiagsnostics(), but we want to bail out ASAP. | ||
return sourceFile['parseDiagnostics']; | ||
} | ||
@@ -61,110 +59,109 @@ | ||
// The context may contain compiler options and a list of visitors. | ||
// If it doesn't, we use the default as defined in ./configuration.ts | ||
// The context may contain compiler options and a list of visitors. | ||
// If it doesn't, we use the default as defined in ./configuration.ts | ||
const compilerOptions = config.compilerOptions || defaultCompilerOptions; | ||
const compilerOptions = config.compilerOptions || defaultCompilerOptions; | ||
// First we initialize a SourceFile object with the given source code | ||
// First we initialize a SourceFile object with the given source code | ||
const fileName: string = config.sourceFileName; | ||
const fileName: string = config.sourceFileName; | ||
// Then we let TypeScript parse it into an AST | ||
// Then we let TypeScript parse it into an AST | ||
const ast = ts.createSourceFile(fileName, content, compilerOptions.target, true); | ||
const parserErrors = getParserErrors(ast); | ||
if(parserErrors.length>0) { | ||
return { | ||
code: null, | ||
diags: parserErrors, | ||
halted: true, | ||
sourceMap: null | ||
} | ||
} | ||
const ast = ts.createSourceFile(fileName, content, compilerOptions.target, true); | ||
const parserErrors = getParserErrors(ast); | ||
if (parserErrors.length > 0) { | ||
return { | ||
code: null, | ||
diags: parserErrors, | ||
halted: true, | ||
sourceMap: null | ||
} | ||
} | ||
// The context contains code modifications and diagnostics | ||
// The context contains code modifications and diagnostics | ||
let context: TranspilerContext = new TranspilerContext(ast.fileName); | ||
let context: TranspilerContext = new TranspilerContext(ast.fileName); | ||
// We execute the various visitors, each traversing the AST and generating | ||
// lines to be pushed into the code and diagbostic messages. | ||
// If one of the visitors halts the transilation process we return the halted object. | ||
// We execute the various visitors, each traversing the AST and generating | ||
// lines to be pushed into the code and diagbostic messages. | ||
// If one of the visitors halts the transilation process we return the halted object. | ||
config.visitors.some((visitor) => { | ||
traverseAst(ast, visitor, context); | ||
return context.halted; | ||
}); | ||
config.visitors.some((visitor) => { | ||
traverseAst(ast, visitor, context); | ||
return context.halted; | ||
}); | ||
if(context.halted) { | ||
return { | ||
code: null, | ||
sourceMap: null, | ||
diags: context.diags, | ||
halted: true | ||
}; | ||
} | ||
if (context.halted) { | ||
return { | ||
code: null, | ||
sourceMap: null, | ||
diags: context.diags, | ||
halted: true | ||
}; | ||
} | ||
// Now, we mutate the code with the resulting list of strings to be pushed | ||
// Now, we mutate the code with the resulting list of strings to be pushed | ||
const mutable = new MutableSourceCode(ast); | ||
mutable.execute(context.actions); | ||
const mutable = new MutableSourceCode(ast); | ||
mutable.execute(context.actions); | ||
// This intermediate code has to be transpiled by TypeScript | ||
// This intermediate code has to be transpiled by TypeScript | ||
const compilerHost = new SingleFileHost(mutable.ast); | ||
const program: ts.Program = ts.createProgram([fileName], compilerOptions, compilerHost); | ||
const emitResult = program.emit(); | ||
const compilerHost = new SingleFileHost(mutable.ast); | ||
const program: ts.Program = ts.createProgram([fileName], compilerOptions, compilerHost); | ||
const emitResult = program.emit(); | ||
emitResult.diagnostics.forEach((d: ts.Diagnostic) => { | ||
context.pushDiag(mutable.translateDiagnostic(d)); | ||
}); | ||
emitResult.diagnostics.forEach((d: ts.Diagnostic) => { | ||
context.pushDiag(mutable.translateDiagnostic(d)); | ||
}); | ||
// If TypeScript did not complete the transpilation, we return the halted object | ||
// If TypeScript did not complete the transpilation, we return the halted object | ||
if(emitResult.emitSkipped) { | ||
return { | ||
code: null, | ||
sourceMap: null, | ||
diags: context.diags, | ||
halted: true | ||
}; | ||
} | ||
if (emitResult.emitSkipped) { | ||
return { | ||
code: null, | ||
sourceMap: null, | ||
diags: context.diags, | ||
halted: true | ||
}; | ||
} | ||
// If we got here, it means we have final source code to return | ||
// If we got here, it means we have final source code to return | ||
const finalCode: string = compilerHost.output; | ||
const intermediateSourceMap = compilerHost.sourceMap; | ||
const finalCode: string = compilerHost.output; | ||
const intermediateSourceMap = compilerHost.sourceMap; | ||
// The resulting sourcemap maps the final code to the intermediate code, | ||
// but we want a sourcemap that maps the final code to the original code, | ||
// so... | ||
// The resulting sourcemap maps the final code to the intermediate code, | ||
// but we want a sourcemap that maps the final code to the original code, | ||
// so... | ||
const finalSourceMap: RawSourceMap = intermediateSourceMap ? mutable.translateMap(intermediateSourceMap) : null; | ||
const finalSourceMap: RawSourceMap = intermediateSourceMap ? mutable.translateMap(intermediateSourceMap) : null; | ||
// Now we return the final code and the final sourcemap | ||
// Now we return the final code and the final sourcemap | ||
return { | ||
code: finalCode, | ||
sourceMap: finalSourceMap, | ||
diags: context.diags, | ||
halted: false | ||
}; | ||
return { | ||
code: finalCode, | ||
sourceMap: finalSourceMap, | ||
diags: context.diags, | ||
halted: false | ||
}; | ||
} | ||
export function validateAll(files: string[], config: ValidatorConfig): ts.Diagnostic[] { | ||
let langService: ts.LanguageService; | ||
const sourceHost = new MultipleFilesHost(config.resolutionHosts, defaultCompilerOptions); | ||
const astCache = new AstCacheHost(); | ||
const cachedSource: ts.CompilerHost = chainHosts(sourceHost, astCache); | ||
const semanticHost = <SemanticHost>chainHosts(cachedSource, new SemanticHost(files, defaultCompilerOptions)); | ||
const langServiceProvider = () => langService | ||
? langService | ||
: langService = ts.createLanguageService(semanticHost, semanticHost); | ||
const transformHost = new TransformationHost(config.mutators || [], langServiceProvider); | ||
const program: ts.Program = ts.createProgram(files, defaultCompilerOptions, chainHosts(cachedSource, transformHost)); | ||
const diags: ts.Diagnostic[] = [].concat( | ||
sourceHost.getSyntacticErrors(), | ||
program.getSemanticDiagnostics() | ||
); | ||
return diags.map(diagnostic => transformHost.translateDiagnostic(diagnostic)); | ||
let langService: ts.LanguageService; | ||
const sourceHost = new MultipleFilesHost(config.resolutionHosts, defaultCompilerOptions); | ||
const astCache = new AstCacheHost(); | ||
const cachedSource: ts.CompilerHost = chainHosts(sourceHost, astCache); | ||
const semanticHost = <SemanticHost>chainHosts(cachedSource, new SemanticHost(files, defaultCompilerOptions)); | ||
const langServiceProvider = () => langService | ||
? langService | ||
: langService = ts.createLanguageService(semanticHost, semanticHost); | ||
const transformHost = new TransformationHost(config.mutators || [], langServiceProvider); | ||
const program: ts.Program = ts.createProgram(files, defaultCompilerOptions, chainHosts(cachedSource, transformHost)); | ||
const diags: ts.Diagnostic[] = [].concat( | ||
sourceHost.getSyntacticErrors(), | ||
program.getSemanticDiagnostics() | ||
); | ||
return diags.map(diagnostic => transformHost.translateDiagnostic(diagnostic)); | ||
} | ||
@@ -1,79 +0,77 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
import { MutableSourceCode, Action, ReplaceAction } from './mutable-source-code'; | ||
import { Visitor, VisitorContext } from './visitor'; | ||
import * as ts from 'typescript'; | ||
import {FastAppendAction} from "./mutable-source-code"; | ||
import {FastRewriteAction} from "./mutable-source-code"; | ||
import {InsertAction} from "./mutable-source-code"; | ||
import {MutableSourceCode, Action, ReplaceAction} from './mutable-source-code'; | ||
import {Visitor, VisitorContext} from './visitor'; | ||
import {FastAppendAction} from './mutable-source-code'; | ||
import {FastRewriteAction} from './mutable-source-code'; | ||
import {InsertAction} from './mutable-source-code'; | ||
export class TranspilerContext implements VisitorContext { | ||
private _halted = false; | ||
private _actions: Action[] = []; | ||
private _diags: ts.Diagnostic[] = []; | ||
private _halted = false; | ||
private _actions: Action[] = []; | ||
private _diags: ts.Diagnostic[] = []; | ||
constructor(private _fileName: string, private langServiceProvider: () => ts.LanguageService = null) {} | ||
constructor(private _fileName: string, private langServiceProvider: () => ts.LanguageService = null) { } | ||
isHalted(): boolean { | ||
return this._halted; | ||
} | ||
isHalted(): boolean { | ||
return this._halted; | ||
} | ||
insertLine(position: number, str: string): void { | ||
this._actions.push(new InsertAction(position, str + '\n')); | ||
} | ||
insertLine(position: number, str: string): void { | ||
this._actions.push(new InsertAction(position, str + '\n')); | ||
} | ||
replace(start: number, end: number, str: string): void { | ||
this._actions.push(new ReplaceAction(start, end, str )); | ||
} | ||
replace(start: number, end: number, str: string): void { | ||
this._actions.push(new ReplaceAction(start, end, str)); | ||
} | ||
fastAppend(str: string): void { | ||
this._actions.push(new FastAppendAction(str)); | ||
} | ||
fastAppend(str: string): void { | ||
this._actions.push(new FastAppendAction(str)); | ||
} | ||
fastRewrite(start: number, str: string): void { | ||
this._actions.push(new FastRewriteAction(start, str)); | ||
} | ||
fastRewrite(start: number, str: string): void { | ||
this._actions.push(new FastRewriteAction(start, str)); | ||
} | ||
reportDiag(node: ts.Node, category: ts.DiagnosticCategory, message: string, halt?: boolean): void { | ||
let diagnostic: ts.Diagnostic = { | ||
file: node.getSourceFile(), | ||
start: node.getStart(), | ||
length: node.getEnd() - node.getStart(), | ||
messageText: message, | ||
category: category, | ||
code: 0 | ||
}; | ||
this._diags.push(diagnostic); | ||
this._halted = this._halted || halt; | ||
} | ||
reportDiag(node: ts.Node, category: ts.DiagnosticCategory, message: string, halt?: boolean): void { | ||
let diagnostic: ts.Diagnostic = { | ||
file: node.getSourceFile(), | ||
start: node.getStart(), | ||
length: node.getEnd() - node.getStart(), | ||
messageText: message, | ||
category: category, | ||
code: 0 | ||
}; | ||
this._diags.push(diagnostic); | ||
this._halted = this._halted || halt; | ||
} | ||
pushDiag(diagnostic: ts.Diagnostic): void { | ||
this._diags.push(diagnostic); | ||
} | ||
pushDiag(diagnostic: ts.Diagnostic): void { | ||
this._diags.push(diagnostic); | ||
} | ||
get actions(): Action[] { | ||
return this._actions; | ||
} | ||
get actions(): Action[] { | ||
return this._actions; | ||
} | ||
get diags(): ts.Diagnostic[] { | ||
return this._diags; | ||
} | ||
get diags(): ts.Diagnostic[] { | ||
return this._diags; | ||
} | ||
get halted(): boolean { | ||
return this._halted; | ||
} | ||
get halted(): boolean { | ||
return this._halted; | ||
} | ||
get fileName(): string { | ||
return this._fileName; | ||
} | ||
get fileName(): string { | ||
return this._fileName; | ||
} | ||
getLanguageService(): ts.LanguageService { | ||
if(this.langServiceProvider) { | ||
return this.langServiceProvider(); | ||
} else { | ||
return null; | ||
} | ||
getLanguageService(): ts.LanguageService { | ||
if (this.langServiceProvider) { | ||
return this.langServiceProvider(); | ||
} else { | ||
return null; | ||
} | ||
} | ||
} | ||
} |
import * as ts from 'typescript'; | ||
import { Visitor, VisitorContext } from './visitor'; | ||
import {Visitor, VisitorContext} from './visitor'; | ||
function descend(node: ts.Node, context: VisitorContext) { | ||
return function visit(...visitors: Visitor[]): void { | ||
visitors.forEach(visitor => { | ||
traverseAst(node, visitor, context); | ||
}); | ||
} | ||
return function visit(...visitors: Visitor[]): void { | ||
visitors.forEach(visitor => { | ||
traverseAst(node, visitor, context); | ||
}); | ||
} | ||
} | ||
@@ -15,11 +15,11 @@ | ||
function traverse(node: ts.Node) { | ||
if(visitor.filter(node)) { | ||
visitor.visit(node, context, descend(node, context)); | ||
return context.halted || ts.forEachChild(node, traverse); | ||
} | ||
return ts.forEachChild(node, traverse); | ||
} | ||
function traverse(node: ts.Node) { | ||
if (visitor.filter(node)) { | ||
visitor.visit(node, context, descend(node, context)); | ||
return context.halted || ts.forEachChild(node, traverse); | ||
} | ||
return ts.forEachChild(node, traverse); | ||
} | ||
return traverse(root); | ||
return traverse(root); | ||
} |
import * as ts from 'typescript'; | ||
import {Action} from "./mutable-source-code"; | ||
import {Action} from './mutable-source-code'; | ||
@@ -8,7 +8,7 @@ /** | ||
export interface VisitorContext { | ||
fileName: string; | ||
fileName: string; | ||
/** | ||
* was the transpilation declared as failed by any previous visitor | ||
*/ | ||
halted: boolean; | ||
halted: boolean; | ||
/** | ||
@@ -19,3 +19,3 @@ * add a text line at given position | ||
*/ | ||
insertLine(position: number, str: string): void; | ||
insertLine(position: number, str: string): void; | ||
/** | ||
@@ -27,3 +27,3 @@ * replace a piece of text in the code with a given new text | ||
*/ | ||
replace(start: number, end: number, str: string): void; | ||
replace(start: number, end: number, str: string): void; | ||
/** | ||
@@ -36,8 +36,8 @@ * report transpilation diagnostics | ||
*/ | ||
reportDiag(node: ts.Node, category: ts.DiagnosticCategory, message: string, halt?: boolean): void; | ||
reportDiag(node: ts.Node, category: ts.DiagnosticCategory, message: string, halt?: boolean): void; | ||
fastAppend(str: string): void; | ||
fastRewrite(start: number, str: string): void; | ||
fastAppend(str: string): void; | ||
fastRewrite(start: number, str: string): void; | ||
getLanguageService(): ts.LanguageService; | ||
getLanguageService(): ts.LanguageService; | ||
} | ||
@@ -53,3 +53,3 @@ | ||
*/ | ||
filter(node: ts.Node) : boolean; | ||
filter(node: ts.Node): boolean; | ||
/** | ||
@@ -60,3 +60,3 @@ * perform visitor logic on given node | ||
*/ | ||
visit(node: ts.Node, context: VisitorContext, traverse: (...visitors: Visitor[])=> void): void; | ||
visit(node: ts.Node, context: VisitorContext, traverse: (...visitors: Visitor[]) => void): void; | ||
} |
@@ -1,10 +0,4 @@ | ||
/** | ||
* Created by gadig on 9/24/15. | ||
*/ | ||
/// <reference path="../typings/main.d.ts" /> | ||
import * as SourceMap from 'source-map'; | ||
import * as _ from 'lodash'; | ||
import * as ts from "typescript"; | ||
import * as ts from 'typescript'; | ||
@@ -14,3 +8,3 @@ export function findCodePosition(code: string, snippet: string): SourceMap.Position { | ||
var lineNo = _.findIndex(lines, (line) => _.includes(line, snippet)); | ||
if(lineNo > -1) { | ||
if (lineNo > -1) { | ||
var column = (lineNo > -1) && lines[lineNo].indexOf(snippet); | ||
@@ -26,5 +20,5 @@ return { | ||
export function findCodeRange(code: string, snippet: string): ts.TextRange { | ||
export function findCodeRange(code: string, snippet: string): ts.TextRange { | ||
var pos = code.indexOf(snippet); | ||
return pos < 0 ? null : { pos, end: pos + snippet.length }; | ||
} |
import ts = require('typescript'); | ||
function printMessage(messageText:string | ts.DiagnosticMessageChain):string { | ||
if(messageText["messageText"]) { | ||
return printMessage((<ts.DiagnosticMessageChain>messageText).messageText); | ||
} else { | ||
return messageText.toString(); | ||
} | ||
function printMessage(messageText: string | ts.DiagnosticMessageChain): string { | ||
if (messageText['messageText']) { | ||
return printMessage((<ts.DiagnosticMessageChain>messageText).messageText); | ||
} else { | ||
return messageText.toString(); | ||
} | ||
} | ||
@@ -13,6 +13,6 @@ | ||
export function printDiagnostic(diagnostic: ts.Diagnostic) { | ||
const linePos: ts.LineAndCharacter = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); | ||
const message: string = printMessage(diagnostic.messageText); | ||
return `${diagnostic.file.fileName} -> ${linePos.line + 1}:${linePos.character} ${message}`; | ||
const linePos: ts.LineAndCharacter = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); | ||
const message: string = printMessage(diagnostic.messageText); | ||
return `${diagnostic.file.fileName} -> ${linePos.line + 1}:${linePos.character} ${message}`; | ||
} | ||
@@ -1,3 +0,1 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
import {getModuleLoader, Dependency} from './mocks/module-loaders'; | ||
@@ -7,8 +5,8 @@ | ||
export function evaluateModuleExports(source: string, dependencies: Dependency[] = []) :Object{ | ||
const moduleLoader = getModuleLoader(); | ||
dependencies.forEach(d => { | ||
moduleLoader.addDependency(d); | ||
}); | ||
return moduleLoader.load(source); | ||
export function evaluateModuleExports(source: string, dependencies: Dependency[] = []): Object { | ||
const moduleLoader = getModuleLoader(); | ||
dependencies.forEach(d => { | ||
moduleLoader.addDependency(d); | ||
}); | ||
return moduleLoader.load(source); | ||
} |
@@ -1,18 +0,18 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
/// <reference path='../typings/main.d.ts' /> | ||
import * as chai from 'chai'; | ||
import typecheck from './matchers/typecheck'; | ||
import * as chai from 'chai'; | ||
chai.use(typecheck); | ||
export { findCodePosition, findCodeRange } from './code-positions'; | ||
export { evaluateModuleExports } from './evaluate-module'; | ||
export {findCodePosition, findCodeRange} from './code-positions'; | ||
export {evaluateModuleExports} from './evaluate-module'; | ||
// This is a loader for debugging purposes when running node tests (not from webpack bundle) | ||
if(require.extensions) { | ||
require.extensions['.ts'] = function (module, fileName) { | ||
var content = require('fs').readFileSync(fileName).toString(); | ||
var code = 'module.exports = ' + JSON.stringify(content) + ';'; | ||
return module._compile(code, fileName); | ||
}; | ||
if (require.extensions) { | ||
require.extensions['.ts'] = function(module, fileName) { | ||
var content = require('fs').readFileSync(fileName).toString(); | ||
var code = 'module.exports = ' + JSON.stringify(content) + ';'; | ||
return module._compile(code, fileName); | ||
}; | ||
} |
@@ -1,17 +0,16 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
/// <reference path='../typings/main.d.ts' /> | ||
declare module Chai { | ||
interface Assertion { | ||
pass(): Assertion; | ||
fail(): Matchers.TypeCheckFailure; | ||
} | ||
interface Assertion { | ||
pass(): Assertion; | ||
fail(): Matchers.TypeCheckFailure; | ||
} | ||
} | ||
declare module Matchers { | ||
interface TypeCheckFailure { | ||
withMessage(messageMatch: string | RegExp): TypeCheckFailure; | ||
withMessageCount(expectedCount: number): TypeCheckFailure; | ||
and: TypeCheckFailure; | ||
} | ||
interface TypeCheckFailure { | ||
withMessage(messageMatch: string | RegExp): TypeCheckFailure; | ||
withMessageCount(expectedCount: number): TypeCheckFailure; | ||
and: TypeCheckFailure; | ||
} | ||
} | ||
@@ -1,3 +0,1 @@ | ||
/// <reference path="../../typings/main.d.ts" /> | ||
import {expect} from 'chai'; | ||
@@ -8,64 +6,63 @@ import {printDiagnostic} from '../diagnostics-utils'; | ||
class TypecheckFailure implements Matchers.TypeCheckFailure { | ||
constructor(private assertion: Chai.AssertStatic, private diags: ts.Diagnostic[]) {} | ||
constructor(private assertion: Chai.AssertStatic, private diags: ts.Diagnostic[]) { } | ||
get and(): TypecheckFailure { | ||
return this; | ||
} | ||
get and(): TypecheckFailure { | ||
return this; | ||
} | ||
withMessage(messageMatch:string|RegExp): Matchers.TypeCheckFailure { | ||
let isMatch; | ||
if(messageMatch instanceof RegExp) { | ||
isMatch = this.diags.some(diag => { | ||
const messageText = printDiagnostic(diag); | ||
return !!(messageText.match(messageMatch)) | ||
}); | ||
} else if(typeof messageMatch === 'string') { | ||
isMatch = this.diags.some(diag => !!(diag.messageText && diag.messageText === messageMatch)) | ||
} | ||
expect(isMatch).to.equal(true, `Expected some of the typechecker messages to match ${messageMatch}. | ||
withMessage(messageMatch: string | RegExp): Matchers.TypeCheckFailure { | ||
let isMatch; | ||
if (messageMatch instanceof RegExp) { | ||
isMatch = this.diags.some(diag => { | ||
const messageText = printDiagnostic(diag); | ||
return !!(messageText.match(messageMatch)) | ||
}); | ||
} else if (typeof messageMatch === 'string') { | ||
isMatch = this.diags.some(diag => !!(diag.messageText && diag.messageText === messageMatch)) | ||
} | ||
expect(isMatch).to.equal(true, `Expected some of the typechecker messages to match ${messageMatch}. | ||
Actual errors: | ||
${printErrors(this.diags)} | ||
`) | ||
return this; | ||
} | ||
return this; | ||
} | ||
withMessageCount(expectedCount:number): Matchers.TypeCheckFailure { | ||
expect(this.diags.length) | ||
.to.equal(expectedCount, | ||
`Expected to fail with ${expectedCount} errors, but got ${this.diags.length}. | ||
withMessageCount(expectedCount: number): Matchers.TypeCheckFailure { | ||
expect(this.diags.length) | ||
.to.equal(expectedCount, | ||
`Expected to fail with ${expectedCount} errors, but got ${this.diags.length}. | ||
Actual errors: | ||
${printErrors(this.diags)} | ||
`); | ||
return this; | ||
} | ||
return this; | ||
} | ||
} | ||
function printErrors(diags: ts.Diagnostic[]): string { | ||
return diags.map<string>(printDiagnostic).join('\n'); | ||
return diags.map<string>(printDiagnostic).join('\n'); | ||
} | ||
export default function (chai, util) { | ||
chai.Assertion.addMethod('pass', function () { | ||
expect(this._obj).to.be.an('Array'); | ||
this.empty; | ||
}); | ||
export default function(chai, util) { | ||
chai.Assertion.addMethod('pass', function() { | ||
expect(this._obj).to.be.an('Array'); | ||
this.empty; | ||
}); | ||
chai.Assertion.addMethod('fail', function () { | ||
expect(this._obj).to.be.an('Array'); | ||
this.not.empty; | ||
const diags: ts.Diagnostic[] = <ts.Diagnostic[]>this._obj; | ||
return new TypecheckFailure(this, diags); | ||
}); | ||
chai.Assertion.addMethod('fail', function() { | ||
expect(this._obj).to.be.an('Array'); | ||
this.not.empty; | ||
const diags: ts.Diagnostic[] = <ts.Diagnostic[]>this._obj; | ||
return new TypecheckFailure(this, diags); | ||
}); | ||
chai.Assertion.addMethod('pass', function () { | ||
this.an('Array'); | ||
this.assert(this._obj.length === 0, | ||
`Expected to get 0 validation errors, but got, but got ${this._obj.length}: | ||
chai.Assertion.addMethod('pass', function() { | ||
this.an('Array'); | ||
this.assert(this._obj.length === 0, | ||
`Expected to get 0 validation errors, but got, but got ${this._obj.length}: | ||
${printErrors(this._obj)} | ||
`, | ||
'Expected to get validation errors, but got, but got none.' | ||
); | ||
}); | ||
'Expected to get validation errors, but got, but got none.' | ||
); | ||
}); | ||
} |
@@ -1,16 +0,14 @@ | ||
/// <reference path="../../typings/main.d.ts" /> | ||
export interface Dependency { | ||
depName : string; | ||
exportName : string; | ||
value : any; | ||
depName: string; | ||
exportName: string; | ||
value: any; | ||
} | ||
export interface ModuleLoader { | ||
addDependency(dependency: Dependency): void; | ||
load(source:string):Object; | ||
addDependency(dependency: Dependency): void; | ||
load(source: string): Object; | ||
} | ||
export function getModuleLoader(): ModuleLoader { | ||
return new CommonJSMockLoader(); | ||
return new CommonJSMockLoader(); | ||
} | ||
@@ -22,13 +20,13 @@ | ||
function __decorate(decorators, target, key, desc) { | ||
switch (arguments.length) { | ||
case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); | ||
case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); | ||
case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); | ||
} | ||
switch (arguments.length) { | ||
case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); | ||
case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); | ||
case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); | ||
} | ||
} | ||
function __extends(d, b) { | ||
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
} | ||
@@ -38,18 +36,18 @@ | ||
class CommonJSMockLoader implements ModuleLoader{ | ||
private _dependencies = {}; | ||
class CommonJSMockLoader implements ModuleLoader { | ||
private _dependencies = {}; | ||
addDependency({depName, exportName, value}) { | ||
this._dependencies[depName] = this._dependencies[depName] || {}; | ||
if(exportName === 'default') { | ||
this._dependencies[depName] = value; | ||
} | ||
this._dependencies[depName][exportName] = value; | ||
} | ||
addDependency({depName, exportName, value}) { | ||
this._dependencies[depName] = this._dependencies[depName] || {}; | ||
if (exportName === 'default') { | ||
this._dependencies[depName] = value; | ||
} | ||
this._dependencies[depName][exportName] = value; | ||
} | ||
load(source:string):Object { | ||
let Module = { | ||
exports: {} | ||
}; | ||
const testFn = new Function(` | ||
load(source: string): Object { | ||
let Module = { | ||
exports: {} | ||
}; | ||
const testFn = new Function(` | ||
return function loadModuleCjs(require, exports, module, __decorate, __extends) { | ||
@@ -59,5 +57,5 @@ ${source} | ||
`); | ||
testFn()(moduleName => this._dependencies[moduleName], Module.exports, Module, __decorate, __extends); | ||
return Module.exports; | ||
} | ||
testFn()(moduleName => this._dependencies[moduleName], Module.exports, Module, __decorate, __extends); | ||
return Module.exports; | ||
} | ||
} |
@@ -5,11 +5,11 @@ import * as ts from 'typescript'; | ||
constructor(private mockFileName: string, private mockContent: string){} | ||
constructor(private mockFileName: string, private mockContent: string) { } | ||
fileExists(fileName:string):boolean { | ||
return this.mockFileName === fileName; | ||
} | ||
fileExists(fileName: string): boolean { | ||
return this.mockFileName === fileName; | ||
} | ||
readFile(fileName:string):string { | ||
return this.mockFileName === fileName ? this.mockContent : null; | ||
} | ||
readFile(fileName: string): string { | ||
return this.mockFileName === fileName ? this.mockContent : null; | ||
} | ||
} |
@@ -6,40 +6,40 @@ import {expect} from 'chai'; | ||
describe('poc example', function () { | ||
let sourceCode, configNoVisitors, configWithVisitors; | ||
before(() => { | ||
sourceCode = require('../../examples/poc/src.ts'); // the path is relative to tspoon/dist/test | ||
configNoVisitors = { | ||
sourceFileName: 'src.ts', | ||
visitors: [] | ||
}; | ||
configWithVisitors = { | ||
sourceFileName: 'src.ts', | ||
visitors: [visitor] | ||
}; | ||
}); | ||
describe('poc example', function() { | ||
let sourceCode, configNoVisitors, configWithVisitors; | ||
before(() => { | ||
sourceCode = require('../../examples/poc/src.ts'); // the path is relative to tspoon/dist/test | ||
configNoVisitors = { | ||
sourceFileName: 'src.ts', | ||
visitors: [] | ||
}; | ||
configWithVisitors = { | ||
sourceFileName: 'src.ts', | ||
visitors: [visitor] | ||
}; | ||
}); | ||
it('is transpiled', function () { | ||
let transpilerOut = tspoon.transpile(sourceCode, configWithVisitors); | ||
expect(transpilerOut.halted).not.to.be.ok; | ||
expect(transpilerOut.code).to.be.ok; | ||
expect(transpilerOut.sourceMap).to.be.ok; | ||
}); | ||
it('is transpiled', function() { | ||
let transpilerOut = tspoon.transpile(sourceCode, configWithVisitors); | ||
expect(transpilerOut.halted).not.to.be.ok; | ||
expect(transpilerOut.code).to.be.ok; | ||
expect(transpilerOut.sourceMap).to.be.ok; | ||
}); | ||
it('is transpiled correctly without visitors', function () { | ||
let transpilerOut = tspoon.transpile(sourceCode, configNoVisitors); | ||
let TwoNames = evaluateModuleExports(transpilerOut.code)['TwoNames']; | ||
let instance = new TwoNames(); | ||
expect(transpilerOut.diags).to.be.empty; | ||
expect(transpilerOut.halted).not.to.be.ok; | ||
expect(instance.publicName, 'publicName').to.eql('Doe'); | ||
expect(instance.privateName, 'privateName').to.eql('John'); | ||
}); | ||
it('is transpiled correctly without visitors', function() { | ||
let transpilerOut = tspoon.transpile(sourceCode, configNoVisitors); | ||
let TwoNames = evaluateModuleExports(transpilerOut.code)['TwoNames']; | ||
let instance = new TwoNames(); | ||
expect(transpilerOut.diags).to.be.empty; | ||
expect(transpilerOut.halted).not.to.be.ok; | ||
expect(instance.publicName, 'publicName').to.eql('Doe'); | ||
expect(instance.privateName, 'privateName').to.eql('John'); | ||
}); | ||
it('is transpiled correctly with visitors and removes fields with no explicit visibility', function () { | ||
let transpilerOut = tspoon.transpile(sourceCode, configWithVisitors); | ||
let TwoNames = evaluateModuleExports(transpilerOut.code)['TwoNames']; | ||
let instance = new TwoNames(); | ||
expect(instance.publicName, 'publicName').to.eql('Doe'); | ||
expect(instance.privateName, 'privateName').not.to.be.ok; | ||
}); | ||
it('is transpiled correctly with visitors and removes fields with no explicit visibility', function() { | ||
let transpilerOut = tspoon.transpile(sourceCode, configWithVisitors); | ||
let TwoNames = evaluateModuleExports(transpilerOut.code)['TwoNames']; | ||
let instance = new TwoNames(); | ||
expect(instance.publicName, 'publicName').to.eql('Doe'); | ||
expect(instance.privateName, 'privateName').not.to.be.ok; | ||
}); | ||
}); |
import {expect} from 'chai'; | ||
import * as tspoon from '../src/index'; | ||
import { evaluateModuleExports } from '../test-kit/index'; | ||
import {evaluateModuleExports} from '../test-kit/index'; | ||
let visitor = require('../../examples/readme/alertProperty.js'); | ||
describe('readme example', function () { | ||
let sourceCode, config; | ||
before(() => { | ||
sourceCode = require('../../examples/readme/src.ts'); // the path is relative to tspoon/dist/test | ||
config = { | ||
sourceFileName: 'src.ts', | ||
visitors: [visitor] | ||
}; | ||
}); | ||
describe('readme example', function() { | ||
let sourceCode, config; | ||
before(() => { | ||
sourceCode = require('../../examples/readme/src.ts'); // the path is relative to tspoon/dist/test | ||
config = { | ||
sourceFileName: 'src.ts', | ||
visitors: [visitor] | ||
}; | ||
}); | ||
it('is transpiled', function () { | ||
let transpilerOut = tspoon.transpile(sourceCode, config); | ||
expect(transpilerOut.halted).not.to.be.ok; | ||
expect(transpilerOut.code).to.be.ok; | ||
expect(transpilerOut.sourceMap).to.be.ok; | ||
}); | ||
it('is transpiled', function() { | ||
let transpilerOut = tspoon.transpile(sourceCode, config); | ||
expect(transpilerOut.halted).not.to.be.ok; | ||
expect(transpilerOut.code).to.be.ok; | ||
expect(transpilerOut.sourceMap).to.be.ok; | ||
}); | ||
it('is transpiled correctly with visitors', function () { | ||
let transpilerOut = tspoon.transpile(sourceCode, config); | ||
let TwoNames = evaluateModuleExports(transpilerOut.code)['TwoNames']; | ||
let instance = new TwoNames(); | ||
expect(transpilerOut.halted).not.to.be.ok; | ||
expect(instance.publicName, 'publicName').to.eql('Doe'); | ||
expect(instance.privateName, 'privateName').to.eql('John'); | ||
}); | ||
it('is transpiled correctly with visitors', function() { | ||
let transpilerOut = tspoon.transpile(sourceCode, config); | ||
let TwoNames = evaluateModuleExports(transpilerOut.code)['TwoNames']; | ||
let instance = new TwoNames(); | ||
expect(transpilerOut.halted).not.to.be.ok; | ||
expect(instance.publicName, 'publicName').to.eql('Doe'); | ||
expect(instance.privateName, 'privateName').to.eql('John'); | ||
}); | ||
it('is transpiled with two diagnostics', function () { | ||
expect(tspoon.transpile(sourceCode, config).diags.length).to.eql(2); | ||
}); | ||
it('is transpiled with two diagnostics', function() { | ||
expect(tspoon.transpile(sourceCode, config).diags.length).to.eql(2); | ||
}); | ||
}); |
import {expect} from 'chai'; | ||
import * as ts from 'typescript'; | ||
import * as tspoon from '../src/index'; | ||
import * as ts from 'typescript'; | ||
import {ValidatorConfig} from "../src/transpile"; | ||
import {VisitorContext} from "../index"; | ||
import {Visitor} from "../src/visitor"; | ||
import {MockModule} from "../test-kit/mocks/resolution-hosts"; | ||
import {ValidatorConfig} from '../src/transpile'; | ||
import {VisitorContext} from '../index'; | ||
import {Visitor} from '../src/visitor'; | ||
import {MockModule} from '../test-kit/mocks/resolution-hosts'; | ||
function beforeVariable(varName: string) { | ||
return { | ||
insert(code: string): Visitor { | ||
return { | ||
filter: (node:ts.Node) => { | ||
if (node.kind === ts.SyntaxKind.VariableDeclarationList) { | ||
const declList = <ts.VariableDeclarationList>node; | ||
return declList.declarations.some((decl:ts.VariableDeclaration) => decl.name.getText() === varName); | ||
} | ||
return false; | ||
}, | ||
return { | ||
insert(code: string): Visitor { | ||
return { | ||
filter: (node: ts.Node) => { | ||
if (node.kind === ts.SyntaxKind.VariableDeclarationList) { | ||
const declList = <ts.VariableDeclarationList>node; | ||
return declList.declarations.some((decl: ts.VariableDeclaration) => decl.name.getText() === varName); | ||
} | ||
return false; | ||
}, | ||
visit: (node:ts.Node, context:VisitorContext) => { | ||
context.insertLine(node.pos, code); | ||
} | ||
} | ||
} | ||
} | ||
visit: (node: ts.Node, context: VisitorContext) => { | ||
context.insertLine(node.pos, code); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
describe('tspoon.validateAll()', function () { | ||
it("lets valid code pass", function () { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', 'export const perfectlyValid: number = 666;'), | ||
new MockModule('index2.ts', 'export const perfectlyValid2: number = 777;') | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts', 'index2.ts'], config)).to.pass(); | ||
}); | ||
describe('tspoon.validateAll()', function() { | ||
it('lets valid code pass', function() { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', 'export const perfectlyValid: number = 666;'), | ||
new MockModule('index2.ts', 'export const perfectlyValid2: number = 777;') | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts', 'index2.ts'], config)).to.pass(); | ||
}); | ||
it("makes invalid code fail", function () { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', 'export const perfectlyValid: number = 666;'), | ||
new MockModule('index2.ts', `// This comment here | ||
it('makes invalid code fail', function() { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', 'export const perfectlyValid: number = 666;'), | ||
new MockModule('index2.ts', `// This comment here | ||
// is just | ||
// to add some lines | ||
export const perfectlyValid: number = 666; | ||
export const perfectlyInvalid: SomeUndefinedType = "HAHAHA"; | ||
export const perfectlyInvalid: SomeUndefinedType = 'HAHAHA'; | ||
`) | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts', 'index2.ts'], config)).to.fail() | ||
.withMessageCount(1) | ||
.withMessage(/index2.ts -> 5:\d+ Cannot find name 'SomeUndefinedType'./); | ||
}); | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts', 'index2.ts'], config)).to.fail() | ||
.withMessageCount(1) | ||
.withMessage(/index2.ts -> 5:\d+ Cannot find name 'SomeUndefinedType'./); | ||
}); | ||
it("lets invalid code that was fixed by a visitor pass", function () { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', ` | ||
it('lets invalid code that was fixed by a visitor pass', function() { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', ` | ||
// This comment here | ||
@@ -65,17 +65,17 @@ // is just | ||
const perfectlyValid: number = 666; | ||
const perfectlyInvalid: SomeUndefinedType = "HAHAHA"; | ||
const perfectlyInvalid: SomeUndefinedType = 'HAHAHA'; | ||
`) | ||
], | ||
], | ||
mutators: [ | ||
beforeVariable('perfectlyInvalid').insert('\ntype SomeUndefinedType = string;') | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts'], config)).to.pass(); | ||
}); | ||
mutators: [ | ||
beforeVariable('perfectlyInvalid').insert('\ntype SomeUndefinedType = string;') | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts'], config)).to.pass(); | ||
}); | ||
it("preserves error lines despite the modifications", function () { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', ` // line 1 | ||
it('preserves error lines despite the modifications', function() { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', ` // line 1 | ||
// This comment here // line 2 | ||
@@ -85,102 +85,101 @@ // is just // line 3 | ||
const perfectlyValid: number = 666; // line 5 | ||
const perfectlyInvalid: SomeUndefinedType = "HAHAHA"; // line 6 | ||
const perfectlyInvalid: SomeUndefinedType = 'HAHAHA'; // line 6 | ||
`) | ||
], | ||
mutators: [ | ||
beforeVariable('perfectlyInvalid').insert('\nconst anotherValidLine: number = 777;') | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts'], config)).to.fail() | ||
.withMessageCount(1) | ||
.withMessage(/.* -> 6:\d+ Cannot find name 'SomeUndefinedType'./); | ||
}); | ||
], | ||
mutators: [ | ||
beforeVariable('perfectlyInvalid').insert('\nconst anotherValidLine: number = 777;') | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts'], config)).to.fail() | ||
.withMessageCount(1) | ||
.withMessage(/.* -> 6:\d+ Cannot find name 'SomeUndefinedType'./); | ||
}); | ||
it("modifies a dependency of the validated file", function () { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', ` | ||
it('modifies a dependency of the validated file', function() { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', ` | ||
import Product from './Product'; | ||
const product: Product = { title: 'Sample' }; | ||
`), | ||
new MockModule('Product.ts', ` | ||
new MockModule('Product.ts', ` | ||
interface Product { title: string; } | ||
const somethingUnrelated: string = 'what?'; | ||
`) | ||
], | ||
mutators: [ | ||
beforeVariable('somethingUnrelated').insert('export default Product;') | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts'], config)).to.pass(); | ||
}); | ||
], | ||
mutators: [ | ||
beforeVariable('somethingUnrelated').insert('export default Product;') | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts'], config)).to.pass(); | ||
}); | ||
it("fails gracefully with syntactically incorrect input", function () { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', ` | ||
it('fails gracefully with syntactically incorrect input', function() { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', ` | ||
import {Product} from './Product'; | ||
const product: Product = { title: 'Sample' | ||
`), | ||
new MockModule('Product.ts', ` | ||
new MockModule('Product.ts', ` | ||
export class Product { title: string; } | ||
const somethingUnrelated: string = 'what?'; | ||
`) | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts'], config)).to.fail() | ||
.withMessage(/index.ts -> \d+:\d+ '}' expected./); | ||
}); | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts'], config)).to.fail() | ||
.withMessage(/index.ts -> \d+:\d+ '}' expected./); | ||
}); | ||
it("fails gracefully with syntactically incorrect dependency", function () { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', ` | ||
it('fails gracefully with syntactically incorrect dependency', function() { | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('index.ts', ` | ||
import {Product} from './Product'; | ||
const product: Product = { title: 'Sample' } | ||
`), | ||
new MockModule('Product.ts', ` | ||
new MockModule('Product.ts', ` | ||
export class Product { title: string; } | ||
const somethingUnrelated: string = 'what? | ||
`) | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts'], config)).to.fail() | ||
.withMessage(/Product.ts -> \d+:\d+ Unterminated string literal./); | ||
}); | ||
] | ||
}; | ||
expect(tspoon.validateAll(['index.ts'], config)).to.fail() | ||
.withMessage(/Product.ts -> \d+:\d+ Unterminated string literal./); | ||
}); | ||
it("can access semantic information", function () { | ||
this.timeout(10000); | ||
class MockVisitor implements Visitor { | ||
public realTypeName: string; | ||
it('can access semantic information', function() { | ||
this.timeout(10000); | ||
class MockVisitor implements Visitor { | ||
public realTypeName: string; | ||
filter(node:ts.Node): boolean { | ||
return node.getSourceFile().fileName === 'index.ts' && node.kind === ts.SyntaxKind.VariableDeclaration; | ||
} | ||
filter(node: ts.Node): boolean { | ||
return node.getSourceFile().fileName === 'index.ts' && node.kind === ts.SyntaxKind.VariableDeclaration; | ||
} | ||
visit(node:ts.Node, context:VisitorContext): void { | ||
const ls: ts.LanguageService = context.getLanguageService(); | ||
const x = ls.getTypeDefinitionAtPosition(node.getSourceFile().fileName, node.getStart()); | ||
this.realTypeName = x[0].name; | ||
} | ||
} | ||
const visitor = new MockVisitor(); | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('a.ts', ` | ||
visit(node: ts.Node, context: VisitorContext): void { | ||
const ls: ts.LanguageService = context.getLanguageService(); | ||
const x = ls.getTypeDefinitionAtPosition(node.getSourceFile().fileName, node.getStart()); | ||
this.realTypeName = x[0].name; | ||
} | ||
} | ||
const visitor = new MockVisitor(); | ||
const config: ValidatorConfig = { | ||
resolutionHosts: [ | ||
new MockModule('a.ts', ` | ||
export default class Product {} | ||
`), | ||
new MockModule('index.ts', ` | ||
new MockModule('index.ts', ` | ||
import {default as SomeClass} from './a'; | ||
const a: SomeClass = null; | ||
`), | ||
new MockModule("lib.d.ts", require('typescript/lib/lib.d.ts')) | ||
], | ||
mutators: [ | ||
visitor | ||
] | ||
}; | ||
tspoon.validateAll(['index.ts'], config); | ||
expect(visitor.realTypeName).to.equal('Product'); | ||
}); | ||
new MockModule('lib.d.ts', require('typescript/lib/lib.d.ts')) | ||
], | ||
mutators: [ | ||
visitor | ||
] | ||
}; | ||
tspoon.validateAll(['index.ts'], config); | ||
expect(visitor.realTypeName).to.equal('Product'); | ||
}); | ||
}); |
@@ -1,11 +0,11 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
/// <reference path='../typings/main.d.ts' /> | ||
require('source-map-support').install(); | ||
import "./mutable-source-code.spec"; | ||
import "./transpile.spec"; | ||
import "./visitor.spec"; | ||
import "./transpile.spec"; | ||
import "./e2e.validate-all.spec"; | ||
import "./e2e.example-poc.spec"; | ||
import "./e2e.example-readme.spec"; | ||
import './mutable-source-code.spec'; | ||
import './transpile.spec'; | ||
import './visitor.spec'; | ||
import './transpile.spec'; | ||
import './e2e.validate-all.spec'; | ||
import './e2e.example-poc.spec'; | ||
import './e2e.example-readme.spec'; |
@@ -1,75 +0,68 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
import {expect} from 'chai'; | ||
import * as ts from 'typescript'; | ||
import {MutableSourceCode} from '../src/mutable-source-code'; | ||
import {defaultCompilerOptions} from '../src/configuration'; | ||
import {FastAppendAction, FastRewriteAction, ReplaceAction, InsertAction} from '../src/mutable-source-code'; | ||
import { expect } from 'chai'; | ||
import * as ts from "typescript"; | ||
import { MutableSourceCode, } from "../src/mutable-source-code"; | ||
import { defaultCompilerOptions } from '../src/configuration'; | ||
import {FastAppendAction, FastRewriteAction, ReplaceAction, InsertAction} from "../src/mutable-source-code"; | ||
function aSourceMapperFor(source: string): MutableSourceCode { | ||
const ast = ts.createSourceFile("test.ts", source, defaultCompilerOptions.target, true); | ||
return new MutableSourceCode(ast); | ||
const ast = ts.createSourceFile('test.ts', source, defaultCompilerOptions.target, true); | ||
return new MutableSourceCode(ast); | ||
} | ||
describe("Mutable source actions performs", function () { | ||
it("FastAppendAction at the end of source", ()=> { | ||
const source = "const someCode = 'Some string';"; | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([ new FastAppendAction('const b = 666;')]); | ||
expect(mutableCode.code).to.equal("const someCode = 'Some string';const b = 666;"); | ||
}); | ||
describe('Mutable source actions performs', function() { | ||
it('FastAppendAction at the end of source', () => { | ||
const source = `const someCode = 'Some string';`; | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([new FastAppendAction('const b = 666;')]); | ||
expect(mutableCode.code).to.equal(`const someCode = 'Some string';const b = 666;`); | ||
}); | ||
it("FastRewriteAction", function () { | ||
const source = "const someCode = 'Some string';"; | ||
// 0123456789012345678901234567890 | ||
// 1 2 3 | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([ new FastRewriteAction(18, 'XXXXXXXXXXX')]); | ||
expect(mutableCode.code).to.equal("const someCode = 'XXXXXXXXXXX';"); | ||
}); | ||
it('FastRewriteAction', function() { | ||
const source = `const someCode = 'Some string';`; | ||
// 0123456789012345678901234567890 | ||
// 1 2 3 | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([new FastRewriteAction(18, 'XXXXXXXXXXX')]); | ||
expect(mutableCode.code).to.equal(`const someCode = 'XXXXXXXXXXX';`); | ||
}); | ||
it("ReplaceAction - replace style", function () { | ||
const source = "const someCode = 'Some string';"; | ||
// 0123456789012345678901234567890 | ||
// 1 2 3 | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([ new ReplaceAction(6, 30, 'b = 666')]); | ||
expect(mutableCode.code).to.equal("const b = 666;"); | ||
}); | ||
it('ReplaceAction - replace style', function() { | ||
const source = `const someCode = 'Some string';`; | ||
// 0123456789012345678901234567890 | ||
// 1 2 3 | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([new ReplaceAction(6, 30, 'b = 666')]); | ||
expect(mutableCode.code).to.equal('const b = 666;'); | ||
}); | ||
it("InsertAction", function () { | ||
const source = "const someCode = 'Some string';"; | ||
// 0123456789012345678901234567890 | ||
// 1 2 3 | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([ new InsertAction(6, '__')]); | ||
expect(mutableCode.code).to.equal("const __someCode = 'Some string';"); | ||
}); | ||
it('InsertAction', function() { | ||
const source = `const someCode = 'Some string';`; | ||
// 0123456789012345678901234567890 | ||
// 1 2 3 | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([new InsertAction(6, '__')]); | ||
expect(mutableCode.code).to.equal(`const __someCode = 'Some string';`); | ||
}); | ||
it("InsertAction (beginning)", function () { | ||
const source = "const someCode = 'Some string';"; | ||
// 0123456789012345678901234567890 | ||
// 1 2 3 | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([ new InsertAction(0, '__')]); | ||
expect(mutableCode.code).to.equal("__const someCode = 'Some string';"); | ||
}); | ||
it('InsertAction (beginning)', function() { | ||
const source = `const someCode = 'Some string';`; | ||
// 0123456789012345678901234567890 | ||
// 1 2 3 | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([new InsertAction(0, '__')]); | ||
expect(mutableCode.code).to.equal(`__const someCode = 'Some string';`); | ||
}); | ||
it("several actions in sequence", function () { | ||
const source = "const someCode = 'Some string';"; | ||
// 0123456789012345678901234567890 | ||
// 1 2 3 | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([ | ||
new ReplaceAction(6, 6, '__'), | ||
new FastAppendAction('const b = 666;'), | ||
new FastRewriteAction(18, 'XXXXXXXXXXX') | ||
]); | ||
expect(mutableCode.code).to.equal("const __someCode = 'XXXXXXXXXXX';const b = 666;"); | ||
}) | ||
it('several actions in sequence', function() { | ||
const source = `const someCode = 'Some string';`; | ||
// 0123456789012345678901234567890 | ||
// 1 2 3 | ||
const mutableCode = aSourceMapperFor(source); | ||
mutableCode.execute([ | ||
new ReplaceAction(6, 6, '__'), | ||
new FastAppendAction('const b = 666;'), | ||
new FastRewriteAction(18, 'XXXXXXXXXXX') | ||
]); | ||
expect(mutableCode.code).to.equal(`const __someCode = 'XXXXXXXXXXX';const b = 666;`); | ||
}) | ||
}); | ||
@@ -1,56 +0,53 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
import {expect} from 'chai'; | ||
import * as ts from 'typescript'; | ||
import {MutableSourceCode, ReplaceAction, Action} from '../src/mutable-source-code'; | ||
import {traverseAst} from '../src/traverse-ast'; | ||
import {findCodeRange, findCodePosition} from '../test-kit/index'; | ||
import {SingleFileHost} from '../src/hosts'; | ||
import {defaultCompilerOptions} from '../src/configuration'; | ||
import {RawSourceMap, SourceMapConsumer, SourceMapGenerator} from 'source-map'; | ||
import { expect } from 'chai'; | ||
import * as chai from "chai"; | ||
import * as ts from "typescript"; | ||
import { MutableSourceCode, ReplaceAction, Action} from "../src/mutable-source-code"; | ||
import { traverseAst } from '../src/traverse-ast'; | ||
import { findCodeRange, findCodePosition } from "../test-kit/index"; | ||
import { SingleFileHost } from '../src/hosts'; | ||
import { defaultCompilerOptions } from '../src/configuration'; | ||
import { RawSourceMap, SourceMapConsumer, SourceMapGenerator } from 'source-map'; | ||
function makeReplacement(source: string, atStr: string, insStr: string): Action { | ||
const textRange = findCodeRange(source, atStr); | ||
return new ReplaceAction(textRange.pos, textRange.end, insStr); | ||
const textRange = findCodeRange(source, atStr); | ||
return new ReplaceAction(textRange.pos, textRange.end, insStr); | ||
} | ||
function makeLineInsersion(source: string, atStr: string, insStr: string): Action { | ||
const textRange = findCodeRange(source, atStr); | ||
return new ReplaceAction(textRange.pos, textRange.pos, insStr + "\n"); | ||
const textRange = findCodeRange(source, atStr); | ||
return new ReplaceAction(textRange.pos, textRange.pos, insStr + '\n'); | ||
} | ||
function aSourceMapperFor(source: string): MutableSourceCode { | ||
const ast = ts.createSourceFile("test.ts", source, defaultCompilerOptions.target, true); | ||
return new MutableSourceCode(ast); | ||
const ast = ts.createSourceFile('test.ts', source, defaultCompilerOptions.target, true); | ||
return new MutableSourceCode(ast); | ||
} | ||
function expectSourceMapToMatchChangeForSuppliedText(source: string, target: string, sourceMap: RawSourceMap, text: string) { | ||
const positionBeforeChange: SourceMap.Position = findCodePosition(source, text); | ||
const positionAfterChange: SourceMap.Position = findCodePosition(target, text); | ||
const mapConsumer = new SourceMapConsumer(sourceMap); | ||
const mappedPosition: SourceMap.Position = mapConsumer.originalPositionFor(positionAfterChange); | ||
expect({ line: mappedPosition.line, column: mappedPosition.column }) | ||
.to.eql({ line: positionBeforeChange.line, column: positionBeforeChange.column }); | ||
const positionBeforeChange: SourceMap.Position = findCodePosition(source, text); | ||
const positionAfterChange: SourceMap.Position = findCodePosition(target, text); | ||
const mapConsumer = new SourceMapConsumer(sourceMap); | ||
const mappedPosition: SourceMap.Position = mapConsumer.originalPositionFor(positionAfterChange); | ||
expect({ line: mappedPosition.line, column: mappedPosition.column }) | ||
.to.eql({ line: positionBeforeChange.line, column: positionBeforeChange.column }); | ||
} | ||
function transpile(source: string): { code: string, map: RawSourceMap } { | ||
const ast = ts.createSourceFile("test.ts", source, defaultCompilerOptions.target, true); | ||
const compilerHost = new SingleFileHost(ast); | ||
const program = ts.createProgram(["test.ts"], defaultCompilerOptions, compilerHost); | ||
program.emit(); | ||
return { | ||
code: compilerHost.output, | ||
map: compilerHost.sourceMap | ||
}; | ||
const ast = ts.createSourceFile('test.ts', source, defaultCompilerOptions.target, true); | ||
const compilerHost = new SingleFileHost(ast); | ||
const program = ts.createProgram(['test.ts'], defaultCompilerOptions, compilerHost); | ||
program.emit(); | ||
return { | ||
code: compilerHost.output, | ||
map: compilerHost.sourceMap | ||
}; | ||
} | ||
describe("given a source code and given a replacement command, sourcemapper should", ()=> { | ||
describe('given a source code and given a replacement command, sourcemapper should', () => { | ||
const source = `class A {} | ||
const source = `class A {} | ||
PLACE_HOLDERclass B {} | ||
fubar();`; | ||
const target = `class A {} | ||
const target = `class A {} | ||
@bar | ||
@@ -61,36 +58,34 @@ @foo | ||
const action1 = makeLineInsersion(source, "PLACE_HOLDER", "@bar"); | ||
const action2 = makeReplacement(source, "PLACE_HOLDER", "@foo\n"); | ||
// adding action3 demonstrates problems with making changes based on a text after that text has been replaced (see action2). | ||
// using the next versin of magic-string may solve this | ||
// const action3 = makeLineInsersion(source, "PLACE_HOLDER", "\n@baz"); | ||
// mutableCode.execute([action1, action2 /*, action3*/]); | ||
const action1 = makeLineInsersion(source, 'PLACE_HOLDER', '@bar'); | ||
const action2 = makeReplacement(source, 'PLACE_HOLDER', '@foo\n'); | ||
// adding action3 demonstrates problems with making changes based on a text after that text has been replaced (see action2). | ||
// using the next versin of magic-string may solve this | ||
// const action3 = makeLineInsersion(source, 'PLACE_HOLDER', '\n@baz'); | ||
// mutableCode.execute([action1, action2 /*, action3*/]); | ||
var mutableCode; | ||
beforeEach(() =>{ | ||
mutableCode = aSourceMapperFor(source); | ||
var mutableCode; | ||
beforeEach(() => { | ||
mutableCode = aSourceMapperFor(source); | ||
}); | ||
}); | ||
it("generate a new string that matches the expected target", ()=> { | ||
mutableCode.execute([action1, action2 /*, action3*/]); | ||
it('generate a new string that matches the expected target', () => { | ||
mutableCode.execute([action1, action2 /*, action3*/]); | ||
expect(mutableCode.code).to.equal(target); | ||
}); | ||
expect(mutableCode.code).to.equal(target); | ||
}); | ||
it("generate correct sourcemap that reflects the changes", ()=> { | ||
mutableCode.execute([action1, action2 /*, action3*/]); | ||
it('generate correct sourcemap that reflects the changes', () => { | ||
mutableCode.execute([action1, action2 /*, action3*/]); | ||
expectSourceMapToMatchChangeForSuppliedText(source, mutableCode.code, mutableCode.sourceMap, "class B"); | ||
}); | ||
expectSourceMapToMatchChangeForSuppliedText(source, mutableCode.code, mutableCode.sourceMap, 'class B'); | ||
}); | ||
it("map the changes onto a sourcemap generated by typescript", ()=> { | ||
mutableCode.execute([action1, action2 /*, action3*/]); | ||
it('map the changes onto a sourcemap generated by typescript', () => { | ||
mutableCode.execute([action1, action2 /*, action3*/]); | ||
var result = transpile(mutableCode.code); | ||
const sourceMap = mutableCode.translateMap(result.map); | ||
expectSourceMapToMatchChangeForSuppliedText(source, result.code, sourceMap, "fubar()"); | ||
}); | ||
var result = transpile(mutableCode.code); | ||
const sourceMap = mutableCode.translateMap(result.map); | ||
expectSourceMapToMatchChangeForSuppliedText(source, result.code, sourceMap, 'fubar()'); | ||
}); | ||
}); | ||
@@ -1,73 +0,70 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
import {expect} from 'chai'; | ||
import {Node, Decorator, ClassDeclaration, SyntaxKind, CompilerOptions} from 'typescript'; | ||
import * as _ from 'lodash'; | ||
import {transpile, TranspilerConfig, VisitorContext} from '../src'; | ||
import { Node, Decorator, ClassDeclaration, SyntaxKind } from 'typescript'; | ||
import { CompilerOptions } from 'typescript'; | ||
import * as _ from 'lodash'; | ||
const config: TranspilerConfig = { | ||
sourceFileName: 'sample.tsx', | ||
visitors: [] | ||
sourceFileName: 'sample.tsx', | ||
visitors: [] | ||
}; | ||
describe('transpiler', function () { | ||
it('fails on parser errors', function () { | ||
const source = 'let a = <div><div></div>;'; | ||
const transpiled = transpile(source, config); | ||
expect(transpiled.code).not.to.be.ok; | ||
expect(transpiled.diags).not.to.be.empty; | ||
}); | ||
describe('transpiler', function() { | ||
it('fails on parser errors', function() { | ||
const source = 'let a = <div><div></div>;'; | ||
const transpiled = transpile(source, config); | ||
expect(transpiled.code).not.to.be.ok; | ||
expect(transpiled.diags).not.to.be.empty; | ||
}); | ||
describe('e2e regression test', ()=> { | ||
describe('e2e regression test', () => { | ||
const config2: TranspilerConfig = { | ||
compilerOptions : <CompilerOptions>{ | ||
inlineSourceMap : false, | ||
sourceMap: true, | ||
inlineSources : false, | ||
noEmitHelpers: false | ||
}, | ||
sourceFileName: 'sample.tsx', | ||
visitors: [{ | ||
filter: function(node: Node): boolean { | ||
return node.kind == SyntaxKind.ClassDeclaration && node.decorators && node.decorators.length > 0; | ||
}, | ||
const config2: TranspilerConfig = { | ||
compilerOptions: <CompilerOptions>{ | ||
inlineSourceMap: false, | ||
sourceMap: true, | ||
inlineSources: false, | ||
noEmitHelpers: false | ||
}, | ||
sourceFileName: 'sample.tsx', | ||
visitors: [{ | ||
filter: function(node: Node): boolean { | ||
return node.kind == SyntaxKind.ClassDeclaration && node.decorators && node.decorators.length > 0; | ||
}, | ||
visit: function(node: Node, context: VisitorContext): void { | ||
let targetPosition: number = node.pos; | ||
const classNode: ClassDeclaration = <ClassDeclaration> node; | ||
if(!_.isEmpty(classNode.decorators)) { | ||
targetPosition = (<Decorator>_.last(classNode.decorators)).end + 1; | ||
} | ||
// console.log("targetPosition", targetPosition); | ||
context.insertLine(targetPosition, `@fooo(\`------------------------ | ||
"tags": ["@type"], | ||
"properties": [ | ||
visit: function(node: Node, context: VisitorContext): void { | ||
let targetPosition: number = node.pos; | ||
const classNode: ClassDeclaration = <ClassDeclaration>node; | ||
if (!_.isEmpty(classNode.decorators)) { | ||
targetPosition = (<Decorator>_.last(classNode.decorators)).end + 1; | ||
} | ||
// console.log('targetPosition', targetPosition); | ||
context.insertLine(targetPosition, `@fooo(\`------------------------ | ||
'tags': ['@type'], | ||
'properties': [ | ||
{ | ||
"name": "title", | ||
"type": "core3.types.String" | ||
'name': 'title', | ||
'type': 'core3.types.String' | ||
}, | ||
{ | ||
"name": "price", | ||
"type": "core3.types.Number" | ||
'name': 'price', | ||
'type': 'core3.types.Number' | ||
}, | ||
{ | ||
"name": "flag", | ||
"type": "core3.types.Boolean" | ||
'name': 'flag', | ||
'type': 'core3.types.Boolean' | ||
}, | ||
{ | ||
"name": "func", | ||
"type": "core3.types.Function" | ||
'name': 'func', | ||
'type': 'core3.types.Function' | ||
} | ||
], | ||
"methods": [] | ||
'methods': [] | ||
\`)`); | ||
} | ||
}] | ||
}; | ||
} | ||
}] | ||
}; | ||
it('checks sample code doesn\'t get garbled up the same way it once did', () => { | ||
const source = ` | ||
/// <reference path="../../../typings/tsd.d.ts"/> | ||
it('checks sample code doesn\'t get garbled up the same way it once did', () => { | ||
const source = ` | ||
/// <reference path='../../../typings/tsd.d.ts'/> | ||
@@ -129,7 +126,7 @@ function bar(){ | ||
`; | ||
const transpiled = transpile(source, config2); | ||
expect(() => eval(transpiled.code)).not.to.throw(); | ||
}); | ||
}); | ||
const transpiled = transpile(source, config2); | ||
expect(() => eval(transpiled.code)).not.to.throw(); | ||
}); | ||
}); | ||
}); |
@@ -1,15 +0,12 @@ | ||
/// <reference path="../typings/main.d.ts" /> | ||
import * as ts from 'typescript'; | ||
import {expect} from 'chai'; | ||
import {findCodeRange, findCodePosition} from '../test-kit/index'; | ||
import {defaultCompilerOptions} from '../src/configuration'; | ||
import {Visitor, VisitorContext, transpile, TranspilerOutput} from '../src/index'; | ||
import {traverseAst} from '../src/traverse-ast'; | ||
import {TranspilerContext} from '../src/transpiler-context'; | ||
import {MutableSourceCode} from '../src/mutable-source-code'; | ||
import { expect } from 'chai'; | ||
import * as chai from "chai"; | ||
import * as ts from "typescript"; | ||
import { findCodeRange, findCodePosition } from "../test-kit/index"; | ||
import { defaultCompilerOptions } from '../src/configuration'; | ||
import { Visitor, VisitorContext, transpile, TranspilerOutput } from "../src/index"; | ||
import { traverseAst } from "../src/traverse-ast"; | ||
import { TranspilerContext } from "../src/transpiler-context"; | ||
import { MutableSourceCode } from "../src/mutable-source-code"; | ||
function applyVisitor(source: string, visitor: Visitor): TranspilerOutput { | ||
const ast = ts.createSourceFile("test.ts", source, defaultCompilerOptions.target, true); | ||
const ast = ts.createSourceFile('test.ts', source, defaultCompilerOptions.target, true); | ||
let context: TranspilerContext = new TranspilerContext(ast.fileName); | ||
@@ -28,3 +25,3 @@ traverseAst(ast, visitor, context); | ||
function matchDiagRanges(expected: ts.TextRange, actual: ts.Diagnostic): void { | ||
chai.expect({ | ||
expect({ | ||
start: expected.pos, | ||
@@ -38,79 +35,77 @@ end: expected.end | ||
describe("given source code", function () { | ||
describe('given source code', function() { | ||
describe("and a simple visitor, transpiler should", function () { | ||
const source = "\nclass A {}\nclass B {}\n"; | ||
describe('and a simple visitor, transpiler should', function() { | ||
const source = '\nclass A {}\nclass B {}\n'; | ||
const fakeVisitor: Visitor = { | ||
filter: (node: ts.Node): boolean => { | ||
return node.kind == ts.SyntaxKind.ClassDeclaration; | ||
}, | ||
visit: (node: ts.Node, context: VisitorContext): void => { | ||
context.insertLine(node.getStart(), "@blah"); | ||
context.replace(node.getStart(), node.getStart() + 'class'.length, "interface"); | ||
context.reportDiag(node, ts.DiagnosticCategory.Error, "Test message"); | ||
} | ||
}; | ||
const fakeVisitor: Visitor = { | ||
filter: (node: ts.Node): boolean => { | ||
return node.kind == ts.SyntaxKind.ClassDeclaration; | ||
}, | ||
visit: (node: ts.Node, context: VisitorContext): void => { | ||
context.insertLine(node.getStart(), '@blah'); | ||
context.replace(node.getStart(), node.getStart() + 'class'.length, 'interface'); | ||
context.reportDiag(node, ts.DiagnosticCategory.Error, 'Test message'); | ||
} | ||
}; | ||
let postVisitorOutput; | ||
let postVisitorOutput; | ||
beforeEach(()=>{ | ||
postVisitorOutput = applyVisitor(source, fakeVisitor); | ||
}); | ||
beforeEach(() => { | ||
postVisitorOutput = applyVisitor(source, fakeVisitor); | ||
}); | ||
const target = "\n@blah\ninterface A {}\n@blah\ninterface B {}\n"; | ||
const target = '\n@blah\ninterface A {}\n@blah\ninterface B {}\n'; | ||
it("generate the correct intermediate code", function () { | ||
chai.expect(postVisitorOutput.code).to.equal(target); | ||
}); | ||
it('generate the correct intermediate code', function() { | ||
expect(postVisitorOutput.code).to.equal(target); | ||
}); | ||
it("give correct diag positions", ()=> { | ||
it('give correct diag positions', () => { | ||
chai.expect(postVisitorOutput.diags).to.have.length(2); | ||
expect(postVisitorOutput.diags).to.have.length(2); | ||
matchDiagRanges( | ||
findCodeRange(source, "class A {}"), | ||
postVisitorOutput.diags[0]); | ||
matchDiagRanges( | ||
findCodeRange(source, 'class A {}'), | ||
postVisitorOutput.diags[0]); | ||
matchDiagRanges( | ||
findCodeRange(source, "class B {}"), | ||
postVisitorOutput.diags[1]); | ||
}); | ||
}); | ||
matchDiagRanges( | ||
findCodeRange(source, 'class B {}'), | ||
postVisitorOutput.diags[1]); | ||
}); | ||
}); | ||
describe("and a recursive visitor, transpiler should", function () { | ||
const source = "class A { methodA() {} }\nclass B { methodB() {} }"; | ||
describe('and a recursive visitor, transpiler should', function() { | ||
const source = 'class A { methodA() {} }\nclass B { methodB() {} }'; | ||
const subVisitor: Visitor = { | ||
filter: (node:ts.Node):boolean => { | ||
return node.kind == ts.SyntaxKind.MethodDeclaration; | ||
}, | ||
visit: (node:ts.Node, context:VisitorContext):void => { | ||
context.insertLine(node.getStart(), "@blah"); | ||
} | ||
}; | ||
const subVisitor: Visitor = { | ||
filter: (node: ts.Node): boolean => { | ||
return node.kind == ts.SyntaxKind.MethodDeclaration; | ||
}, | ||
visit: (node: ts.Node, context: VisitorContext): void => { | ||
context.insertLine(node.getStart(), '@blah'); | ||
} | ||
}; | ||
const fakeVisitor: Visitor = { | ||
filter: (node: ts.Node): boolean => { | ||
return node.kind == ts.SyntaxKind.ClassDeclaration; | ||
}, | ||
visit: (node: ts.Node, context: VisitorContext, traverse: (...visitors: Visitor[])=> void): void => { | ||
traverse(subVisitor); | ||
} | ||
}; | ||
const fakeVisitor: Visitor = { | ||
filter: (node: ts.Node): boolean => { | ||
return node.kind == ts.SyntaxKind.ClassDeclaration; | ||
}, | ||
visit: (node: ts.Node, context: VisitorContext, traverse: (...visitors: Visitor[]) => void): void => { | ||
traverse(subVisitor); | ||
} | ||
}; | ||
let postVisitorOutput; | ||
let postVisitorOutput; | ||
beforeEach(()=>{ | ||
postVisitorOutput = applyVisitor(source, fakeVisitor); | ||
}); | ||
beforeEach(() => { | ||
postVisitorOutput = applyVisitor(source, fakeVisitor); | ||
}); | ||
it("generate the correct intermediate code", function () { | ||
chai.expect(postVisitorOutput.code).to.equal( | ||
"class A { @blah\nmethodA() {} }\nclass B { @blah\nmethodB() {} }" | ||
); | ||
}); | ||
}); | ||
it('generate the correct intermediate code', function() { | ||
expect(postVisitorOutput.code).to.equal( | ||
'class A { @blah\nmethodA() {} }\nclass B { @blah\nmethodB() {} }' | ||
); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
833322
0
77
22886
+ Addedlodash@4.8.2(transitive)
- Removedlodash@4.7.0(transitive)
Updatedlodash@4.8.2