From c4edc9fe1bdee13b06ab1a564d5a83076bd22be6 Mon Sep 17 00:00:00 2001 From: Philippe Elsass Date: Fri, 6 Jul 2018 18:44:04 +0100 Subject: [PATCH] Sanitize type names, and fields, operation parameters. Upgrade Typescript --- package.json | 4 +++- src/gen/js/genOperations.ts | 44 ++++--------------------------------- src/gen/js/genTypes.ts | 13 ++++++++--- src/gen/js/support.ts | 8 ++++--- src/gen/util.ts | 44 +++++++++++++++++++++++++++++++++++++ src/spec/spec.ts | 4 ++-- tsconfig.json | 3 +++ yarn.lock | 28 +++++++++++++++++++---- 8 files changed, 95 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index f16423c..c314119 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,8 @@ "codegen" ], "dependencies": { + "@types/node": "^10.5.2", + "assert": "^1.4.1", "chalk": "^1.1.3", "commander": "^2.9.0", "isomorphic-fetch": "^2.2.1", @@ -61,7 +63,7 @@ "mocha": "^2.5.3", "shx": "^0.1.2", "tslint": "^3.10.2", - "typescript": "^1.8.10", + "typescript": "^2.9.2", "typings": "^1.0.5" } } diff --git a/src/gen/js/genOperations.ts b/src/gen/js/genOperations.ts index c2e4ef2..f6da1d2 100644 --- a/src/gen/js/genOperations.ts +++ b/src/gen/js/genOperations.ts @@ -1,4 +1,4 @@ -import { writeFileSync, join, groupOperationsByGroupName, camelToUppercase, getBestResponse } from '../util' +import { writeFileSync, join, groupOperationsByGroupName, sanitizeIdentifier, isReserved, getBestResponse } from '../util' import { DOC, SP, ST, getDocType, getTSParamType } from './support' export default function genOperations(spec: ApiSpec, operations: ApiOperation[], options: ClientOptions) { @@ -168,45 +168,9 @@ function getParamName(name: string): string { } function escapeReservedWords(name: string): string { - let escapedName = name - - const reservedWords = [ - 'break', - 'case', - 'catch', - 'class', - 'const', - 'continue', - 'debugger', - 'default', - 'delete', - 'do', - 'else', - 'export', - 'extends', - 'finally', - 'for', - 'function', - 'if', - 'import', - 'in', - 'instanceof', - 'new', - 'return', - 'super', - 'switch', - 'this', - 'throw', - 'try', - 'typeof', - 'var', - 'void', - 'while', - 'with', - 'yield' - ] - - if (reservedWords.indexOf(name) >= 0) { + let escapedName = sanitizeIdentifier(name) + + if (isReserved(escapedName)) { escapedName = name + '_' } return escapedName diff --git a/src/gen/js/genTypes.ts b/src/gen/js/genTypes.ts index 38777be..f21efc3 100644 --- a/src/gen/js/genTypes.ts +++ b/src/gen/js/genTypes.ts @@ -1,5 +1,5 @@ -import { writeFileSync, join } from '../util' -import { DOC, SP, ST, getDocType, getTSParamType, formatDocDescription } from './support' +import { writeFileSync, join, sanitizeIdentifier, isReserved } from '../util' +import { DOC, SP, ST, getDocType, getTSParamType } from './support' export default function genTypes(spec: ApiSpec, options: ClientOptions) { const file = genTypesFile(spec, options) @@ -50,6 +50,8 @@ function renderTsType(name, def, options) { return [] } + name = sanitizeIdentifier(name) + const lines = [] if (def.description) { lines.push(`/**`) @@ -89,7 +91,10 @@ function renderTsInheritance(name: string, allOf: any[], options: ClientOptions) } function renderTsTypeProp(prop: string, info: any, required: boolean): string[] { - const lines = [] + + if (isReserved(prop) || !/^[a-z0-9_]+$/i.test(prop)) prop = `'${prop}'` + + const lines = [] const type = getTSParamType(info, true) if (info.description) { lines.push(`${SP}/**`) @@ -255,6 +260,8 @@ function renderTypeDoc(name: string, def: any): string[] { return [] } + name = sanitizeIdentifier(name) + const group = 'types' const lines = [ '/**', diff --git a/src/gen/js/support.ts b/src/gen/js/support.ts index 410cd7c..1f36ce5 100644 --- a/src/gen/js/support.ts +++ b/src/gen/js/support.ts @@ -1,3 +1,5 @@ +import { sanitizeIdentifier } from '../util' + export const DOC = ' * ' export const DEFAULT_SP = ' ' export let SP = DEFAULT_SP @@ -33,8 +35,8 @@ export function getDocType(param: any): string { if (!param) { return 'object' } else if (param.$ref) { - const type = param.$ref.split('/').pop() - return `module:types.${type}` + const type = sanitizeIdentifier(param.$ref.split('/').pop()) + return `module:api.${type}` } else if (param.schema) { return getDocType(param.schema) } else if (param.type === 'array') { @@ -63,7 +65,7 @@ export function getTSParamType(param: any, inTypesModule?: boolean): string { else if (param.type === 'number') return `${param.enum.join(`|`)}` } if (param.$ref) { - const type = param.$ref.split('/').pop() + const type = sanitizeIdentifier(param.$ref.split('/').pop()) return inTypesModule ? type : `api.${type}` diff --git a/src/gen/util.ts b/src/gen/util.ts index 4bc3017..cd76c6a 100644 --- a/src/gen/util.ts +++ b/src/gen/util.ts @@ -32,6 +32,50 @@ export function camelToUppercase(value: string): string { return value.replace(/([A-Z]+)/g, '_$1').toUpperCase() } +export function sanitizeIdentifier(value: string): string { + return value.replace(/[\(\)\[\]\/ .-]/g, '') +} + +export function isReserved(value: string): boolean { + const reservedWords = [ + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'debugger', + 'default', + 'delete', + 'do', + 'else', + 'export', + 'extends', + 'finally', + 'for', + 'function', + 'if', + 'import', + 'in', + 'instanceof', + 'new', + 'return', + 'super', + 'switch', + 'this', + 'throw', + 'try', + 'typeof', + 'var', + 'void', + 'while', + 'with', + 'yield' + ] + + return reservedWords.indexOf(value) >= 0 +} + export function getBestResponse(op: ApiOperation): ApiOperationResponse { const NOT_FOUND = 100000 const lowestCode = op.responses.reduce((code, resp) => { diff --git a/src/spec/spec.ts b/src/spec/spec.ts index 7281775..a629fc3 100644 --- a/src/spec/spec.ts +++ b/src/spec/spec.ts @@ -22,7 +22,7 @@ function loadJson(src: string): Promise { .then(response => response.json()) } else if (String(process) === '[object process]') { return readFile(src) - .then(contents => parseFileContents(contents, src)) + .then(contents => parseFileContents(contents, src)) } else { throw new Error(`Unable to load api at '${src}'`) } @@ -34,7 +34,7 @@ function readFile(filePath: string): Promise { err ? rej(err) : res(contents))) } -function parseFileContents(contents: string, path: string): Object { +function parseFileContents(contents: string, path: string): T { return /.ya?ml$/i.test(path) ? YAML.safeLoad(contents) : JSON.parse(contents) diff --git a/tsconfig.json b/tsconfig.json index 5581437..cf34d75 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,9 @@ "rootDir": "./src", "target": "es6", "module": "commonjs", + "types": [ + "node" + ], "removeComments": false }, "exclude": [ diff --git a/yarn.lock b/yarn.lock index bbeffae..64aaf95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,10 @@ # yarn lockfile v1 +"@types/node@^10.5.2": + version "10.5.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.2.tgz#f19f05314d5421fe37e74153254201a7bf00a707" + abbrev@1: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" @@ -53,6 +57,12 @@ arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" +assert@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -469,6 +479,10 @@ inherits@2, inherits@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" @@ -1203,14 +1217,14 @@ typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@^1.8.10: - version "1.8.10" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-1.8.10.tgz#b475d6e0dff0bf50f296e5ca6ef9fbb5c7320f1e" - typescript@^2.0.3: version "2.1.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.1.1.tgz#41c2b64472f529331b2055c0424862b44ce58d42" +typescript@^2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" + typings-core@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/typings-core/-/typings-core-1.6.1.tgz#ce4b2931ea2f19bb8f3dacbec69983ac4e964a37" @@ -1307,6 +1321,12 @@ util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + uuid@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"