@idealyst/config 1.2.12 → 1.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -87
- package/bin/idealyst-config.js +169 -64
- package/package.json +1 -1
- package/src/cli/generate.ts +140 -34
- package/src/config.web.ts +55 -17
- package/src/index.web.ts +12 -7
- package/src/vite-env.d.ts +0 -22
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# @idealyst/config
|
|
2
2
|
|
|
3
|
-
Cross-platform configuration
|
|
3
|
+
Cross-platform configuration for React and React Native with env inheritance support.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Single API** - Same code works on web and native
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
8
|
+
- **Env inheritance** - Shared config with platform-specific overrides
|
|
9
|
+
- **Type-safe** - Auto-generated TypeScript declarations
|
|
10
|
+
- **Bundler agnostic** - No bundler configuration needed
|
|
11
|
+
- **Monorepo friendly** - Designed for shared/web/mobile patterns
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
@@ -22,89 +22,124 @@ cd ios && pod install
|
|
|
22
22
|
|
|
23
23
|
## Quick Start
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
import { config } from '@idealyst/config'
|
|
25
|
+
### 1. Create your .env files
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
```
|
|
28
|
+
my-app/
|
|
29
|
+
├── packages/
|
|
30
|
+
│ ├── shared/
|
|
31
|
+
│ │ └── .env # Base config (lowest priority)
|
|
32
|
+
│ ├── web/
|
|
33
|
+
│ │ └── .env # Web overrides
|
|
34
|
+
│ └── mobile/
|
|
35
|
+
│ └── .env # Mobile overrides
|
|
36
|
+
```
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
**shared/.env:**
|
|
39
|
+
```bash
|
|
40
|
+
API_URL=https://api.example.com
|
|
41
|
+
GOOGLE_CLIENT_ID=abc123
|
|
42
|
+
ANALYTICS_ENABLED=true
|
|
43
|
+
```
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
**web/.env:**
|
|
46
|
+
```bash
|
|
47
|
+
# Override API for web
|
|
48
|
+
API_URL=https://web-api.example.com
|
|
39
49
|
```
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
**mobile/.env:**
|
|
52
|
+
```bash
|
|
53
|
+
# Mobile uses shared API_URL, but different analytics
|
|
54
|
+
ANALYTICS_ENABLED=false
|
|
55
|
+
```
|
|
42
56
|
|
|
43
|
-
###
|
|
57
|
+
### 2. Generate config
|
|
44
58
|
|
|
45
59
|
```bash
|
|
46
|
-
#
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
# In packages/web/
|
|
61
|
+
npx idealyst-config generate --extends ../shared/.env --env .env
|
|
62
|
+
|
|
63
|
+
# In packages/mobile/
|
|
64
|
+
npx idealyst-config generate --extends ../shared/.env --env .env
|
|
50
65
|
```
|
|
51
66
|
|
|
52
|
-
|
|
67
|
+
This creates `src/config.generated.ts` with merged values:
|
|
53
68
|
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
```typescript
|
|
70
|
+
// Web: API_URL overridden, others inherited
|
|
71
|
+
export const generatedConfig = {
|
|
72
|
+
API_URL: "https://web-api.example.com",
|
|
73
|
+
GOOGLE_CLIENT_ID: "abc123",
|
|
74
|
+
ANALYTICS_ENABLED: "true"
|
|
75
|
+
}
|
|
59
76
|
```
|
|
60
77
|
|
|
61
|
-
|
|
78
|
+
### 3. Initialize and use
|
|
62
79
|
|
|
63
80
|
```typescript
|
|
64
|
-
//
|
|
65
|
-
|
|
81
|
+
// In your app entry point (e.g., App.tsx, main.tsx)
|
|
82
|
+
import { setConfig } from '@idealyst/config'
|
|
83
|
+
import { generatedConfig } from './config.generated'
|
|
84
|
+
|
|
85
|
+
setConfig(generatedConfig)
|
|
66
86
|
```
|
|
67
87
|
|
|
68
|
-
|
|
88
|
+
```typescript
|
|
89
|
+
// Anywhere in your app
|
|
90
|
+
import { config } from '@idealyst/config'
|
|
69
91
|
|
|
70
|
-
|
|
92
|
+
const apiUrl = config.get('API_URL')
|
|
93
|
+
const analyticsEnabled = config.get('ANALYTICS_ENABLED') === 'true'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## CLI Reference
|
|
71
97
|
|
|
72
98
|
```bash
|
|
73
|
-
|
|
74
|
-
|
|
99
|
+
idealyst-config generate [options]
|
|
100
|
+
|
|
101
|
+
Options:
|
|
102
|
+
--env <path> Path to .env file (default: auto-detect)
|
|
103
|
+
--extends <path> Inherit from another .env (can use multiple times)
|
|
104
|
+
--output <path> Output path (default: src/config.generated.ts)
|
|
105
|
+
--types-only Generate only .d.ts file, no values
|
|
106
|
+
--help Show help
|
|
107
|
+
```
|
|
75
108
|
|
|
76
|
-
|
|
77
|
-
npx idealyst-config generate --env .env.local
|
|
109
|
+
### Examples
|
|
78
110
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
111
|
+
```bash
|
|
112
|
+
# Simple - auto-detect .env
|
|
113
|
+
idealyst-config generate
|
|
82
114
|
|
|
83
|
-
|
|
115
|
+
# With shared inheritance
|
|
116
|
+
idealyst-config generate --extends ../shared/.env --env .env
|
|
84
117
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
118
|
+
# Multiple inheritance (lowest to highest priority)
|
|
119
|
+
idealyst-config generate \
|
|
120
|
+
--extends ../../shared/.env \
|
|
121
|
+
--extends ../.env.common \
|
|
122
|
+
--env .env
|
|
123
|
+
|
|
124
|
+
# Types only (for CI/type checking without values)
|
|
125
|
+
idealyst-config generate --types-only --output src/env.d.ts
|
|
94
126
|
```
|
|
95
127
|
|
|
96
|
-
|
|
128
|
+
## Inheritance Priority
|
|
129
|
+
|
|
130
|
+
Configs are merged in order, with later files overriding earlier ones:
|
|
97
131
|
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
|
|
132
|
+
```
|
|
133
|
+
1. --extends ../shared/.env (lowest priority)
|
|
134
|
+
2. --extends ../.env.common
|
|
135
|
+
3. --env .env (highest priority)
|
|
101
136
|
```
|
|
102
137
|
|
|
103
138
|
## API Reference
|
|
104
139
|
|
|
105
140
|
### `config.get(key: string): string | undefined`
|
|
106
141
|
|
|
107
|
-
Get a
|
|
142
|
+
Get a config value.
|
|
108
143
|
|
|
109
144
|
```typescript
|
|
110
145
|
const apiUrl = config.get('API_URL')
|
|
@@ -112,7 +147,7 @@ const apiUrl = config.get('API_URL')
|
|
|
112
147
|
|
|
113
148
|
### `config.get(key: string, defaultValue: string): string`
|
|
114
149
|
|
|
115
|
-
Get
|
|
150
|
+
Get with fallback default.
|
|
116
151
|
|
|
117
152
|
```typescript
|
|
118
153
|
const port = config.get('PORT', '3000')
|
|
@@ -120,16 +155,15 @@ const port = config.get('PORT', '3000')
|
|
|
120
155
|
|
|
121
156
|
### `config.getRequired(key: string): string`
|
|
122
157
|
|
|
123
|
-
Get
|
|
158
|
+
Get required value. Throws if not defined.
|
|
124
159
|
|
|
125
160
|
```typescript
|
|
126
161
|
const secret = config.getRequired('JWT_SECRET')
|
|
127
|
-
// Throws: 'Required config key "JWT_SECRET" is not defined'
|
|
128
162
|
```
|
|
129
163
|
|
|
130
164
|
### `config.has(key: string): boolean`
|
|
131
165
|
|
|
132
|
-
Check if
|
|
166
|
+
Check if key exists.
|
|
133
167
|
|
|
134
168
|
```typescript
|
|
135
169
|
if (config.has('DEBUG')) {
|
|
@@ -137,54 +171,77 @@ if (config.has('DEBUG')) {
|
|
|
137
171
|
}
|
|
138
172
|
```
|
|
139
173
|
|
|
140
|
-
### `config.
|
|
174
|
+
### `config.validate(requiredKeys: string[]): void`
|
|
141
175
|
|
|
142
|
-
|
|
176
|
+
Validate required keys at startup.
|
|
143
177
|
|
|
144
178
|
```typescript
|
|
145
|
-
|
|
179
|
+
config.validate(['API_URL', 'AUTH_SECRET'])
|
|
180
|
+
// Throws ConfigValidationError if any are missing
|
|
146
181
|
```
|
|
147
182
|
|
|
148
|
-
### `config
|
|
183
|
+
### `setConfig(config: Record<string, string>)`
|
|
149
184
|
|
|
150
|
-
|
|
185
|
+
Initialize config values (call once at app startup).
|
|
151
186
|
|
|
152
187
|
```typescript
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (error instanceof ConfigValidationError) {
|
|
158
|
-
console.error('Missing config:', error.missingKeys)
|
|
159
|
-
}
|
|
160
|
-
}
|
|
188
|
+
import { setConfig } from '@idealyst/config'
|
|
189
|
+
import { generatedConfig } from './config.generated'
|
|
190
|
+
|
|
191
|
+
setConfig(generatedConfig)
|
|
161
192
|
```
|
|
162
193
|
|
|
163
|
-
##
|
|
194
|
+
## React Native Setup
|
|
164
195
|
|
|
165
|
-
|
|
196
|
+
1. Install react-native-config:
|
|
197
|
+
```bash
|
|
198
|
+
npm install react-native-config
|
|
199
|
+
cd ios && pod install
|
|
200
|
+
```
|
|
166
201
|
|
|
167
|
-
|
|
168
|
-
- Your code: `config.get('API_URL')`
|
|
169
|
-
- Internal lookup: `import.meta.env.VITE_API_URL`
|
|
202
|
+
2. Follow [react-native-config setup](https://github.com/luggit/react-native-config#setup)
|
|
170
203
|
|
|
171
|
-
|
|
204
|
+
3. Generate and use the same way as web:
|
|
205
|
+
```bash
|
|
206
|
+
idealyst-config generate --extends ../shared/.env --env .env
|
|
207
|
+
```
|
|
172
208
|
|
|
173
|
-
|
|
174
|
-
- Your code: `config.get('API_URL')`
|
|
175
|
-
- Internal lookup: `Config.API_URL`
|
|
209
|
+
## Type Safety
|
|
176
210
|
|
|
177
|
-
|
|
211
|
+
The CLI generates TypeScript declarations for autocomplete:
|
|
178
212
|
|
|
179
|
-
|
|
213
|
+
```typescript
|
|
214
|
+
// Generated: src/config.generated.d.ts
|
|
215
|
+
declare module '@idealyst/config' {
|
|
216
|
+
interface ConfigKeys {
|
|
217
|
+
API_URL: string
|
|
218
|
+
GOOGLE_CLIENT_ID: string
|
|
219
|
+
ANALYTICS_ENABLED: string
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
This provides autocomplete and catches typos at compile time.
|
|
225
|
+
|
|
226
|
+
## Build Integration
|
|
180
227
|
|
|
181
|
-
|
|
228
|
+
Add to your build scripts:
|
|
182
229
|
|
|
183
|
-
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"scripts": {
|
|
233
|
+
"prebuild": "idealyst-config generate --extends ../shared/.env",
|
|
234
|
+
"build": "vite build"
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
184
238
|
|
|
185
|
-
|
|
239
|
+
## Best Practices
|
|
186
240
|
|
|
187
|
-
|
|
241
|
+
1. **Generate before build** - Add to prebuild script
|
|
242
|
+
2. **Gitignore generated files** - Add `config.generated.ts` to `.gitignore`
|
|
243
|
+
3. **Commit .env.example** - Document required keys without values
|
|
244
|
+
4. **Validate at startup** - Use `config.validate()` early
|
|
188
245
|
|
|
189
246
|
## License
|
|
190
247
|
|
package/bin/idealyst-config.js
CHANGED
|
@@ -1,60 +1,93 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* CLI for @idealyst/config - Generate
|
|
4
|
+
* CLI for @idealyst/config - Generate config from .env files with inheritance
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Supports monorepo patterns with shared config:
|
|
7
|
+
* shared/.env → base config (lowest priority)
|
|
8
|
+
* web/.env → web overrides
|
|
9
|
+
* mobile/.env → mobile overrides
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
const fs = require('fs')
|
|
11
13
|
const path = require('path')
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
|
-
* Parse a .env file and extract
|
|
15
|
-
* Strips VITE_ prefix to normalize to canonical names.
|
|
16
|
+
* Parse a .env file and extract key-value pairs.
|
|
16
17
|
*/
|
|
17
|
-
function parseEnvFile(
|
|
18
|
-
|
|
18
|
+
function parseEnvFile(filePath) {
|
|
19
|
+
if (!fs.existsSync(filePath)) {
|
|
20
|
+
return {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
24
|
+
const config = {}
|
|
19
25
|
|
|
20
26
|
for (const line of content.split('\n')) {
|
|
21
27
|
const trimmed = line.trim()
|
|
22
28
|
|
|
23
|
-
// Skip empty lines and comments
|
|
24
29
|
if (!trimmed || trimmed.startsWith('#')) {
|
|
25
30
|
continue
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
// Extract key name (everything before the first =)
|
|
29
33
|
const equalsIndex = trimmed.indexOf('=')
|
|
30
34
|
if (equalsIndex === -1) {
|
|
31
35
|
continue
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
let key = trimmed.substring(0, equalsIndex).trim()
|
|
39
|
+
let value = trimmed.substring(equalsIndex + 1).trim()
|
|
40
|
+
|
|
41
|
+
// Remove quotes if present
|
|
42
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
43
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
44
|
+
value = value.slice(1, -1)
|
|
45
|
+
}
|
|
35
46
|
|
|
36
|
-
// Strip VITE_ prefix to normalize
|
|
47
|
+
// Strip VITE_ prefix to normalize
|
|
37
48
|
if (key.startsWith('VITE_')) {
|
|
38
49
|
key = key.substring(5)
|
|
39
50
|
}
|
|
40
51
|
|
|
41
|
-
|
|
42
|
-
if (key && !keys.includes(key)) {
|
|
43
|
-
keys.push(key)
|
|
44
|
-
}
|
|
52
|
+
config[key] = value
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
return
|
|
55
|
+
return config
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
/**
|
|
51
|
-
* Generate TypeScript
|
|
59
|
+
* Generate TypeScript config module with actual values.
|
|
52
60
|
*/
|
|
53
|
-
function
|
|
61
|
+
function generateConfigModule(config, sourceFiles) {
|
|
62
|
+
const sources = sourceFiles.join(', ')
|
|
63
|
+
const entries = Object.entries(config)
|
|
64
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
65
|
+
.map(([key, value]) => ` ${key}: ${JSON.stringify(value)}`)
|
|
66
|
+
.join(',\n')
|
|
67
|
+
|
|
68
|
+
return `// Auto-generated by @idealyst/config - DO NOT EDIT
|
|
69
|
+
// Sources: ${sources}
|
|
70
|
+
// Run \`idealyst-config generate\` to regenerate
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generated configuration values.
|
|
74
|
+
* Merged from: ${sources}
|
|
75
|
+
*/
|
|
76
|
+
export const generatedConfig: Record<string, string> = {
|
|
77
|
+
${entries}
|
|
78
|
+
}
|
|
79
|
+
`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Generate TypeScript declaration file.
|
|
84
|
+
*/
|
|
85
|
+
function generateDeclaration(keys, sourceFiles) {
|
|
54
86
|
const keyDefinitions = keys.map(k => ` ${k}: string`).join('\n')
|
|
87
|
+
const sources = sourceFiles.join(', ')
|
|
55
88
|
|
|
56
89
|
return `// Auto-generated by @idealyst/config - DO NOT EDIT
|
|
57
|
-
//
|
|
90
|
+
// Sources: ${sources}
|
|
58
91
|
// Run \`idealyst-config generate\` to regenerate
|
|
59
92
|
|
|
60
93
|
declare module '@idealyst/config' {
|
|
@@ -76,42 +109,78 @@ export {}
|
|
|
76
109
|
}
|
|
77
110
|
|
|
78
111
|
/**
|
|
79
|
-
* Find
|
|
112
|
+
* Find .env file in directory.
|
|
80
113
|
*/
|
|
81
114
|
function findEnvFile(directory) {
|
|
82
115
|
const candidates = ['.env.local', '.env.development', '.env']
|
|
83
|
-
|
|
84
116
|
for (const candidate of candidates) {
|
|
85
117
|
const envPath = path.join(directory, candidate)
|
|
86
118
|
if (fs.existsSync(envPath)) {
|
|
87
119
|
return envPath
|
|
88
120
|
}
|
|
89
121
|
}
|
|
122
|
+
return null
|
|
123
|
+
}
|
|
90
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Look for shared .env in common monorepo locations.
|
|
127
|
+
*/
|
|
128
|
+
function findSharedEnv(directory) {
|
|
129
|
+
const patterns = [
|
|
130
|
+
'../shared/.env',
|
|
131
|
+
'../../shared/.env',
|
|
132
|
+
'../../packages/shared/.env',
|
|
133
|
+
'../.env.shared',
|
|
134
|
+
]
|
|
135
|
+
for (const pattern of patterns) {
|
|
136
|
+
const sharedPath = path.resolve(directory, pattern)
|
|
137
|
+
if (fs.existsSync(sharedPath)) {
|
|
138
|
+
return sharedPath
|
|
139
|
+
}
|
|
140
|
+
}
|
|
91
141
|
return null
|
|
92
142
|
}
|
|
93
143
|
|
|
94
144
|
function printUsage() {
|
|
95
145
|
console.log(`
|
|
96
|
-
@idealyst/config - Generate
|
|
146
|
+
@idealyst/config - Generate config from .env files with inheritance
|
|
97
147
|
|
|
98
148
|
Usage:
|
|
99
149
|
idealyst-config generate [options]
|
|
100
150
|
|
|
101
151
|
Options:
|
|
102
|
-
--env <path>
|
|
103
|
-
--
|
|
104
|
-
--
|
|
152
|
+
--env <path> Path to .env file (default: auto-detect)
|
|
153
|
+
--extends <path> Inherit from another .env file (can use multiple times)
|
|
154
|
+
--output <path> Output path for generated config (default: src/config.generated.ts)
|
|
155
|
+
--types-only Generate only .d.ts file, no values
|
|
156
|
+
--help Show this help message
|
|
105
157
|
|
|
106
158
|
Examples:
|
|
159
|
+
# Simple usage - auto-detect .env
|
|
107
160
|
idealyst-config generate
|
|
108
|
-
|
|
109
|
-
|
|
161
|
+
|
|
162
|
+
# With shared config inheritance
|
|
163
|
+
idealyst-config generate --extends ../shared/.env --env .env
|
|
164
|
+
|
|
165
|
+
# Multiple inheritance (lowest to highest priority)
|
|
166
|
+
idealyst-config generate --extends ../../shared/.env --extends ../.env.common --env .env
|
|
167
|
+
|
|
168
|
+
# Types only (for type checking without exposing values)
|
|
169
|
+
idealyst-config generate --types-only --output src/env.d.ts
|
|
170
|
+
|
|
171
|
+
Inheritance:
|
|
172
|
+
In a monorepo with shared/web/mobile packages, you can set up inheritance:
|
|
173
|
+
|
|
174
|
+
shared/.env → API_URL=https://api.example.com
|
|
175
|
+
web/.env → API_URL=https://web-api.example.com (overrides shared)
|
|
176
|
+
mobile/.env → (inherits API_URL from shared)
|
|
177
|
+
|
|
178
|
+
The --extends flag loads configs in order, with later files taking priority.
|
|
110
179
|
`)
|
|
111
180
|
}
|
|
112
181
|
|
|
113
182
|
function parseArgs(args) {
|
|
114
|
-
const result = {}
|
|
183
|
+
const result = { extends: [] }
|
|
115
184
|
|
|
116
185
|
for (let i = 0; i < args.length; i++) {
|
|
117
186
|
const arg = args[i]
|
|
@@ -120,8 +189,12 @@ function parseArgs(args) {
|
|
|
120
189
|
result.help = true
|
|
121
190
|
} else if (arg === '--env' && args[i + 1]) {
|
|
122
191
|
result.env = args[++i]
|
|
192
|
+
} else if (arg === '--extends' && args[i + 1]) {
|
|
193
|
+
result.extends.push(args[++i])
|
|
123
194
|
} else if (arg === '--output' && args[i + 1]) {
|
|
124
195
|
result.output = args[++i]
|
|
196
|
+
} else if (arg === '--types-only') {
|
|
197
|
+
result.typesOnly = true
|
|
125
198
|
} else if (!arg.startsWith('-') && !result.command) {
|
|
126
199
|
result.command = arg
|
|
127
200
|
}
|
|
@@ -145,59 +218,91 @@ function main() {
|
|
|
145
218
|
}
|
|
146
219
|
|
|
147
220
|
const cwd = process.cwd()
|
|
221
|
+
const sourceFiles = []
|
|
222
|
+
const configs = []
|
|
223
|
+
|
|
224
|
+
// Load inherited configs first (lowest priority)
|
|
225
|
+
for (const extendPath of args.extends) {
|
|
226
|
+
const resolvedPath = path.isAbsolute(extendPath)
|
|
227
|
+
? extendPath
|
|
228
|
+
: path.resolve(cwd, extendPath)
|
|
229
|
+
|
|
230
|
+
if (fs.existsSync(resolvedPath)) {
|
|
231
|
+
configs.push(parseEnvFile(resolvedPath))
|
|
232
|
+
sourceFiles.push(path.relative(cwd, resolvedPath))
|
|
233
|
+
console.log(` ← ${path.relative(cwd, resolvedPath)}`)
|
|
234
|
+
} else {
|
|
235
|
+
console.warn(`Warning: Inherited env file not found: ${resolvedPath}`)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
148
238
|
|
|
149
|
-
//
|
|
239
|
+
// Auto-detect shared env if no extends specified
|
|
240
|
+
if (args.extends.length === 0) {
|
|
241
|
+
const sharedEnv = findSharedEnv(cwd)
|
|
242
|
+
if (sharedEnv) {
|
|
243
|
+
configs.push(parseEnvFile(sharedEnv))
|
|
244
|
+
sourceFiles.push(path.relative(cwd, sharedEnv))
|
|
245
|
+
console.log(` ← ${path.relative(cwd, sharedEnv)} (auto-detected shared)`)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Load main env file (highest priority)
|
|
150
250
|
let envPath
|
|
151
251
|
if (args.env) {
|
|
152
|
-
envPath = path.isAbsolute(args.env) ? args.env : path.
|
|
252
|
+
envPath = path.isAbsolute(args.env) ? args.env : path.resolve(cwd, args.env)
|
|
153
253
|
} else {
|
|
154
254
|
envPath = findEnvFile(cwd)
|
|
155
|
-
if (!envPath) {
|
|
156
|
-
console.error('Error: No .env file found in current directory.')
|
|
157
|
-
console.error('Create a .env file or specify one with --env <path>')
|
|
158
|
-
process.exit(1)
|
|
159
|
-
}
|
|
160
255
|
}
|
|
161
256
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
257
|
+
if (envPath && fs.existsSync(envPath)) {
|
|
258
|
+
configs.push(parseEnvFile(envPath))
|
|
259
|
+
sourceFiles.push(path.relative(cwd, envPath))
|
|
260
|
+
console.log(` ← ${path.relative(cwd, envPath)}`)
|
|
261
|
+
} else if (args.env) {
|
|
262
|
+
console.error(`Error: Environment file not found: ${args.env}`)
|
|
165
263
|
process.exit(1)
|
|
166
264
|
}
|
|
167
265
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
// Read and parse env file
|
|
175
|
-
const envContent = fs.readFileSync(envPath, 'utf-8')
|
|
176
|
-
const keys = parseEnvFile(envContent)
|
|
266
|
+
if (configs.length === 0) {
|
|
267
|
+
console.error('Error: No .env files found.')
|
|
268
|
+
console.error('Create a .env file or specify one with --env <path>')
|
|
269
|
+
process.exit(1)
|
|
270
|
+
}
|
|
177
271
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
272
|
+
// Merge configs (later configs override earlier ones)
|
|
273
|
+
const mergedConfig = Object.assign({}, ...configs)
|
|
274
|
+
const keys = Object.keys(mergedConfig).sort()
|
|
181
275
|
|
|
182
|
-
|
|
183
|
-
|
|
276
|
+
// Determine output path
|
|
277
|
+
const outputPath = args.output
|
|
278
|
+
? (path.isAbsolute(args.output) ? args.output : path.resolve(cwd, args.output))
|
|
279
|
+
: path.resolve(cwd, 'src', 'config.generated.ts')
|
|
184
280
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
281
|
+
// Ensure output directory exists
|
|
282
|
+
const outputDir = path.dirname(outputPath)
|
|
283
|
+
if (!fs.existsSync(outputDir)) {
|
|
284
|
+
fs.mkdirSync(outputDir, { recursive: true })
|
|
285
|
+
}
|
|
190
286
|
|
|
191
|
-
|
|
287
|
+
if (args.typesOnly) {
|
|
288
|
+
// Generate declaration file only
|
|
289
|
+
const declaration = generateDeclaration(keys, sourceFiles)
|
|
192
290
|
fs.writeFileSync(outputPath, declaration)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
console.
|
|
199
|
-
|
|
291
|
+
console.log(`\nGenerated types at: ${path.relative(cwd, outputPath)}`)
|
|
292
|
+
} else {
|
|
293
|
+
// Generate config module
|
|
294
|
+
const module = generateConfigModule(mergedConfig, sourceFiles)
|
|
295
|
+
fs.writeFileSync(outputPath, module)
|
|
296
|
+
console.log(`\nGenerated config at: ${path.relative(cwd, outputPath)}`)
|
|
297
|
+
|
|
298
|
+
// Also generate declaration file
|
|
299
|
+
const declPath = outputPath.replace(/\.ts$/, '.d.ts')
|
|
300
|
+
const declaration = generateDeclaration(keys, sourceFiles)
|
|
301
|
+
fs.writeFileSync(declPath, declaration)
|
|
302
|
+
console.log(`Generated types at: ${path.relative(cwd, declPath)}`)
|
|
200
303
|
}
|
|
304
|
+
|
|
305
|
+
console.log(`Keys: ${keys.join(', ')}`)
|
|
201
306
|
}
|
|
202
307
|
|
|
203
308
|
main()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/config",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.13",
|
|
4
4
|
"description": "Cross-platform configuration and environment variable support for React and React Native",
|
|
5
5
|
"documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/config#readme",
|
|
6
6
|
"readme": "README.md",
|
package/src/cli/generate.ts
CHANGED
|
@@ -3,22 +3,38 @@ import path from 'path'
|
|
|
3
3
|
|
|
4
4
|
export interface GenerateOptions {
|
|
5
5
|
/**
|
|
6
|
-
* Path to the .env file
|
|
6
|
+
* Path to the platform-specific .env file (highest priority)
|
|
7
7
|
*/
|
|
8
|
-
envPath
|
|
8
|
+
envPath?: string
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Paths to inherited .env files (lowest to highest priority)
|
|
12
|
+
* e.g., ['../shared/.env'] - shared is loaded first, then envPath overrides
|
|
13
|
+
*/
|
|
14
|
+
inheritFrom?: string[]
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Path to write the generated TypeScript config file
|
|
12
18
|
*/
|
|
13
19
|
outputPath: string
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Whether to generate types only (declaration file) or full config module
|
|
23
|
+
*/
|
|
24
|
+
typesOnly?: boolean
|
|
14
25
|
}
|
|
15
26
|
|
|
16
27
|
/**
|
|
17
|
-
* Parse a .env file and extract
|
|
28
|
+
* Parse a .env file and extract key-value pairs.
|
|
18
29
|
* Strips VITE_ prefix to normalize to canonical names.
|
|
19
30
|
*/
|
|
20
|
-
export function parseEnvFile(
|
|
21
|
-
|
|
31
|
+
export function parseEnvFile(filePath: string): Record<string, string> {
|
|
32
|
+
if (!fs.existsSync(filePath)) {
|
|
33
|
+
return {}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
37
|
+
const config: Record<string, string> = {}
|
|
22
38
|
|
|
23
39
|
for (const line of content.split('\n')) {
|
|
24
40
|
const trimmed = line.trim()
|
|
@@ -28,36 +44,48 @@ export function parseEnvFile(content: string): string[] {
|
|
|
28
44
|
continue
|
|
29
45
|
}
|
|
30
46
|
|
|
31
|
-
// Extract key
|
|
47
|
+
// Extract key=value
|
|
32
48
|
const equalsIndex = trimmed.indexOf('=')
|
|
33
49
|
if (equalsIndex === -1) {
|
|
34
50
|
continue
|
|
35
51
|
}
|
|
36
52
|
|
|
37
53
|
let key = trimmed.substring(0, equalsIndex).trim()
|
|
54
|
+
let value = trimmed.substring(equalsIndex + 1).trim()
|
|
55
|
+
|
|
56
|
+
// Remove quotes if present
|
|
57
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
58
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
59
|
+
value = value.slice(1, -1)
|
|
60
|
+
}
|
|
38
61
|
|
|
39
62
|
// Strip VITE_ prefix to normalize to canonical names
|
|
40
63
|
if (key.startsWith('VITE_')) {
|
|
41
64
|
key = key.substring(5)
|
|
42
65
|
}
|
|
43
66
|
|
|
44
|
-
|
|
45
|
-
if (key && !keys.includes(key)) {
|
|
46
|
-
keys.push(key)
|
|
47
|
-
}
|
|
67
|
+
config[key] = value
|
|
48
68
|
}
|
|
49
69
|
|
|
50
|
-
return
|
|
70
|
+
return config
|
|
51
71
|
}
|
|
52
72
|
|
|
53
73
|
/**
|
|
54
|
-
*
|
|
74
|
+
* Merge multiple env configs with later configs taking priority.
|
|
55
75
|
*/
|
|
56
|
-
export function
|
|
76
|
+
export function mergeEnvConfigs(...configs: Record<string, string>[]): Record<string, string> {
|
|
77
|
+
return Object.assign({}, ...configs)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generate TypeScript declaration content from config keys.
|
|
82
|
+
*/
|
|
83
|
+
export function generateDeclaration(keys: string[], sourceFiles: string[]): string {
|
|
57
84
|
const keyDefinitions = keys.map(k => ` ${k}: string`).join('\n')
|
|
85
|
+
const sources = sourceFiles.join(', ')
|
|
58
86
|
|
|
59
87
|
return `// Auto-generated by @idealyst/config - DO NOT EDIT
|
|
60
|
-
//
|
|
88
|
+
// Sources: ${sources}
|
|
61
89
|
// Run \`idealyst-config generate\` to regenerate
|
|
62
90
|
|
|
63
91
|
declare module '@idealyst/config' {
|
|
@@ -79,42 +107,98 @@ export {}
|
|
|
79
107
|
}
|
|
80
108
|
|
|
81
109
|
/**
|
|
82
|
-
* Generate TypeScript config
|
|
83
|
-
*
|
|
84
|
-
* @param options - Generation options
|
|
85
|
-
* @returns The path to the generated file
|
|
110
|
+
* Generate a TypeScript config module with actual values.
|
|
86
111
|
*/
|
|
87
|
-
export function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
112
|
+
export function generateConfigModule(config: Record<string, string>, sourceFiles: string[]): string {
|
|
113
|
+
const sources = sourceFiles.join(', ')
|
|
114
|
+
const entries = Object.entries(config)
|
|
115
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
116
|
+
.map(([key, value]) => ` ${key}: ${JSON.stringify(value)}`)
|
|
117
|
+
.join(',\n')
|
|
118
|
+
|
|
119
|
+
return `// Auto-generated by @idealyst/config - DO NOT EDIT
|
|
120
|
+
// Sources: ${sources}
|
|
121
|
+
// Run \`idealyst-config generate\` to regenerate
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Generated configuration values.
|
|
125
|
+
* Merged from: ${sources}
|
|
126
|
+
*/
|
|
127
|
+
export const generatedConfig: Record<string, string> = {
|
|
128
|
+
${entries}
|
|
129
|
+
}
|
|
130
|
+
`
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Generate config from .env files with inheritance support.
|
|
135
|
+
*/
|
|
136
|
+
export function generateConfigTypes(options: GenerateOptions): { outputPath: string; keys: string[] } {
|
|
137
|
+
const sourceFiles: string[] = []
|
|
138
|
+
const configs: Record<string, string>[] = []
|
|
139
|
+
|
|
140
|
+
// Load inherited configs first (lowest priority)
|
|
141
|
+
if (options.inheritFrom) {
|
|
142
|
+
for (const inheritPath of options.inheritFrom) {
|
|
143
|
+
const resolvedPath = path.isAbsolute(inheritPath)
|
|
144
|
+
? inheritPath
|
|
145
|
+
: path.resolve(path.dirname(options.outputPath), inheritPath)
|
|
146
|
+
|
|
147
|
+
if (fs.existsSync(resolvedPath)) {
|
|
148
|
+
configs.push(parseEnvFile(resolvedPath))
|
|
149
|
+
sourceFiles.push(path.basename(resolvedPath))
|
|
150
|
+
}
|
|
151
|
+
}
|
|
91
152
|
}
|
|
92
153
|
|
|
93
|
-
|
|
94
|
-
|
|
154
|
+
// Load main env file (highest priority)
|
|
155
|
+
if (options.envPath) {
|
|
156
|
+
const resolvedEnvPath = path.isAbsolute(options.envPath)
|
|
157
|
+
? options.envPath
|
|
158
|
+
: path.resolve(process.cwd(), options.envPath)
|
|
95
159
|
|
|
96
|
-
|
|
97
|
-
|
|
160
|
+
if (fs.existsSync(resolvedEnvPath)) {
|
|
161
|
+
configs.push(parseEnvFile(resolvedEnvPath))
|
|
162
|
+
sourceFiles.push(path.basename(resolvedEnvPath))
|
|
163
|
+
}
|
|
98
164
|
}
|
|
99
165
|
|
|
100
|
-
//
|
|
101
|
-
const
|
|
166
|
+
// Merge configs
|
|
167
|
+
const mergedConfig = mergeEnvConfigs(...configs)
|
|
168
|
+
const keys = Object.keys(mergedConfig).sort()
|
|
169
|
+
|
|
170
|
+
if (keys.length === 0) {
|
|
171
|
+
console.warn('Warning: No environment variables found')
|
|
172
|
+
}
|
|
102
173
|
|
|
103
|
-
// Ensure
|
|
174
|
+
// Ensure output directory exists
|
|
104
175
|
const outputDir = path.dirname(options.outputPath)
|
|
105
176
|
if (!fs.existsSync(outputDir)) {
|
|
106
177
|
fs.mkdirSync(outputDir, { recursive: true })
|
|
107
178
|
}
|
|
108
179
|
|
|
109
|
-
|
|
110
|
-
|
|
180
|
+
if (options.typesOnly) {
|
|
181
|
+
// Generate declaration file only
|
|
182
|
+
const declaration = generateDeclaration(keys, sourceFiles)
|
|
183
|
+
fs.writeFileSync(options.outputPath, declaration)
|
|
184
|
+
} else {
|
|
185
|
+
// Generate full config module
|
|
186
|
+
const module = generateConfigModule(mergedConfig, sourceFiles)
|
|
187
|
+
fs.writeFileSync(options.outputPath, module)
|
|
188
|
+
|
|
189
|
+
// Also generate declaration file alongside
|
|
190
|
+
const declPath = options.outputPath.replace(/\.ts$/, '.d.ts')
|
|
191
|
+
if (declPath !== options.outputPath) {
|
|
192
|
+
const declaration = generateDeclaration(keys, sourceFiles)
|
|
193
|
+
fs.writeFileSync(declPath, declaration)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
111
196
|
|
|
112
|
-
return options.outputPath
|
|
197
|
+
return { outputPath: options.outputPath, keys }
|
|
113
198
|
}
|
|
114
199
|
|
|
115
200
|
/**
|
|
116
201
|
* Find the most appropriate .env file in a directory.
|
|
117
|
-
* Prefers .env.local > .env.development > .env
|
|
118
202
|
*/
|
|
119
203
|
export function findEnvFile(directory: string): string | null {
|
|
120
204
|
const candidates = ['.env.local', '.env.development', '.env']
|
|
@@ -128,3 +212,25 @@ export function findEnvFile(directory: string): string | null {
|
|
|
128
212
|
|
|
129
213
|
return null
|
|
130
214
|
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Look for a shared .env file in parent directories.
|
|
218
|
+
*/
|
|
219
|
+
export function findSharedEnv(directory: string): string | null {
|
|
220
|
+
// Common patterns for shared env in monorepos
|
|
221
|
+
const patterns = [
|
|
222
|
+
'../shared/.env',
|
|
223
|
+
'../../shared/.env',
|
|
224
|
+
'../.env.shared',
|
|
225
|
+
'../../.env.shared',
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
for (const pattern of patterns) {
|
|
229
|
+
const sharedPath = path.resolve(directory, pattern)
|
|
230
|
+
if (fs.existsSync(sharedPath)) {
|
|
231
|
+
return sharedPath
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return null
|
|
236
|
+
}
|
package/src/config.web.ts
CHANGED
|
@@ -2,22 +2,63 @@ import type { IConfig } from './types'
|
|
|
2
2
|
import { ConfigValidationError } from './types'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Config store - populated by the generated config module or manually via setConfig().
|
|
6
|
+
*/
|
|
7
|
+
let configStore: Record<string, string> = {}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Set config values. Called automatically when importing from a project that
|
|
11
|
+
* has generated config, or can be called manually.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // In your app entry point:
|
|
16
|
+
* import { setConfig } from '@idealyst/config'
|
|
17
|
+
* import { generatedConfig } from './config.generated'
|
|
18
|
+
*
|
|
19
|
+
* setConfig(generatedConfig)
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function setConfig(config: Record<string, string>): void {
|
|
23
|
+
configStore = { ...configStore, ...config }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Clear all config values. Useful for testing.
|
|
28
|
+
*/
|
|
29
|
+
export function clearConfig(): void {
|
|
30
|
+
configStore = {}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get the raw config store. Useful for debugging.
|
|
35
|
+
*/
|
|
36
|
+
export function getConfigStore(): Record<string, string> {
|
|
37
|
+
return { ...configStore }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Web implementation of IConfig.
|
|
42
|
+
*
|
|
43
|
+
* Config values come from:
|
|
44
|
+
* 1. Generated config module (via setConfig)
|
|
45
|
+
* 2. Manual setConfig() calls
|
|
46
|
+
*
|
|
47
|
+
* Usage:
|
|
48
|
+
* ```typescript
|
|
49
|
+
* import { config, setConfig } from '@idealyst/config'
|
|
50
|
+
* import { generatedConfig } from './config.generated'
|
|
6
51
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* - Internally we look up: import.meta.env.VITE_API_URL
|
|
52
|
+
* // Initialize config (do this once at app startup)
|
|
53
|
+
* setConfig(generatedConfig)
|
|
10
54
|
*
|
|
11
|
-
*
|
|
55
|
+
* // Then use anywhere
|
|
56
|
+
* const apiUrl = config.get('API_URL')
|
|
57
|
+
* ```
|
|
12
58
|
*/
|
|
13
59
|
class WebConfig implements IConfig {
|
|
14
60
|
get(key: string, defaultValue?: string): string | undefined {
|
|
15
|
-
|
|
16
|
-
// environment variables to client-side code.
|
|
17
|
-
// User code uses canonical names: config.get('API_URL')
|
|
18
|
-
// Internally we look up: import.meta.env.VITE_API_URL
|
|
19
|
-
const value = (import.meta.env as Record<string, string | undefined>)[`VITE_${key}`]
|
|
20
|
-
return value ?? defaultValue
|
|
61
|
+
return configStore[key] ?? defaultValue
|
|
21
62
|
}
|
|
22
63
|
|
|
23
64
|
getRequired(key: string): string {
|
|
@@ -25,21 +66,18 @@ class WebConfig implements IConfig {
|
|
|
25
66
|
if (value === undefined) {
|
|
26
67
|
throw new Error(
|
|
27
68
|
`Required config key "${key}" is not defined. ` +
|
|
28
|
-
`Make sure
|
|
69
|
+
`Make sure you've run "idealyst-config generate" and imported the generated config.`
|
|
29
70
|
)
|
|
30
71
|
}
|
|
31
72
|
return value
|
|
32
73
|
}
|
|
33
74
|
|
|
34
75
|
has(key: string): boolean {
|
|
35
|
-
return
|
|
76
|
+
return configStore[key] !== undefined
|
|
36
77
|
}
|
|
37
78
|
|
|
38
79
|
keys(): string[] {
|
|
39
|
-
|
|
40
|
-
return Object.keys(import.meta.env)
|
|
41
|
-
.filter(k => k.startsWith('VITE_'))
|
|
42
|
-
.map(k => k.replace(/^VITE_/, ''))
|
|
80
|
+
return Object.keys(configStore).sort()
|
|
43
81
|
}
|
|
44
82
|
|
|
45
83
|
validate(requiredKeys: string[]): void {
|
package/src/index.web.ts
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Web entry point for @idealyst/config
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Config values come from a generated module created by the CLI.
|
|
5
|
+
* This approach works with any bundler and supports env inheritance.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* ```typescript
|
|
9
|
-
*
|
|
9
|
+
* // 1. Generate config (run in terminal)
|
|
10
|
+
* // idealyst-config generate --extends ../shared/.env --env .env
|
|
10
11
|
*
|
|
11
|
-
* //
|
|
12
|
-
*
|
|
12
|
+
* // 2. Initialize in your app entry point
|
|
13
|
+
* import { config, setConfig } from '@idealyst/config'
|
|
14
|
+
* import { generatedConfig } from './config.generated'
|
|
15
|
+
* setConfig(generatedConfig)
|
|
16
|
+
*
|
|
17
|
+
* // 3. Use anywhere
|
|
13
18
|
* const apiUrl = config.get('API_URL')
|
|
14
19
|
* ```
|
|
15
20
|
*/
|
|
16
21
|
|
|
17
|
-
import WebConfig from './config.web'
|
|
22
|
+
import WebConfig, { setConfig, clearConfig, getConfigStore } from './config.web'
|
|
18
23
|
|
|
19
24
|
// Create singleton instance for web
|
|
20
25
|
const config = new WebConfig()
|
|
21
26
|
|
|
22
27
|
export default config
|
|
23
|
-
export { config, config as Config, WebConfig }
|
|
28
|
+
export { config, config as Config, WebConfig, setConfig, clearConfig, getConfigStore }
|
|
24
29
|
export * from './types'
|
package/src/vite-env.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Type declarations for Vite's import.meta.env
|
|
5
|
-
*
|
|
6
|
-
* This file provides type information for import.meta.env when Vite types
|
|
7
|
-
* are not available. In projects using Vite, these types are provided by
|
|
8
|
-
* vite/client.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
interface ImportMetaEnv {
|
|
12
|
-
[key: string]: string | undefined
|
|
13
|
-
readonly MODE: string
|
|
14
|
-
readonly BASE_URL: string
|
|
15
|
-
readonly PROD: boolean
|
|
16
|
-
readonly DEV: boolean
|
|
17
|
-
readonly SSR: boolean
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface ImportMeta {
|
|
21
|
-
readonly env: ImportMetaEnv
|
|
22
|
-
}
|