11import { QueryResult } from '../../driver/database-connection.js'
2- import { IdentifierNode } from '../../operation-node/identifier-node.js'
3- import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js'
42import { RootOperationNode } from '../../query-compiler/query-compiler.js'
53import { isObject } from '../../util/object-utils.js'
64import { UnknownRow } from '../../util/type-utils.js'
@@ -9,6 +7,12 @@ import {
97 PluginTransformQueryArgs ,
108 PluginTransformResultArgs ,
119} from '../kysely-plugin.js'
10+ import { SnakeCaseTransformer } from './camel-case-transformer.js'
11+ import {
12+ createCamelCaseMapper ,
13+ createSnakeCaseMapper ,
14+ StringMapper ,
15+ } from './camel-case.js'
1216
1317export interface CamelCasePluginOptions {
1418 /**
@@ -108,8 +112,8 @@ export class CamelCasePlugin implements KyselyPlugin {
108112 readonly #snakeCaseTransformer: SnakeCaseTransformer
109113
110114 constructor ( opt : CamelCasePluginOptions = { } ) {
111- this . #camelCase = createCamelCase ( opt )
112- this . #snakeCase = createSnakeCase ( opt )
115+ this . #camelCase = createCamelCaseMapper ( opt )
116+ this . #snakeCase = createSnakeCaseMapper ( opt )
113117
114118 this . #snakeCaseTransformer = new SnakeCaseTransformer (
115119 this . snakeCase . bind ( this )
@@ -157,149 +161,6 @@ export class CamelCasePlugin implements KyselyPlugin {
157161 }
158162}
159163
160- type StringMapper = ( str : string ) => string
161-
162- class SnakeCaseTransformer extends OperationNodeTransformer {
163- readonly #snakeCase: StringMapper
164-
165- constructor ( snakeCase : StringMapper ) {
166- super ( )
167- this . #snakeCase = snakeCase
168- }
169-
170- protected override transformIdentifier ( node : IdentifierNode ) : IdentifierNode {
171- node = super . transformIdentifier ( node )
172-
173- return {
174- ...node ,
175- identifier : this . #snakeCase( node . identifier ) ,
176- }
177- }
178- }
179-
180- function canMap ( obj : any ) : obj is Record < string , any > {
164+ function canMap ( obj : unknown ) : obj is Record < string , unknown > {
181165 return isObject ( obj ) && ! ( obj instanceof Date ) && ! Buffer . isBuffer ( obj )
182166}
183-
184- function createSnakeCase ( {
185- upperCase = false ,
186- underscoreBeforeDigits = false ,
187- underscoreBetweenUppercaseLetters = false ,
188- } = { } ) : StringMapper {
189- return memoize ( ( str : string ) : string => {
190- if ( str . length === 0 ) {
191- return str
192- }
193-
194- const upper = str . toUpperCase ( )
195- const lower = str . toLowerCase ( )
196-
197- let out = lower [ 0 ]
198-
199- for ( let i = 1 , l = str . length ; i < l ; ++ i ) {
200- const char = str [ i ]
201- const prevChar = str [ i - 1 ]
202-
203- const upperChar = upper [ i ]
204- const prevUpperChar = upper [ i - 1 ]
205-
206- const lowerChar = lower [ i ]
207- const prevLowerChar = lower [ i - 1 ]
208-
209- // If underScoreBeforeDigits is true then, well, insert an underscore
210- // before digits :). Only the first digit gets an underscore if
211- // there are multiple.
212- if ( underscoreBeforeDigits && isDigit ( char ) && ! isDigit ( prevChar ) ) {
213- out += '_' + char
214- continue
215- }
216-
217- // Test if `char` is an upper-case character and that the character
218- // actually has different upper and lower case versions.
219- if ( char === upperChar && upperChar !== lowerChar ) {
220- const prevCharacterIsUppercase =
221- prevChar === prevUpperChar && prevUpperChar !== prevLowerChar
222-
223- // If underscoreBetweenUppercaseLetters is true, we always place an underscore
224- // before consecutive uppercase letters (e.g. "fooBAR" becomes "foo_b_a_r").
225- // Otherwise, we don't (e.g. "fooBAR" becomes "foo_bar").
226- if ( underscoreBetweenUppercaseLetters || ! prevCharacterIsUppercase ) {
227- out += '_' + lowerChar
228- } else {
229- out += lowerChar
230- }
231- } else {
232- out += char
233- }
234- }
235-
236- if ( upperCase ) {
237- return out . toUpperCase ( )
238- } else {
239- return out
240- }
241- } )
242- }
243-
244- function createCamelCase ( { upperCase = false } = { } ) : StringMapper {
245- return memoize ( ( str : string ) : string => {
246- if ( str . length === 0 ) {
247- return str
248- }
249-
250- if ( upperCase && isAllUpperCaseSnakeCase ( str ) ) {
251- // Only convert to lower case if the string is all upper
252- // case snake_case. This allows camelCase strings to go
253- // through without changing.
254- str = str . toLowerCase ( )
255- }
256-
257- let out = str [ 0 ]
258-
259- for ( let i = 1 , l = str . length ; i < l ; ++ i ) {
260- const char = str [ i ]
261- const prevChar = str [ i - 1 ]
262-
263- if ( char !== '_' ) {
264- if ( prevChar === '_' ) {
265- out += char . toUpperCase ( )
266- } else {
267- out += char
268- }
269- }
270- }
271-
272- return out
273- } )
274- }
275-
276- function isAllUpperCaseSnakeCase ( str : string ) : boolean {
277- for ( let i = 1 , l = str . length ; i < l ; ++ i ) {
278- const char = str [ i ]
279-
280- if ( char !== '_' && char !== char . toUpperCase ( ) ) {
281- return false
282- }
283- }
284-
285- return true
286- }
287-
288- function isDigit ( char : string ) : boolean {
289- return char >= '0' && char <= '9'
290- }
291-
292- function memoize ( func : StringMapper ) : StringMapper {
293- const cache = new Map < string , string > ( )
294-
295- return ( str : string ) => {
296- let mapped = cache . get ( str )
297-
298- if ( ! mapped ) {
299- mapped = func ( str )
300- cache . set ( str , mapped )
301- }
302-
303- return mapped
304- }
305- }
0 commit comments