This repository has been archived by the owner on Jul 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement test framework (and more changes i forgot)
- Loading branch information
1 parent
04216b9
commit 9f1193c
Showing
25 changed files
with
528 additions
and
333 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
node_modules/ | ||
tmp/ | ||
build/ | ||
lib/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
src | ||
tmp | ||
test | ||
src/ | ||
build/ | ||
test/ | ||
.mocharc.json | ||
tsconfig.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ | |
"description": "Typescript AST transformer to generate type checks at compile time", | ||
"scripts": { | ||
"build": "ttsc", | ||
"test": "mocha" | ||
"test": "mocha --timeout 0" | ||
}, | ||
"author": "Kevin Ramharak <[email protected]>", | ||
"repository": { | ||
|
@@ -34,13 +34,13 @@ | |
"@types/chai": "^4.2.14", | ||
"@types/mocha": "^8.2.0", | ||
"@types/node": "^14.14.22", | ||
"@typescript/vfs": "^1.3.2", | ||
"chai": "^4.2.0", | ||
"mocha": "^8.2.1", | ||
"ts-expose-internals": "^4.1.3", | ||
"ts-node": "^9.1.1", | ||
"ts-transform-runtime-check": "^0.0.1-alpha19", | ||
"ts-transform-test-compiler": "^1.1.0", | ||
"ttypescript": "^1.5.12", | ||
"typescript": "^4.1.3" | ||
"typescript": "^4.2.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
|
||
interface Logger { | ||
debug(...input: unknown[]): void; | ||
log(...input: unknown[]): void; | ||
info(...input: unknown[]): void; | ||
warn(...input: unknown[]): void; | ||
error(...input: unknown[]): void; | ||
assert(condition: boolean, ...input: unknown[]): asserts condition; | ||
} | ||
|
||
function noop(...input: unknown[]) {} | ||
|
||
const noopLogger: Logger = { | ||
debug: noop, | ||
log: noop, | ||
info: noop, | ||
warn: noop, | ||
error: noop, | ||
assert(condition, ...input) { | ||
if (!condition) { | ||
throw new Error('assertion error: ' + input.join(', ')); | ||
} | ||
} | ||
}; | ||
|
||
let _logger: Logger = noopLogger; | ||
|
||
export function useLogger(logger: Logger) { | ||
_logger = logger; | ||
} | ||
|
||
export function debug(...input: unknown[]) { | ||
_logger.debug(...input); | ||
} | ||
|
||
export function info(...input: unknown[]) { | ||
_logger.info(...input); | ||
} | ||
|
||
export function warn(...input: unknown[]) { | ||
_logger.warn(...input); | ||
} | ||
|
||
export function error(...input: unknown[]) { | ||
_logger.error(...input); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
|
||
import ts from 'typescript'; | ||
import * as tsvfs from '@typescript/vfs'; | ||
import { readFileSync, writeFileSync } from 'fs'; | ||
|
||
import RuntimeCheck from '../src/transformer'; | ||
import { IPackageOptions } from '../src/config'; | ||
import { Transform } from 'stream'; | ||
import { expect } from 'chai'; | ||
|
||
export interface FileDiagnostic { | ||
file: string; | ||
line: number; | ||
character: number; | ||
message: string; | ||
} | ||
|
||
export interface GeneralDiagnostic { | ||
message: string; | ||
} | ||
|
||
export type Diagnostic = FileDiagnostic | GeneralDiagnostic; | ||
|
||
type TransformerOptions = Partial<IPackageOptions> & Pick<IPackageOptions, 'PackageModuleName'>; | ||
|
||
function formatDiagnostic(diagnostic: Diagnostic = { message: 'stub diagnostic' }) { | ||
return typeof (diagnostic as FileDiagnostic).file === 'string' ? `${(diagnostic as FileDiagnostic).file}:${(diagnostic as FileDiagnostic).line}:${(diagnostic as FileDiagnostic).character} ${diagnostic.message}` : diagnostic.message; | ||
} | ||
|
||
export function createEnvironment(options: ts.CompilerOptions, transformerOptions: TransformerOptions) { | ||
const currentDirectory = process.cwd(); | ||
const fs = tsvfs.createDefaultMapFromNodeModules(options); | ||
const system = tsvfs.createFSBackedSystem(fs, currentDirectory, ts); | ||
|
||
// override the `getCurrentDirectory()` | ||
// https://github.com/microsoft/TypeScript-Website/blob/v2/packages/typescript-vfs/src/index.ts#L432 | ||
system.getCurrentDirectory = () => currentDirectory; | ||
|
||
const host = tsvfs.createVirtualCompilerHost(system, options, ts).compilerHost; | ||
const typeDefs = readFileSync('./lib/index.d.ts', { encoding: 'utf-8' }).replace(/ declare function /g, ' function '); | ||
writeFileSync('test/types.d.ts', `declare module '${transformerOptions.PackageModuleName}' {\n${typeDefs}\n}`); | ||
|
||
const rootNames = ['test/types.d.ts', 'test/values.ts']; | ||
|
||
const environment = { | ||
fs, | ||
system, | ||
host, | ||
compileString(input: string, inlineTransformerOptions: Partial<IPackageOptions> = {}) { | ||
inlineTransformerOptions = Object.assign({}, transformerOptions, inlineTransformerOptions); | ||
input = input.trim(); | ||
const tempFile = 'test/input.ts'; | ||
const template = ` | ||
import { is } from "${inlineTransformerOptions.PackageModuleName}"; | ||
output: { | ||
${input} | ||
} | ||
`.trim(); | ||
fs.set(tempFile, template); | ||
const program = ts.createProgram({ | ||
rootNames: [...rootNames, tempFile], | ||
options, | ||
host, | ||
}); | ||
const { diagnostics } = environment.emit(program, tempFile, inlineTransformerOptions); | ||
environment.assertNoDiagnostics(diagnostics); | ||
const result = fs.get(tempFile.replace('.ts', '.js')); | ||
if (!result) { | ||
throw new Error(`failed to compile string: '${input}'`); | ||
} | ||
const extractor = /output:\s*{\s*([\s\S]+)\s*}/m | ||
const extracted = result.match(extractor); | ||
if (extracted && extracted[1]) { | ||
const code = extracted[1]; | ||
// small hack to strip a trailing semi-colon if it was not present with the input | ||
if (!input.endsWith(';') && code.endsWith(';')) { | ||
return code.slice(0, -1); | ||
} | ||
return code; | ||
} | ||
throw new Error(` | ||
failed to extract contents from compield string. | ||
input: | ||
------------ | ||
${input} | ||
------------ | ||
output: | ||
------------ | ||
${result} | ||
------------ | ||
`.trim() | ||
); | ||
}, | ||
createProgram(files: string[], compilerOptions: Partial<ts.CompilerOptions> = {}) { | ||
return ts.createProgram({ | ||
rootNames: [...rootNames, ...files], | ||
options: Object.assign({}, options, compilerOptions), | ||
host, | ||
}); | ||
}, | ||
emit(program: ts.Program, target?: string | ts.SourceFile, options: Partial<IPackageOptions> = {}, writeFile: ts.WriteFileCallback = (fileName: string, content: string) => fs.set(fileName, content)) { | ||
if (typeof target === 'string') { | ||
const file = program.getSourceFile(target); | ||
if (file) { | ||
target = file; | ||
} else { | ||
throw new Error(`no source file exits for: '${target}'`); | ||
} | ||
} | ||
|
||
const result = program.emit(target, writeFile, void 0, void 0, { | ||
before: [ | ||
RuntimeCheck(program, Object.assign({}, transformerOptions, options)), | ||
], | ||
}); | ||
|
||
const diagnostics: Diagnostic[] = [...ts.getPreEmitDiagnostics(program), ...result.diagnostics].map(diagnostic => { | ||
if (diagnostic.file) { | ||
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!); | ||
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); | ||
return { | ||
file: diagnostic.file.fileName, | ||
line, | ||
character, | ||
message, | ||
}; | ||
} else { | ||
return { | ||
message: diagnostic.messageText as string, | ||
}; | ||
} | ||
}); | ||
|
||
return { | ||
diagnostics, | ||
}; | ||
}, | ||
assertNoDiagnostics(diagnostics: Diagnostic[]) { | ||
expect(diagnostics.length).to.equal(0, formatDiagnostic(diagnostics[0])); | ||
}, | ||
}; | ||
|
||
return environment; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import path from 'path'; | ||
import { Module as _Module } from 'module'; | ||
|
||
interface ModuleConstructor { | ||
_nodeModulePaths(directoryPath: string): string[]; | ||
} | ||
|
||
interface ModuleInstance { | ||
_compile(source: string, fileName: string): void; | ||
} | ||
|
||
const Module = _Module as typeof _Module & ModuleConstructor; | ||
|
||
export function createModule<T extends Record<string, any>>(fileName: string, source: string): InstanceType<typeof Module> & { exports: T } { | ||
fileName = path.resolve(process.cwd(), fileName); | ||
const mod = new Module(fileName, require.main) as InstanceType<typeof Module> & ModuleInstance; | ||
mod.filename = fileName; | ||
mod.paths = Module._nodeModulePaths(path.dirname(fileName)); | ||
mod._compile(source, fileName); | ||
return mod; | ||
} |
Oops, something went wrong.