diff --git a/.vscode/launch.json b/.vscode/launch.json index edbd4ea4b..e83d654b7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,8 +15,7 @@ "skipFiles": ["/**"], "cwd": "${workspaceFolder}/dev", "runtimeExecutable": "node", - "runtimeArgs": ["-r", "ts-node/register/transpile-only"], - "program": "${workspaceFolder}/packages/openapi-ts/src/run.ts", + "program": "${workspaceFolder}/packages/openapi-ts/dist/run.mjs", "env": { "DEBUG": "false" } diff --git a/dev/openapi-ts.config.ts b/dev/openapi-ts.config.ts index a32f38a5d..2bd71dca0 100644 --- a/dev/openapi-ts.config.ts +++ b/dev/openapi-ts.config.ts @@ -5,13 +5,16 @@ import path from 'node:path'; // @ts-ignore import { customClientPlugin } from '@hey-api/custom-client/plugin'; // @ts-ignore -import { defineConfig, utils } from '@hey-api/openapi-ts'; +import { defineConfig, reserved, utils } from '@hey-api/openapi-ts'; // @ts-ignore import { myClientPlugin } from '../packages/openapi-ts-tests/main/test/custom/client/plugin'; // @ts-ignore import { getSpecsPath } from '../packages/openapi-ts-tests/utils'; +reserved.runtime.set((list) => [...list, 'Agent']); +reserved.type.set((list) => [...list, 'Agent']); + // @ts-ignore export default defineConfig(() => { // ... diff --git a/packages/openapi-ts/src/ts-dsl/index.ts b/packages/openapi-ts/src/ts-dsl/index.ts index fa8f7b874..201729a9b 100644 --- a/packages/openapi-ts/src/ts-dsl/index.ts +++ b/packages/openapi-ts/src/ts-dsl/index.ts @@ -350,4 +350,6 @@ export type DollarTsDsl = { export type { MaybeTsDsl, TypeTsDsl } from './base'; export { TsDsl } from './base'; export { TypeScriptRenderer } from './render/typescript'; +export { keywords } from './utils/keywords'; export { regexp } from './utils/regexp'; +export { reserved } from './utils/reserved'; diff --git a/packages/openapi-ts/src/ts-dsl/utils/keywords.ts b/packages/openapi-ts/src/ts-dsl/utils/keywords.ts new file mode 100644 index 000000000..4b0e2b926 --- /dev/null +++ b/packages/openapi-ts/src/ts-dsl/utils/keywords.ts @@ -0,0 +1,104 @@ +const browserGlobals = [ + 'document', + 'history', + 'location', + 'navigator', + 'window', +]; + +const javaScriptGlobals = [ + 'console', + 'Array', + 'Date', + 'Error', + 'Function', + 'JSON', + 'Map', + 'Math', + 'Object', + 'Promise', + 'RegExp', + 'Set', + 'WeakMap', + 'WeakSet', +]; + +const javaScriptKeywords = [ + 'arguments', + 'async', + 'await', + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'debugger', + 'default', + 'delete', + 'do', + 'else', + 'enum', + 'eval', + 'export', + 'extends', + 'false', + 'finally', + 'for', + 'from', + 'function', + 'if', + 'implements', + 'import', + 'in', + 'instanceof', + 'interface', + 'let', + 'new', + 'null', + 'package', + 'private', + 'protected', + 'public', + 'return', + 'static', + 'super', + 'switch', + 'this', + 'throw', + 'true', + 'try', + 'typeof', + 'var', + 'void', + 'while', + 'with', + 'yield', +]; + +const nodeGlobals = ['global', 'process', 'Buffer']; + +const typeScriptKeywords = [ + 'any', + 'as', + 'bigint', + 'boolean', + 'namespace', + 'never', + 'null', + 'number', + 'string', + 'symbol', + 'type', + 'undefined', + 'unknown', + 'void', +]; + +export const keywords = { + browserGlobals, + javaScriptGlobals, + javaScriptKeywords, + nodeGlobals, + typeScriptKeywords, +}; diff --git a/packages/openapi-ts/src/ts-dsl/utils/name.ts b/packages/openapi-ts/src/ts-dsl/utils/name.ts index 205524995..ce4a7045c 100644 --- a/packages/openapi-ts/src/ts-dsl/utils/name.ts +++ b/packages/openapi-ts/src/ts-dsl/utils/name.ts @@ -4,13 +4,8 @@ import type { TsDsl } from '../base'; import { IdTsDsl } from '../expr/id'; import { LiteralTsDsl } from '../expr/literal'; import { regexp } from './regexp'; -import { - browserGlobals, - javaScriptGlobals, - javaScriptKeywords, - nodeGlobals, - typeScriptKeywords, -} from './reserved'; +import type { ReservedList } from './reserved'; +import { reserved } from './reserved'; export const safeMemberName = (name: string): TsDsl => { regexp.typeScriptIdentifier.lastIndex = 0; @@ -36,7 +31,7 @@ export const safePropName = (name: string): TsDsl => { return new LiteralTsDsl(name) as TsDsl; }; -const sanitizeName = (name: string, reserved: Set): string => { +const safeName = (name: string, reserved: ReservedList): string => { let sanitized = ''; let index: number; @@ -56,23 +51,14 @@ const sanitizeName = (name: string, reserved: Set): string => { index += 1; } - if (reserved.has(sanitized)) { + if (reserved['~values'].has(sanitized)) { sanitized = `_${sanitized}`; } return sanitized || '_'; }; -const runtimeReserved = new Set([ - ...browserGlobals, - ...javaScriptGlobals, - ...javaScriptKeywords, - ...nodeGlobals, - ...typeScriptKeywords, -]); - -const typeReserved = new Set([...javaScriptKeywords, ...typeScriptKeywords]); - export const safeRuntimeName = (name: string) => - sanitizeName(name, runtimeReserved); -export const safeTypeName = (name: string) => sanitizeName(name, typeReserved); + safeName(name, reserved.runtime); + +export const safeTypeName = (name: string) => safeName(name, reserved.type); diff --git a/packages/openapi-ts/src/ts-dsl/utils/reserved.ts b/packages/openapi-ts/src/ts-dsl/utils/reserved.ts index 6020eb2c2..751f9e13d 100644 --- a/packages/openapi-ts/src/ts-dsl/utils/reserved.ts +++ b/packages/openapi-ts/src/ts-dsl/utils/reserved.ts @@ -1,96 +1,60 @@ -export const browserGlobals = [ - 'document', - 'history', - 'location', - 'navigator', - 'window', -]; +import { keywords } from './keywords'; -export const javaScriptGlobals = [ - 'console', - 'Array', - 'Date', - 'Error', - 'Function', - 'JSON', - 'Map', - 'Math', - 'Object', - 'Promise', - 'RegExp', - 'Set', - 'WeakMap', - 'WeakSet', -]; +type List = ReadonlyArray; -export const javaScriptKeywords = [ - 'arguments', - 'async', - 'await', - 'break', - 'case', - 'catch', - 'class', - 'const', - 'continue', - 'debugger', - 'default', - 'delete', - 'do', - 'else', - 'enum', - 'eval', - 'export', - 'extends', - 'false', - 'finally', - 'for', - 'from', - 'function', - 'if', - 'implements', - 'import', - 'in', - 'instanceof', - 'interface', - 'let', - 'new', - 'null', - 'package', - 'private', - 'protected', - 'public', - 'return', - 'static', - 'super', - 'switch', - 'this', - 'throw', - 'true', - 'try', - 'typeof', - 'var', - 'void', - 'while', - 'with', - 'yield', -]; +export class ReservedList { + private _array: List; + private _set: Set; -export const nodeGlobals = ['global', 'process', 'Buffer']; + constructor(values: List) { + this._array = values; + this._set = new Set(values); + } -export const typeScriptKeywords = [ - 'any', - 'as', - 'bigint', - 'boolean', - 'namespace', - 'never', - 'null', - 'number', - 'string', - 'symbol', - 'type', - 'undefined', - 'unknown', - 'void', -]; + get '~values'() { + return this._set; + } + + /** + * Updates the reserved list with new values. + * + * @param values New reserved values or a function that receives the previous + * reserved values and returns the new ones. + */ + set(values: List | ((prev: List) => List)): void { + const vals = typeof values === 'function' ? values(this._array) : values; + this._array = vals; + this._set = new Set(vals); + } +} + +const runtimeReserved = new ReservedList([ + ...keywords.browserGlobals, + ...keywords.javaScriptGlobals, + ...keywords.javaScriptKeywords, + ...keywords.nodeGlobals, + ...keywords.typeScriptKeywords, +]); + +const typeReserved = new ReservedList([ + ...keywords.javaScriptKeywords, + ...keywords.typeScriptKeywords, +]); + +/** + * Reserved names for identifiers. These names will not be used + * for variables, functions, classes, or other identifiers in generated code. + */ +export const reserved = { + /** + * Reserved names for runtime identifiers. These names will not be used + * for variables, functions, classes, or other runtime identifiers in + * generated code. + */ + runtime: runtimeReserved, + /** + * Reserved names for type identifiers. These names will not be used + * for type or interface identifiers in generated code. + */ + type: typeReserved, +};