@@ -19,7 +19,11 @@ export async function loadEnvFile(projectRoot: string): Promise<void> {
1919 }
2020}
2121
22- export function resolveEnvVars ( value : string , env : Record < string , string > = { } ) : string {
22+ export function resolveEnvVars (
23+ value : string ,
24+ env : Record < string , string > = { } ,
25+ unresolved ?: Set < string >
26+ ) : string {
2327 const mergedEnv = { ...process . env , ...env } ;
2428 const MAX_DEPTH = 10 ;
2529
@@ -29,7 +33,11 @@ export function resolveEnvVars(value: string, env: Record<string, string> = {}):
2933 result = result . replace ( / \$ \{ ( [ ^ } ] + ) \} / g, ( match , varName ) => {
3034 const resolved = mergedEnv [ varName ] ;
3135 if ( resolved === undefined ) {
32- logger . warn ( `Environment variable ${ varName } is not set` ) ;
36+ if ( unresolved ) {
37+ unresolved . add ( varName ) ;
38+ } else {
39+ logger . warn ( `Environment variable ${ varName } is not set` ) ;
40+ }
3341 return match ;
3442 }
3543 return resolved ;
@@ -39,6 +47,34 @@ export function resolveEnvVars(value: string, env: Record<string, string> = {}):
3947 return result ;
4048}
4149
50+ /**
51+ * Walk a raw (pre-validation) config object and return the names of all
52+ * `${VAR}` references that cannot be resolved from process.env or the config's
53+ * own `env` block. Used by `envkit validate --strict`.
54+ */
55+ export function findUnresolvedVars ( rawConfig : any ) : string [ ] {
56+ const unresolved = new Set < string > ( ) ;
57+ const localEnv : Record < string , string > = { } ;
58+ if ( rawConfig && typeof rawConfig === 'object' && rawConfig . env && typeof rawConfig . env === 'object' ) {
59+ for ( const [ k , v ] of Object . entries ( rawConfig . env ) ) {
60+ if ( typeof v === 'string' ) localEnv [ k ] = v ;
61+ }
62+ }
63+
64+ const walk = ( obj : any ) : void => {
65+ if ( typeof obj === 'string' ) {
66+ resolveEnvVars ( obj , localEnv , unresolved ) ;
67+ } else if ( Array . isArray ( obj ) ) {
68+ obj . forEach ( walk ) ;
69+ } else if ( obj !== null && typeof obj === 'object' ) {
70+ Object . values ( obj ) . forEach ( walk ) ;
71+ }
72+ } ;
73+ walk ( rawConfig ) ;
74+
75+ return [ ...unresolved ] ;
76+ }
77+
4278export async function loadConfig ( projectRoot : string = process . cwd ( ) ) : Promise < DevEnvConfig > {
4379 const configPath = path . join ( projectRoot , '.dev-env.yml' ) ;
4480
0 commit comments