Skip to content
This repository has been archived by the owner on Jun 22, 2020. It is now read-only.

Commit

Permalink
feat(*): implement TypeScript declaration mapping
Browse files Browse the repository at this point in the history
Adds the necessary derived classes to map the paths in the generated TypeScript declaration files.

fixes #2
  • Loading branch information
mattyclarkson committed Feb 7, 2018
1 parent 3e3df9d commit 3392752
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 3 deletions.
10 changes: 8 additions & 2 deletions lib/Mapper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import EsExport from '@es/Export';
import EsFile from '@es/File';
import EsImport from '@es/Import';
import TsExport from '@ts/Export';
import TsFile from '@ts/File';
import TsImport from '@ts/Import';
import { existsSync as fileExistsSync, readFileSync as fileReadSync } from 'fs';
import { dirname, resolve } from 'path';
import * as ts from 'typescript';
Expand Down Expand Up @@ -48,12 +51,15 @@ export default class Mapper {
this.parsed = parsed;
}

async *files(): AsyncIterableIterator<EsFile> {
async *files(): AsyncIterableIterator<EsFile | TsFile> {
const { options } = this.parsed;
yield* this.parsed.fileNames.map(path => new EsFile({ path, options, config: this.parsed }));
if (options.declaration) {
yield* this.parsed.fileNames.map(path => new TsFile({ path, options, config: this.parsed }));
}
}

async *map(): AsyncIterableIterator<EsImport | EsExport> {
async *map(): AsyncIterableIterator<EsImport | EsExport | TsImport | TsExport> {
for await (const file of this.files()) {
yield* file.map(this.parsed.options);
}
Expand Down
32 changes: 32 additions & 0 deletions lib/ts/Declaration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ExportDeclaration, ImportDeclaration, StringLiteral, SyntaxKind } from 'typescript';

import Base, { IDerivedOptions as IBaseOptions } from '@lib/Declaration';

export type Interface = ExportDeclaration | ImportDeclaration;

export type IOptions<T extends Interface> = IBaseOptions<T>;

export default class Declaration<T extends Interface> extends Base<T> {
constructor({ declaration, ...rest}: IOptions<T>) {

// RAII checks
const { kind, text } = (declaration.moduleSpecifier as StringLiteral);
if (kind !== SyntaxKind.StringLiteral) {
throw new TypeError(`Invalid TS declaration source type: ${kind}`);
}

super({declaration, ...rest, path: text});
}

private get literal(): StringLiteral {
return (this.declaration.moduleSpecifier as StringLiteral);
}

get path(): string {
return this.literal.text;
}

protected update(value: string): void {
this.literal.text = value;
}
}
9 changes: 9 additions & 0 deletions lib/ts/Export.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ExportDeclaration } from 'typescript';

import Declaration, { IOptions as IDeclarationOptions } from '@ts/Declaration';

export type Interface = ExportDeclaration;

export type IOptions = IDeclarationOptions<Interface>;

export default class Export extends Declaration<Interface> {}
76 changes: 76 additions & 0 deletions lib/ts/File.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { PathLike, readFile as readFileSync, writeFile as writeFileSync } from 'fs';
import {
createPrinter,
createSourceFile,
EmitHint,
ExportDeclaration,
ImportDeclaration,
ScriptTarget,
SourceFile,
SyntaxKind,
} from 'typescript';
import { promisify } from 'util';

import ParseError from '@error/Parse';
import Base, { IDerivedOptions as IBaseOptions } from '@lib/File';
import Export from '@ts/Export';
import Import from '@ts/Import';

const readFile = promisify(readFileSync);
const writeFile = promisify(writeFileSync);

export type IOptions = IBaseOptions;

export default class File extends Base<Import, Export> {
private sourceFile: SourceFile | undefined;

constructor({ ...options }: IOptions) {
super({...options, extension: '.d.ts'});
this.sourceFile = undefined;
}

private get ast(): Promise<SourceFile> {
if (this.sourceFile) {
return Promise.resolve(this.sourceFile);
} else {
return (async () => {
const data = await readFile(this.destination.toString(), 'utf-8');
try {
return this.sourceFile = createSourceFile(this.destination.toString(), data, ScriptTarget.Latest);
} catch (error) {
if (error instanceof SyntaxError) {
throw new ParseError({file: this, error, data});
} else {
throw error;
}
}
})();
}
}

async *imports(): AsyncIterableIterator<Import> {
const { statements } = await this.ast;
yield* statements
.filter(({kind}) => kind === SyntaxKind.ImportDeclaration)
.map(n => new Import({file: this, declaration: n as ImportDeclaration}));
}

async *exports(): AsyncIterableIterator<Export> {
const { statements } = await this.ast;
yield* statements
.filter(({kind}) => kind === SyntaxKind.ExportDeclaration)
.map(n => new Export({file: this, declaration: n as ExportDeclaration}));
}

async write(path?: PathLike | number, options?: {
encoding?: string | null;
mode?: number | string;
flag?: string;
} | string | null): Promise<void> {
const sourceFile = await this.ast;
const { newLine } = this.options;
const printer = createPrinter({ newLine });
const data = printer.printNode(EmitHint.SourceFile, sourceFile, sourceFile);
return writeFile((path === undefined) ? this.destination.toString() : path, data, options);
}
}
9 changes: 9 additions & 0 deletions lib/ts/Import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ImportDeclaration } from 'typescript';

import Declaration, { IOptions as IDeclarationOptions } from '@ts/Declaration';

export type Interface = ImportDeclaration;

export type IOptions = IDeclarationOptions<Interface>;

export default class Import extends Declaration<Interface> {}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@
"^@test(.*)$": "<rootDir>/test$1",
"^@lib(.*)$": "<rootDir>/lib$1",
"^@error(.*)$": "<rootDir>/lib/error$1",
"^@es(.*)$": "<rootDir>/lib/es$1"
"^@es(.*)$": "<rootDir>/lib/es$1",
"^@ts(.*)$": "<rootDir>/lib/es$1"
}
}
}
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"paths": {
"@lib/*": ["lib/*"],
"@es/*": ["lib/es/*"],
"@ts/*": ["lib/ts/*"],
"@error/*": ["lib/error/*"]
},
"newLine": "LF",
Expand Down

0 comments on commit 3392752

Please sign in to comment.