Skip to content

Commit

Permalink
fix(schema): use scule types for runtimeConfig type hints (#23696)
Browse files Browse the repository at this point in the history
  • Loading branch information
luc122c authored and manniL committed Dec 11, 2023
1 parent d6586a4 commit b501a99
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 40 deletions.
1 change: 1 addition & 0 deletions packages/schema/package.json
Expand Up @@ -65,6 +65,7 @@
"hookable": "^5.5.3",
"pathe": "^1.1.1",
"pkg-types": "^1.0.3",
"scule": "^1.1.0",
"std-env": "^3.5.0",
"ufo": "^1.3.1",
"unimport": "^3.5.0",
Expand Down
39 changes: 2 additions & 37 deletions packages/schema/src/types/config.ts
Expand Up @@ -4,50 +4,15 @@ import type { Options as VuePluginOptions } from '@vitejs/plugin-vue'
import type { Options as VueJsxPluginOptions } from '@vitejs/plugin-vue-jsx'
import type { SchemaDefinition } from 'untyped'
import type { NitroRuntimeConfig, NitroRuntimeConfigApp } from 'nitropack'
import type { SnakeCase } from 'scule'
import type { ConfigSchema } from '../../schema/config'
import type { Nuxt } from './nuxt'
import type { AppHeadMetaObject } from './head'
export type { SchemaDefinition } from 'untyped'

type DeepPartial<T> = T extends Function ? T : T extends Record<string, any> ? { [P in keyof T]?: DeepPartial<T[P]> } : T

type ExtractUpperChunk<T extends string> = T extends `${infer A}${infer B}`
? A extends Uppercase<A>
? B extends `${Uppercase<string>}${infer Rest}`
? B extends `${infer C}${Rest}`
? `${A}${C}${ExtractUpperChunk<Rest>}`
: never
: A
: ''
: never

type SliceLast<T extends string> = T extends `${infer A}${infer B}`
? B extends `${infer C}${infer D}`
? D extends ''
? A
: `${A}${C}${SliceLast<D>}`
: ''
: never

type UpperSnakeCase<T extends string, State extends 'start' | 'lower' | 'upper' = 'start'> = T extends `${infer A}${infer B}`
? A extends Uppercase<A>
? A extends `${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0}`
? `${A}${UpperSnakeCase<B, 'lower'>}`
: State extends 'lower' | 'upper'
? B extends `${SliceLast<ExtractUpperChunk<B>>}${infer Rest}`
? SliceLast<ExtractUpperChunk<B>> extends ''
? `_${A}_${UpperSnakeCase<B, 'start'>}`
: `_${A}${SliceLast<ExtractUpperChunk<B>>}_${UpperSnakeCase<Rest, 'start'>}`
: B extends Uppercase<B>
? `_${A}${B}`
: `_${A}${UpperSnakeCase<B, 'lower'>}`
: State extends 'start'
? `${A}${UpperSnakeCase<B, 'lower'>}`
: never
: State extends 'start' | 'lower'
? `${Uppercase<A>}${UpperSnakeCase<B, 'lower'>}`
: `_${Uppercase<A>}${UpperSnakeCase<B, 'lower'>}`
: Uppercase<T>
export type UpperSnakeCase<S extends string> = Uppercase<SnakeCase<S>>

const message = Symbol('message')
export type RuntimeValue<T, B extends string> = T & { [message]?: B }
Expand Down
10 changes: 8 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion test/fixtures/basic-types/types.ts
Expand Up @@ -3,7 +3,7 @@ import type { Ref } from 'vue'
import type { FetchError } from 'ofetch'
import type { NavigationFailure, RouteLocationNormalized, RouteLocationRaw, Router, useRouter as vueUseRouter } from '#vue-router'

import type { AppConfig, RuntimeValue } from 'nuxt/schema'
import type { AppConfig, RuntimeValue, UpperSnakeCase } from 'nuxt/schema'
import { defineNuxtConfig } from 'nuxt/config'
import { callWithNuxt, isVue3 } from '#app'
import type { NavigateToOptions } from '#app/composables/router'
Expand Down Expand Up @@ -271,6 +271,21 @@ describe('runtimeConfig', () => {
expectTypeOf(val.runtimeConfig!.public!.ids).toEqualTypeOf<undefined | RuntimeValue<Array<number>, 'You can override this value at runtime with NUXT_PUBLIC_IDS'>>()
expectTypeOf(val.runtimeConfig!.unknown).toEqualTypeOf<unknown>()
})

it('correctly converts different kinds of names to snake case', () => {
expectTypeOf<UpperSnakeCase<'testAppName'>>().toEqualTypeOf<'TEST_APP_NAME'>()
expectTypeOf<UpperSnakeCase<'TEST_APP_NAME'>>().toEqualTypeOf<'TEST_APP_NAME'>()
expectTypeOf<UpperSnakeCase<'test_APP_NAME'>>().toEqualTypeOf<'TEST_APP_NAME'>()
expectTypeOf<UpperSnakeCase<'test_app_NAME'>>().toEqualTypeOf<'TEST_APP_NAME'>()
expectTypeOf<UpperSnakeCase<'testAppNAME'>>().toEqualTypeOf<'TEST_APP_NAME'>()
expectTypeOf<UpperSnakeCase<'testApp123NAME'>>().toEqualTypeOf<'TEST_APP123NAME'>()
expectTypeOf<UpperSnakeCase<'testAPPName'>>().toEqualTypeOf<'TEST_APP_NAME'>()
expectTypeOf<UpperSnakeCase<'testAPP_Name'>>().toEqualTypeOf<'TEST_APP_NAME'>()
expectTypeOf<UpperSnakeCase<'test_APP_Name'>>().toEqualTypeOf<'TEST_APP_NAME'>()
expectTypeOf<UpperSnakeCase<'TESTAppName'>>().toEqualTypeOf<'TEST_APP_NAME'>()
expectTypeOf<UpperSnakeCase<'t'>>().toEqualTypeOf<'T'>()
expectTypeOf<UpperSnakeCase<'T'>>().toEqualTypeOf<'T'>()
})
})

describe('head', () => {
Expand Down

0 comments on commit b501a99

Please sign in to comment.