Skip to content

Commit 1e9bbad

Browse files
author
wangw19
committed
feat: 1. 新增对npm包类型的支持 2.修复非同类型枚举类型结果不准确问题 3.修复枚举类型中key为字符串时深度循环问题
1 parent 1e91e7a commit 1e9bbad

File tree

8 files changed

+126
-36
lines changed

8 files changed

+126
-36
lines changed

.eslintrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ module.exports = {
1414
'no-restricted-syntax': 0,
1515
'consistent-return': [0, { treatUndefinedAsUnspecified: true }],
1616
'@typescript-eslint/no-use-before-define': [0, { functions: true }],
17+
'global-require': 0,
18+
'import/no-dynamic-require': 0,
19+
'no-eval': 0,
1720
},
1821
ignorePatterns: ['src/__tests__/**', 'rollup.config.js', 'commitlint.config.js'],
1922
};

jest/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface AAA {
2+
other1: string;
3+
}

jest/interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { AAA, BBB, Param } from './common';
22
import { JSONSchema4TypeName } from 'json-schema'
3+
import * as npm from './npm'
34

45
interface Interface_0 {
56
attr: any;

jest/npm.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { AAA } from '.';
2+
import G from 'glob/common'
3+
import handlebars from 'handlebars'
4+
import * as chalk from 'chalk'
5+
import * as chalk_ from 'chalk/'
6+
7+
export type A = AAA
8+
export type B = G.IGlobBase

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fast-typescript-to-jsonschema",
3-
"version": "0.0.6",
3+
"version": "0.0.7",
44
"description": "fast-typescript-to-jsonschema generates JSON Schema files from your Typescript sources.",
55
"main": "./dist/index.js",
66
"typings": "dist/index.d.ts",

src/__tests__/__snapshots__/enum.test.ts.snap

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,8 @@ exports[`枚举_5 1`] = `
254254
Object {
255255
"enum": Array [
256256
0,
257-
NaN,
257+
"YES",
258258
],
259-
"type": "number",
260259
}
261260
`;
262261

src/__tests__/__snapshots__/interface.test.ts.snap

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,18 @@ exports[`Interface引用过滤文件_1 1`] = `
542542
Object {
543543
"additionalProperties": false,
544544
"definitions": Object {
545-
"AAA": Object {},
545+
"AAA": Object {
546+
"additionalProperties": false,
547+
"properties": Object {
548+
"other1": Object {
549+
"type": "string",
550+
},
551+
},
552+
"required": Array [
553+
"other1",
554+
],
555+
"type": "object",
556+
},
546557
},
547558
"properties": Object {
548559
"attr": Object {

src/typescript-to-file-datas.ts

Lines changed: 97 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -122,19 +122,86 @@ export default class typescriptToFileDatas {
122122
* @param {string} file
123123
* @returns {any} {string}
124124
*/
125-
handleFilterFiles(file: string): string {
126-
const res = this.filterFiles.filter((item: string) => file.indexOf(item) > -1);
125+
handleFilterFiles(file: string): { filePath: string, fileType: 'import' | 'npm' } | undefined {
126+
const res = this.filterFiles.filter((item: string) => file === item);
127127
// 过滤不需要匹配的文件
128128
if (res.length) {
129-
return '';
129+
return;
130+
}
131+
132+
// import 文件
133+
if (file === '.' || file === '/') {
134+
return { filePath: 'index', fileType: 'import' };
130135
}
131136

132137
if (file.indexOf('./') > -1 || file.indexOf('../') > -1) {
133-
return file;
138+
return { filePath: file, fileType: 'import' };
134139
}
135140

136-
// 不解析npm包
137-
return '';
141+
// npm包
142+
return { filePath: file, fileType: 'npm' };
143+
}
144+
145+
getTypeFileFromNpm({ sourceValue }: { sourceValue: string }) {
146+
// 1. 优先查找 index.d.ts 其次查找 ${sourceValue}.d.ts
147+
const sourceValueArr = sourceValue.split('/');
148+
const possibleExt: any = sourceValueArr.splice(sourceValueArr.length - 1, 1)[0];
149+
let possibleExtArr: string[] = [];
150+
possibleExt ?
151+
possibleExtArr.push(`${possibleExt}/index.d.ts`, `${possibleExt}.d.ts`) :
152+
possibleExtArr.push('index.d.ts');
153+
possibleExtArr = possibleExtArr.map(item => sourceValueArr ? `${sourceValueArr.join('/')}/${item}` : item);
154+
let source = ''
155+
for (const possibleExt of possibleExtArr) {
156+
const possibleFile = path.resolve(process.cwd(), 'node_modules', possibleExt);
157+
if (fs.existsSync(possibleFile)) {
158+
source = possibleFile;
159+
break;
160+
}
161+
}
162+
// 2. 查找 package.json.typings 或 index.d.ts
163+
if (!source) {
164+
const possibleFile = path.resolve(process.cwd(), 'node_modules', sourceValue, 'package.json');
165+
if (fs.existsSync(possibleFile)) {
166+
const packageJson = require(possibleFile);
167+
const typingFile = packageJson.typings || packageJson.types;
168+
if (typingFile) {
169+
const possibleFile = path.resolve(process.cwd(), 'node_modules', sourceValue, typingFile);
170+
if (fs.existsSync(possibleFile)) source = possibleFile;
171+
} else {
172+
const possibleFile = path.resolve(process.cwd(), 'node_modules', sourceValue, 'index.d.ts');
173+
if (fs.existsSync(possibleFile)) source = possibleFile;
174+
}
175+
}
176+
}
177+
// 3. 查找 @types/xxx
178+
if (!source) {
179+
const possibleFile = path.resolve(process.cwd(), 'node_modules', '@types', sourceValue, 'package.json');
180+
if (fs.existsSync(possibleFile)) {
181+
const packageJson = require(possibleFile);
182+
const typingFile = packageJson.typings || packageJson.types;
183+
if (typingFile) {
184+
const possibleFile = path.resolve(process.cwd(), 'node_modules', '@types', sourceValue, typingFile);
185+
if (fs.existsSync(possibleFile)) source = possibleFile;
186+
}
187+
}
188+
}
189+
// 避免重复处理
190+
this.filterFiles = [...this.filterFiles, sourceValue];
191+
return source
192+
}
193+
194+
getTypeFileFromImport({ dir, sourceValue, ext }: { dir: string; sourceValue: string; ext: string }) {
195+
let source = '';
196+
// 找依赖的文件,优先级 .ts >> .d.ts >> /index.ts >> /index.d.ts
197+
for (const possibleExt of ['', '.d', '/index', '/index.d']) {
198+
const possibleFile = path.resolve(dir, sourceValue + possibleExt + ext);
199+
if (fs.existsSync(possibleFile)) {
200+
source = possibleFile;
201+
break;
202+
}
203+
}
204+
return source;
138205
}
139206

140207
/**
@@ -165,21 +232,16 @@ export default class typescriptToFileDatas {
165232
const sourceValue = _.get(path_, 'node.source.value');
166233
if (!sourceValue) return;
167234

168-
const filePath = _this.handleFilterFiles(sourceValue);
235+
const { filePath, fileType } = _this.handleFilterFiles(sourceValue) || {};
236+
169237
if (!filePath) {
170238
path_.skip();
171239
return;
172240
}
173241

174-
let source = '';
175-
// 找依赖的文件,优先级 .ts >> .d.ts >> /index.ts >> /index.d.ts
176-
for (const possibleExt of ['', '.d', '/index', '/index.d']) {
177-
const possibleFile = path.resolve(dir, sourceValue + possibleExt + ext);
178-
if (fs.existsSync(possibleFile)) {
179-
source = possibleFile;
180-
break;
181-
}
182-
}
242+
const source = fileType === 'import' ?
243+
_this.getTypeFileFromImport({ dir, sourceValue: filePath, ext }) :
244+
_this.getTypeFileFromNpm({ sourceValue: filePath })
183245

184246
if (!source) return;
185247

@@ -378,37 +440,40 @@ export default class typescriptToFileDatas {
378440
const tsTypeName = path.get('id').toString();
379441
const key = namespaces.length ? `${namespaces.join('.')}.${tsTypeName}` : tsTypeName;
380442
const members = path.get('members');
381-
382443
const memberMap = new Map();
444+
let jsonType: string = ''
445+
383446
const enumKeys = members.reduce((accEnumKeys: (number | string)[], member: any) => {
384-
const type = _this.simpleTsTypeTransform(member.get('initializer').type);
447+
const type = _this.simpleTsTypeTransform(member.get('initializer').type) || 'number';
385448
let value = member.get('initializer').toString().replace(/'/g, '');
386449
const prevEnumKey = accEnumKeys.length ? accEnumKeys[accEnumKeys.length - 1] : -1;
450+
const nameObj = member.get('name');
451+
const name = _.get(nameObj, 'container.id.name') || _.get(nameObj, 'container.id.value');
387452

388-
const name = _.get(member.get('name'), 'container.id.name');
453+
if (!jsonType) {
454+
jsonType = type;
455+
} else if (jsonType !== type) {
456+
jsonType = 'any';
457+
}
389458

390459
if (value === '') {
391460
value = (prevEnumKey as number) + 1;
392-
} else {
461+
} else if (type === 'number') {
393462
try {
394463
[...memberMap.keys()].forEach((memberName) => {
395464
value = value.replace(new RegExp(memberName, 'g'), memberMap.get(memberName));
396465
});
397-
// eslint-disable-next-line no-eval
398466
value = eval(value);
399-
} catch (error) {
400-
if (json.type && json.type !== 'string') {
401-
value = NaN;
402-
}
403-
}
467+
} catch (error) {/* do nothing */ }
404468
}
405469
memberMap.set(name, value);
406-
407-
if (!json.type) json.type = type || 'number';
408-
409470
return [...accEnumKeys, value];
410471
}, []);
411472

473+
if (jsonType && jsonType !== 'any') {
474+
json.type = jsonType;
475+
}
476+
412477
json.enum = enumKeys;
413478
if (!json.enum.length) {
414479
(console as Console).warn(
@@ -645,7 +710,7 @@ export default class typescriptToFileDatas {
645710

646711
// 处理any|never|null类型 转换为object类型
647712
if (type === 'any' || type === 'never' || type === 'null') {
648-
if (file && attrKey) {
713+
if (file && attrKey && !file.includes('.d.ts')) {
649714
(console as Console).warn(
650715
chalk.yellow(`警告:%s 文件下的【%s】属性,不推荐定义为【%s】它将会被解析为object类型。`),
651716
file,
@@ -889,10 +954,10 @@ export default class typescriptToFileDatas {
889954
});
890955

891956
if (isEmpty) {
892-
if (file && attrKey) {
957+
if (file && attrKey && !file.includes('.d.ts')) {
893958
(console as Console).warn(
894959
chalk.yellow(
895-
`警告:% s 文件下的【% s】属性,类型定义较复杂,请考虑简化,它将会被解析为object类型。`,
960+
`警告:%s 文件下的【%s】属性,类型定义较复杂,请考虑简化,它将会被解析为object类型。`,
896961
),
897962
file,
898963
attrKey,

0 commit comments

Comments
 (0)