Parse module associated named types and generate in modules (#16)

* Parse module associated named types

* Generate associated types in module

* Lint

* Serialize associated types

* Lint

* Support multiple module paths

* Fix demo.ts

* Modify template

* Improve print

* Lower cased enum

* Uncapitalize enum

* Update template

* Remove gulp

* Cleanup
This commit is contained in:
Zhuoran 2021-06-03 17:33:46 +08:00 коммит произвёл GitHub
Родитель a7c6cc4a9c
Коммит 8437bf3d95
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 246 добавлений и 6957 удалений

Просмотреть файл

@ -2,6 +2,7 @@
// Copyright 2013-2018 Microsoft Inc.
//
// swiftformat:disable redundantRawValues
// Don't modify this file manually, it's auto generated.
public class {{moduleName}} {
@ -34,3 +35,7 @@ public class {{moduleName}} {
}
{{/methods}}
}
{{#associatedTypes}}
{{> swift-named-type}}
{{/associatedTypes}}

Просмотреть файл

@ -1,13 +1,4 @@
//
// Copyright 2013-2018 Microsoft Inc.
//
// swiftformat:disable redundantRawValues
// Don't modify this file manually, it's auto generated.
import UIKit
{{#customTypes}}
{{#custom}}
public struct {{typeName}}: Codable {
{{#members}}
public var {{name}}: {{type}}
@ -19,12 +10,11 @@ public struct {{typeName}}: Codable {
{{/members}}
}
}
{{/customTypes}}
{{#enumTypes}}
{{/custom}}
{{#enum}}
public enum {{typeName}}: {{valueType}}, Codable {
{{#members}}
case {{key}} = {{{value}}}
{{/members}}
}
{{/enumTypes}}
{{/enum}}

Просмотреть файл

@ -0,0 +1,12 @@
//
// Copyright 2013-2018 Microsoft Inc.
//
// swiftformat:disable redundantRawValues
// Don't modify this file manually, it's auto generated.
import UIKit
{{#.}}
{{> swift-named-type}}
{{/.}}

Просмотреть файл

@ -2,6 +2,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// swiftformat:disable redundantRawValues
import Foundation
public protocol {{moduleName}}: EditorNativeModule {
@ -46,6 +48,7 @@ class {{customTags.bridgeName}}: NativeModuleBridge {
parameters = try decoder.decode(Parameters.self, from: parametersData)
}
catch {
logAssertFail("Parameters of {{methodName}} are invalid: \(error)")
completion(.failure(NativeMethodError.invalidParameters(parametersData)))
return
}
@ -57,3 +60,7 @@ class {{customTags.bridgeName}}: NativeModuleBridge {
}
{{/methods}}
}
{{#associatedTypes}}
{{> swift-named-type}}
{{/associatedTypes}}

Просмотреть файл

@ -1,36 +0,0 @@
import {series, watch, dest} from 'gulp'
import ts from 'gulp-typescript'
import del from 'del'
import {spawn} from 'child_process'
const tsProject = ts.createProject("tsconfig.json", {declaration: true})
function tsBuild() {
return tsProject
.src()
.pipe(tsProject())
.pipe(dest("dist"))
}
async function run() {
spawn('node', ['dist/index.js'], {stdio: 'inherit'})
}
async function tsRun() {
spawn('ts-node', ['src/index.ts'], {stdio: 'inherit'})
}
function watchRun() {
watch('src/*.ts', tsRun)
}
export const build = tsBuild
export function clean() {
return del('dist')
}
export const start = series(clean, build, run)
export const dev = series(tsRun, watchRun)

3046
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -3,14 +3,12 @@
"version": "0.1.11",
"description": "Generate Native API based on TS interface",
"scripts": {
"build": "gulp build",
"clean": "gulp clean",
"start": "gulp start",
"dev": "gulp dev",
"build": "tsc",
"clean": "rm -rf dist",
"debug": "ts-node ./src/index",
"start:example": "ts-node ./src/demo/demo",
"test": "mocha -r ts-node/register test/**/*.ts",
"lint": "eslint ./src --ext .js,.jsx,.ts,.tsx",
"lint": "eslint ./src --ext .js,.ts",
"prettier:write": "prettier --write \"src/**/*.ts\"",
"prettier:check": "prettier --check \"src/**/*.ts\"",
"lint:fix": "npm run lint -- --fix && npm run prettier:write",
@ -30,7 +28,6 @@
"license": "private",
"devDependencies": {
"@types/chai": "^4.2.18",
"@types/gulp": "^4.0.6",
"@types/mocha": "^8.2.2",
"@types/mustache": "^4.1.1",
"@types/sinon": "^10.0.1",
@ -45,8 +42,6 @@
"eslint-config-airbnb-base": "^14.2.1",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-import": "^2.22.1",
"gulp": "^4.0.2",
"gulp-typescript": "^6.0.0-alpha.1",
"mocha": "^8.4.0",
"prettier": "^2.2.1",
"sinon": "^11.1.1",

Просмотреть файл

@ -2,13 +2,16 @@ import { CodeGenerator, RenderingLanguage } from '../generator/CodeGenerator';
function run(): void {
const generator = new CodeGenerator();
generator.parse({ tag: 'APIs', interfacePaths: ['src/demo/data/demoApi.ts'], defaultCustomTags: {}, dropInterfaceIPrefix: true });
generator.render({
tag: 'APIs',
generator.parse({
interfacePaths: ['src/demo/data/demoApi.ts'],
defaultCustomTags: {},
dropInterfaceIPrefix: true,
});
generator.renderModules({
index: 0,
language: RenderingLanguage.Swift,
outputDirectory: 'generated',
moduleTemplatePath: 'templates/swift-bridge.mustache',
namedTypesTemplatePath: 'templates/swift-named-types.mustache',
});
}

Просмотреть файл

@ -1,12 +1,12 @@
import fs from 'fs';
import path from 'path';
import { dropIPrefixInCustomTypes, fetchNamedTypes, NamedType } from './named-types';
import { dropIPrefixInCustomTypes, fetchNamedTypes, NamedType, NamedTypesResult } from './named-types';
import { Parser } from '../parser/Parser';
import { renderCode } from '../renderer/renderer';
import { SwiftCustomTypeView } from '../renderer/swift/SwiftCustomTypeView';
import { SwiftEnumTypeView } from '../renderer/swift/SwiftEnumTypeView';
import { SwiftModuleView } from '../renderer/swift/SwiftModuleView';
import { CustomTypeView, EnumTypeView, ModuleView, NamedTypesView } from '../renderer/views';
import { CustomTypeView, EnumTypeView, ModuleView, NamedTypeView } from '../renderer/views';
import { serializeModule, serializeNamedType } from '../serializers';
import { CustomType, EnumType, isCustomType, Module } from '../types';
import { applyDefaultCustomTags } from './utils';
@ -16,20 +16,18 @@ export enum RenderingLanguage {
}
export class CodeGenerator {
private modulesMap: Record<string, Module[]> = {};
private modulesMap: Module[][] = [];
private namedTypes: Record<string, NamedType> = {};
private namedTypes?: NamedTypesResult;
parse({
tag,
interfacePaths,
defaultCustomTags,
dropInterfaceIPrefix,
}: {
tag: string;
interfacePaths: string[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
defaultCustomTags: Record<string, any>,
defaultCustomTags: Record<string, any>;
dropInterfaceIPrefix: boolean;
}): void {
const parser = new Parser(interfacePaths);
@ -41,59 +39,80 @@ export class CodeGenerator {
dropIPrefixInCustomTypes(modules);
}
const namedTypes = fetchNamedTypes(modules);
this.modulesMap[tag] = modules;
this.pushNamedTypes(namedTypes);
this.modulesMap.push(modules);
}
printModules({ tag }: { tag: string }): void {
const modules = this.modulesMap[tag];
parseNamedTypes(): void {
this.namedTypes = fetchNamedTypes(Object.values(this.modulesMap).flatMap((modules) => modules));
}
printModules(index: number): void {
const modules = this.modulesMap[index];
if (modules === undefined) {
throw Error('Modules not parsed. Run parse first.');
}
console.log('Modules:\n');
console.log(modules.map((module) => serializeModule(module)).join('\n\n'));
}
printNamedTypes(): void {
console.log('\nNamed types:\n');
console.log(
Object.entries(this.namedTypes)
.map(([typeName, namedType]) => serializeNamedType(typeName, namedType))
.join('\n\n')
modules.map((module) => serializeModule(module, this.namedTypes?.associatedTypes[module.name] ?? [])).join('\n\n')
);
console.log();
}
render({
tag,
printSharedNamedTypes(): void {
if (this.namedTypes === undefined) {
throw Error('Named types not parsed. Run parseNamedTypes first.');
}
console.log('Shared named types:\n');
console.log(this.namedTypes.sharedTypes.map((namedType) => serializeNamedType(namedType)).join('\n\n'));
}
renderModules({
index,
language,
outputDirectory,
moduleTemplatePath,
namedTypesTemplatePath,
}: {
tag: string;
index: number;
language: RenderingLanguage;
outputDirectory: string;
moduleTemplatePath: string;
namedTypesTemplatePath: string;
}): void {
const modules = this.modulesMap[tag];
const modules = this.modulesMap[index];
if (modules === undefined) {
throw Error('Modules not parsed. Run parse first.');
}
if (this.namedTypes === undefined) {
throw Error('Named types not parsed. Run parseNamedTypes first.');
}
const { associatedTypes } = this.namedTypes;
modules.forEach((module) => {
const moduleView = this.getModuleView(language, module);
const moduleView = this.getModuleView(language, module, associatedTypes[module.name] ?? []);
const renderedCode = renderCode(moduleTemplatePath, moduleView);
this.writeFile(renderedCode, outputDirectory, `${moduleView.moduleName}${this.getFileExtension(language)}`);
});
}
const namedTypesView = this.getNamedTypesView(language, this.namedTypes);
renderNamedTypes({
language,
namedTypesTemplatePath,
namedTypesOutputPath,
}: {
language: RenderingLanguage;
namedTypesTemplatePath: string;
namedTypesOutputPath: string;
}): void {
if (this.namedTypes === undefined) {
throw Error('Named types not parsed. Run parseNamedTypes first.');
}
const namedTypesView = this.namedTypes.sharedTypes.map((namedType) => this.getNamedTypeView(language, namedType));
const renderedCode = renderCode(namedTypesTemplatePath, namedTypesView);
this.writeFile(renderedCode, outputDirectory, `Generated_CustomInterface${this.getFileExtension(language)}`);
fs.writeFileSync(namedTypesOutputPath, renderedCode);
}
private getFileExtension(language: RenderingLanguage): string {
@ -105,24 +124,26 @@ export class CodeGenerator {
}
}
private getNamedTypesView(language: RenderingLanguage, namedTypes: Record<string, NamedType>): NamedTypesView {
const namedTypesView: NamedTypesView = { customTypes: [], enumTypes: [] };
Object.entries(namedTypes).forEach(([typeName, namedType]) => {
private getNamedTypeView(language: RenderingLanguage, namedType: NamedType): NamedTypeView {
let namedTypeView: NamedTypeView;
if (isCustomType(namedType)) {
namedTypesView.customTypes.push(this.getCustomTypeView(language, typeName, namedType));
namedTypeView = this.getCustomTypeView(language, namedType.name, namedType);
namedTypeView.custom = true;
} else {
namedTypesView.enumTypes.push(this.getEnumTypeView(language, typeName, namedType));
}
});
return namedTypesView;
namedTypeView = this.getEnumTypeView(language, namedType);
namedTypeView.enum = true;
}
private getModuleView(language: RenderingLanguage, module: Module): ModuleView {
return namedTypeView;
}
private getModuleView(language: RenderingLanguage, module: Module, associatedTypes: NamedType[]): ModuleView {
switch (language) {
case RenderingLanguage.Swift:
return new SwiftModuleView(module);
return new SwiftModuleView(
module,
associatedTypes.map((associatedType) => this.getNamedTypeView(language, associatedType))
);
default:
throw Error('Unhandled language');
}
@ -137,10 +158,10 @@ export class CodeGenerator {
}
}
private getEnumTypeView(language: RenderingLanguage, typeName: string, enumType: EnumType): EnumTypeView {
private getEnumTypeView(language: RenderingLanguage, enumType: EnumType): EnumTypeView {
switch (language) {
case RenderingLanguage.Swift:
return new SwiftEnumTypeView(typeName, enumType);
return new SwiftEnumTypeView(enumType);
default:
throw Error('Unhandled language');
}
@ -150,14 +171,4 @@ export class CodeGenerator {
const filePath = path.join(outputDirectory, fileName);
fs.writeFileSync(filePath, content);
}
private pushNamedTypes(namedTypes: Record<string, NamedType>): void {
Object.entries(namedTypes).forEach(([typeName, namedType]) => {
if (this.namedTypes[typeName] !== undefined) {
return;
}
this.namedTypes[typeName] = namedType;
});
}
}

Просмотреть файл

@ -1,7 +1,5 @@
import { capitalize } from '../utils';
import {
CustomType,
EnumType,
isArraryType,
isCustomType,
isDictionaryType,
@ -9,12 +7,17 @@ import {
isOptionalType,
Module,
ValueType,
CustomType,
EnumType,
} from '../types';
export type NamedType = CustomType | EnumType;
export type NamedType = (CustomType & { name: string }) | EnumType;
export type NamedTypesResult = { associatedTypes: Record<string, NamedType[]>; sharedTypes: NamedType[] };
export function dropIPrefixInCustomTypes(modules: Module[]): void {
fetchRootTypes(modules).forEach((valueType) => {
modules
.flatMap((module) => fetchRootTypes(module))
.forEach((valueType) => {
recursiveVisitNamedType(valueType, (namedType) => {
if (!isCustomType(namedType)) {
return;
@ -25,37 +28,53 @@ export function dropIPrefixInCustomTypes(modules: Module[]): void {
});
}
export function fetchNamedTypes(modules: Module[]): Record<string, NamedType> {
const typeMap: Record<string, NamedType> = {};
export function fetchNamedTypes(modules: Module[]): NamedTypesResult {
const typeMap: Record<string, { namedType: NamedType; associatedModules: Set<string> }> = {};
fetchRootTypes(modules).forEach((valueType) => {
modules.forEach((module) => {
fetchRootTypes(module).forEach((valueType) => {
recursiveVisitNamedType(valueType, (namedType, path) => {
if (namedType.name === undefined) {
namedType.name = path;
}
if (typeMap[namedType.name] !== undefined) {
return;
if (typeMap[namedType.name] === undefined) {
typeMap[namedType.name] = { namedType: namedType as NamedType, associatedModules: new Set() };
}
typeMap[namedType.name] = namedType;
typeMap[namedType.name].associatedModules.add(module.name);
});
});
});
return typeMap;
const associatedTypes: Record<string, NamedType[]> = {};
const sharedTypes: NamedType[] = [];
Object.values(typeMap).forEach(({ namedType, associatedModules }) => {
if (associatedModules.size === 1) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const moduleName: string = associatedModules.values().next().value;
if (associatedTypes[moduleName] === undefined) {
associatedTypes[moduleName] = [];
}
associatedTypes[moduleName].push(namedType);
} else {
sharedTypes.push(namedType);
}
});
return { associatedTypes, sharedTypes };
}
function fetchRootTypes(modules: Module[]): ValueType[] {
return modules
.flatMap((module) => module.methods)
.flatMap((method) =>
function fetchRootTypes(module: Module): ValueType[] {
return module.methods.flatMap((method) =>
method.parameters.map((parameter) => parameter.type).concat(method.returnType ? [method.returnType] : [])
);
}
function recursiveVisitNamedType(
valueType: ValueType,
visit: (namedType: NamedType, path: string) => void,
visit: (namedType: CustomType | EnumType, path: string) => void,
path = ''
): void {
if (isCustomType(valueType)) {

Просмотреть файл

@ -2,27 +2,33 @@ import yargs from 'yargs';
import { CodeGenerator, RenderingLanguage } from './generator/CodeGenerator';
import { parseKeyValueText } from './utils';
interface Config {
moduleGenerationMaps: { interfacePaths: string[]; moduleTemplatePath: string; outputDirectory: string }[];
namedTypesTemplatePath: string;
namedTypesOutputPath: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
defaultCustomTags?: any;
dropInterfaceIPrefix?: boolean;
}
const program = yargs(process.argv.slice(2));
const args = program
.config()
.options({
interfacePaths: {
type: 'string',
array: true,
demandOption: true,
describe: 'The path of api interface which should extend IExportedApi',
},
outputDirectory: {
type: 'string',
demandOption: true,
describe: 'The path of output directory',
},
moduleTemplatePath: {
type: 'string',
demandOption: true,
describe: 'The path of module template',
},
namedTypesTemplate: {
namedTypesTemplatePath: {
type: 'string',
demandOption: true,
describe: 'The path of named types template',
@ -30,7 +36,7 @@ const args = program
defaultCustomTag: {
type: 'string',
array: true,
coerce: (values: string[]) => values.map(tagString => parseKeyValueText(tagString)),
coerce: (values: string[]) => values.map((tagString) => parseKeyValueText(tagString)),
default: [],
describe: 'Default values for custom tags',
},
@ -43,20 +49,39 @@ const args = program
.help().argv;
function run(): void {
const config = args as unknown as Config;
const generator = new CodeGenerator();
config.moduleGenerationMaps.forEach((moduleGenerationMap, index) => {
generator.parse({
tag: 'APIs',
interfacePaths: args.interfacePaths,
defaultCustomTags: Object.fromEntries(args.defaultCustomTag.map(tag => [tag.key, tag.value])),
dropInterfaceIPrefix: args.dropInterfaceIPrefix,
interfacePaths: moduleGenerationMap.interfacePaths,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
defaultCustomTags: config.defaultCustomTags ?? {},
dropInterfaceIPrefix: config.dropInterfaceIPrefix ?? false,
});
generator.printModules({ tag: 'APIs' });
generator.render({
tag: 'APIs',
generator.printModules(index);
});
generator.parseNamedTypes();
generator.printSharedNamedTypes();
config.moduleGenerationMaps.forEach((_, index) => {
generator.printModules(index);
});
config.moduleGenerationMaps.forEach((moduleGenerationMap, index) => {
generator.renderModules({
index,
language: RenderingLanguage.Swift,
outputDirectory: args.outputDirectory,
moduleTemplatePath: args.moduleTemplatePath,
namedTypesTemplatePath: args.namedTypesTemplate,
outputDirectory: moduleGenerationMap.outputDirectory,
moduleTemplatePath: moduleGenerationMap.moduleTemplatePath,
});
});
generator.renderNamedTypes({
language: RenderingLanguage.Swift,
namedTypesTemplatePath: config.namedTypesTemplatePath,
namedTypesOutputPath: config.namedTypesOutputPath,
});
}

Просмотреть файл

@ -28,7 +28,11 @@ export class Parser {
parse(): Module[] {
const modules: Module[] = [];
this.program.getSourceFiles().forEach((sourceFile) => {
this.program.getRootFileNames().forEach((fileName) => {
const sourceFile = this.program.getSourceFile(fileName);
if (sourceFile === undefined) {
throw Error('Source file not found');
}
ts.forEachChild(sourceFile, (node) => {
const module = this.moduleFromNode(node);
if (module !== null) {

Просмотреть файл

@ -1,7 +1,12 @@
import fs from 'fs';
import path from 'path';
import Mustache from 'mustache';
export function renderCode<View>(templatePath: string, view: View): string {
const template = fs.readFileSync(templatePath).toString();
return Mustache.render(template, view);
const directory = path.dirname(templatePath);
return Mustache.render(template, view, (partialName) => {
const partialPath = path.join(directory, `${partialName}.mustache`);
return fs.readFileSync(partialPath).toString();
});
}

Просмотреть файл

@ -1,8 +1,13 @@
import { uncapitalize } from '../../utils';
import { EnumSubType, EnumType } from '../../types';
import { EnumTypeView } from '../views';
export class SwiftEnumTypeView implements EnumTypeView {
constructor(readonly typeName: string, private enumType: EnumType) {}
constructor(private enumType: EnumType) {}
get typeName(): string {
return this.enumType.name;
}
get valueType(): string {
switch (this.enumType.subType) {
@ -17,7 +22,8 @@ export class SwiftEnumTypeView implements EnumTypeView {
get members(): { key: string; value: string }[] {
return Object.entries(this.enumType.members).map(([key, value]) => ({
key,
// TODO: Convert to camel case instead of uncapitalize
key: uncapitalize(key),
value: typeof value === 'string' ? `"${value}"` : `${value}`,
}));
}

Просмотреть файл

@ -1,9 +1,9 @@
import { Module } from '../../types';
import { ModuleView, MethodView } from '../views';
import { ModuleView, MethodView, NamedTypeView } from '../views';
import { SwiftMethodView } from './SwiftMethodView';
export class SwiftModuleView implements ModuleView {
constructor(private module: Module) {}
constructor(private readonly module: Module, readonly associatedTypes: NamedTypeView[]) {}
get moduleName(): string {
return this.module.name;

Просмотреть файл

@ -9,13 +9,11 @@ export interface MethodView {
export interface ModuleView {
readonly moduleName: string;
readonly methods: MethodView[];
readonly associatedTypes: NamedTypeView[];
readonly customTags: Record<string, string>;
}
export interface NamedTypesView {
readonly customTypes: CustomTypeView[];
readonly enumTypes: EnumTypeView[];
}
export type NamedTypeView = (CustomTypeView | EnumTypeView) & { custom?: boolean; enum?: boolean };
export interface CustomTypeView {
readonly typeName: string;

Просмотреть файл

@ -18,7 +18,8 @@ const typeColor = chalk.yellow;
const valueColor = chalk.cyan;
const documentationColor = chalk.gray;
export function serializeModule(module: Module): string {
export function serializeModule(module: Module, associatedTypes: NamedType[]): string {
const serializedAssociatedTypes = associatedTypes.map((associatedType) => serializeNamedType(associatedType));
const customTags =
Object.keys(module.customTags).length > 0 ? `Custom tags: ${JSON.stringify(module.customTags)}\n` : '';
@ -32,13 +33,21 @@ ${module.methods
.map((line) => ` ${line}`)
.join('\n')
)
.join('\n')}
.join('\n')}${
serializedAssociatedTypes.length > 0
? `\n\n${serializedAssociatedTypes
.join('\n')
.split('\n')
.map((line) => ` ${line}`)
.join('\n')}`
: ''
}
}`;
}
export function serializeNamedType(typeName: string, namedType: NamedType): string {
export function serializeNamedType(namedType: NamedType): string {
if (isCustomType(namedType)) {
return `${keywordColor('Type')} ${typeName} {
return `${keywordColor('Type')} ${namedType.name} {
${namedType.members
.map(
(member) =>
@ -48,7 +57,7 @@ ${namedType.members
}`;
}
return `${keywordColor('Enum')} ${typeName} {
return `${keywordColor('Enum')} ${namedType.name} {
${Object.entries(namedType.members)
.map(([key, value]) => ` ${identifierColor(key)} = ${valueColor(value)}`)
.join('\n')}

Просмотреть файл

@ -6,8 +6,16 @@ export function capitalize(text: string): string {
return text[0].toUpperCase() + text.slice(1);
}
export function uncapitalize(text: string): string {
if (text.length === 0) {
return text;
}
return text[0].toLowerCase() + text.slice(1);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function parseKeyValueText(text: string): { key: string, value: any } {
export function parseKeyValueText(text: string): [string, any] {
const index = text.indexOf('=');
if (index === -1) {
throw Error('Invalid custom tag');
@ -24,5 +32,5 @@ export function parseKeyValueText(text: string): { key: string, value: any } {
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
return { key, value };
return [key, value];
}

Просмотреть файл

@ -24,7 +24,7 @@ describe('Parser', () => {
`;
withTempParser(exportedTrueSourceCode, parser => {
const modules = parser.parse();
expect(modules).to.deep.equal([{name: 'ExportTrueInterface', methods: [], documentation: ''}]);
expect(modules).to.deep.equal([{name: 'ExportTrueInterface', methods: [], documentation: '', customTags: {}}]);
});
});
@ -52,7 +52,8 @@ describe('Parser', () => {
returnType: null,
documentation: 'This is an example documentation for the method',
}],
documentation: 'This is an example documentation for the module'
documentation: 'This is an example documentation for the module',
customTags: {},
}]);
});
});
@ -71,7 +72,7 @@ describe('Parser', () => {
const stubWarn = sinon.stub(console, 'warn');
const modules = parser.parse();
expect(modules).to.deep.equal([{name: 'MockedInterface', methods: [], documentation: ''}]);
expect(modules).to.deep.equal([{name: 'MockedInterface', methods: [], documentation: '', customTags: {}}]);
const expectedWarning = warnMessage(`Skipped "invalidProperty: string;" at ${filePath}:5 because it is not valid method signature. Please define only methods.`);
expect(stubWarn).to.have.been.calledWith(expectedWarning);
@ -94,7 +95,7 @@ describe('Parser', () => {
const stubWarn = sinon.stub(console, 'warn');
const modules = parser.parse();
expect(modules).to.deep.equal([{name: 'MockedInterface', methods: [], documentation: ''}]);
expect(modules).to.deep.equal([{name: 'MockedInterface', methods: [], documentation: '', customTags: {}}]);
const expectedWarning = warnMessage(`Skipped "multipleParamsMethod(foo: string, bar: number);" at ${filePath}:5 because it has multiple parameters. Methods should only have one property. Please use destructuring object for multiple parameters.`);
expect(stubWarn).to.have.been.calledWith(expectedWarning);

Просмотреть файл

@ -1,5 +1,5 @@
{
"include": ["src/*.ts", "src/**/*.ts", "test/*.ts", "test/**/*.ts"],
"include": ["src/*.ts", "src/**/*.ts"],
"compilerOptions": {
"resolveJsonModule": true,
"target": "es6",

3727
yarn.lock

Разница между файлами не показана из-за своего большого размера Загрузить разницу