fix(schema): use scule types for runtimeConfig type hints (#23696)

This commit is contained in:
Luke Nelson 2023-11-15 18:58:00 +00:00 committed by GitHub
parent c069239b12
commit 6ec267be87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 40 deletions

View File

@ -65,6 +65,7 @@
"hookable": "^5.5.3", "hookable": "^5.5.3",
"pathe": "^1.1.1", "pathe": "^1.1.1",
"pkg-types": "^1.0.3", "pkg-types": "^1.0.3",
"scule": "^1.1.0",
"std-env": "^3.5.0", "std-env": "^3.5.0",
"ufo": "^1.3.1", "ufo": "^1.3.1",
"unimport": "^3.5.0", "unimport": "^3.5.0",

View File

@ -4,6 +4,7 @@ import type { Options as VuePluginOptions } from '@vitejs/plugin-vue'
import type { Options as VueJsxPluginOptions } from '@vitejs/plugin-vue-jsx' import type { Options as VueJsxPluginOptions } from '@vitejs/plugin-vue-jsx'
import type { SchemaDefinition } from 'untyped' import type { SchemaDefinition } from 'untyped'
import type { NitroRuntimeConfig, NitroRuntimeConfigApp } from 'nitropack' import type { NitroRuntimeConfig, NitroRuntimeConfigApp } from 'nitropack'
import type { SnakeCase } from 'scule'
import type { ConfigSchema } from '../../schema/config' import type { ConfigSchema } from '../../schema/config'
import type { Nuxt } from './nuxt' import type { Nuxt } from './nuxt'
import type { AppHeadMetaObject } from './head' import type { AppHeadMetaObject } from './head'
@ -11,43 +12,7 @@ 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 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}` export type UpperSnakeCase<S extends string> = Uppercase<SnakeCase<S>>
? 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>
const message = Symbol('message') const message = Symbol('message')
export type RuntimeValue<T, B extends string> = T & { [message]?: B } export type RuntimeValue<T, B extends string> = T & { [message]?: B }

View File

@ -443,6 +443,9 @@ importers:
pkg-types: pkg-types:
specifier: ^1.0.3 specifier: ^1.0.3
version: 1.0.3 version: 1.0.3
scule:
specifier: ^1.1.0
version: 1.1.0
std-env: std-env:
specifier: ^3.5.0 specifier: ^3.5.0
version: 3.5.0 version: 3.5.0
@ -3768,7 +3771,7 @@ packages:
open: 9.1.0 open: 9.1.0
pathe: 1.1.1 pathe: 1.1.1
pkg-types: 1.0.3 pkg-types: 1.0.3
scule: 1.0.0 scule: 1.1.0
semver: 7.5.4 semver: 7.5.4
std-env: 3.5.0 std-env: 3.5.0
yaml: 2.3.3 yaml: 2.3.3
@ -8194,6 +8197,9 @@ packages:
/scule@1.0.0: /scule@1.0.0:
resolution: {integrity: sha512-4AsO/FrViE/iDNEPaAQlb77tf0csuq27EsVpy6ett584EcRTp6pTDLoGWVxCD77y5iU5FauOvhsI4o1APwPoSQ==} resolution: {integrity: sha512-4AsO/FrViE/iDNEPaAQlb77tf0csuq27EsVpy6ett584EcRTp6pTDLoGWVxCD77y5iU5FauOvhsI4o1APwPoSQ==}
/scule@1.1.0:
resolution: {integrity: sha512-vRUjqhyM/YWYzT+jsMk6tnl3NkY4A4soJ8uyh3O6Um+JXEQL9ozUCe7pqrxn3CSKokw0hw3nFStfskzpgYwR0g==}
/semver@5.7.2: /semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true hasBin: true
@ -8973,7 +8979,7 @@ packages:
mlly: 1.4.2 mlly: 1.4.2
pathe: 1.1.1 pathe: 1.1.1
pkg-types: 1.0.3 pkg-types: 1.0.3
scule: 1.0.0 scule: 1.1.0
strip-literal: 1.3.0 strip-literal: 1.3.0
unplugin: 1.5.0 unplugin: 1.5.0
transitivePeerDependencies: transitivePeerDependencies:

View File

@ -3,7 +3,7 @@ import type { Ref } from 'vue'
import type { FetchError } from 'ofetch' import type { FetchError } from 'ofetch'
import type { NavigationFailure, RouteLocationNormalized, RouteLocationRaw, Router, useRouter as vueUseRouter } from '#vue-router' 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 { defineNuxtConfig } from 'nuxt/config'
import { callWithNuxt, isVue3 } from '#app' import { callWithNuxt, isVue3 } from '#app'
import type { NavigateToOptions } from '#app/composables/router' import type { NavigateToOptions } from '#app/composables/router'
@ -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!.public!.ids).toEqualTypeOf<undefined | RuntimeValue<Array<number>, 'You can override this value at runtime with NUXT_PUBLIC_IDS'>>()
expectTypeOf(val.runtimeConfig!.unknown).toEqualTypeOf<unknown>() 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', () => { describe('head', () => {