@rivinia/env 0.3.1 → 0.3.2
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 +22 -14
- package/package.json +1 -1
- package/schematics/ng-add/index.cjs +65 -33
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Angular CLI `ng add` schematic. It does not read environment values at
|
|
|
8
8
|
runtime: Angular's build file replacements select a statically imported
|
|
9
9
|
environment file.
|
|
10
10
|
|
|
11
|
-
-
|
|
11
|
+
- Package version: **0.3.2**
|
|
12
12
|
- No runtime providers or services; the package supplies a type and CLI tooling.
|
|
13
13
|
- The generated model imports `BaseEnvironment` from this package.
|
|
14
14
|
|
|
@@ -72,12 +72,16 @@ their generated API URLs are `https://staging.example.com/api` and
|
|
|
72
72
|
`https://api.example.com/api`. Replace these example URLs and any other
|
|
73
73
|
generated values with the app's real configuration.
|
|
74
74
|
|
|
75
|
-
The schematic
|
|
76
|
-
|
|
77
|
-
`staging
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
The schematic creates the short `dev`, `stag`, and `prod` build configurations
|
|
76
|
+
for the selected environments, copying settings from the corresponding
|
|
77
|
+
`development`, `staging`, or `production` configuration when one exists.
|
|
78
|
+
Otherwise, it creates the short configuration with the required environment
|
|
79
|
+
replacement when needed (`dev` uses the base file directly). It also wires
|
|
80
|
+
existing legacy keys when present: `development`
|
|
81
|
+
uses `environment.ts`, `staging` replaces it with
|
|
82
|
+
`environment.staging.ts`, and `production` replaces it with
|
|
83
|
+
`environment.prod.ts`. The short names are the recommended CLI commands; the
|
|
84
|
+
legacy names remain available for existing scripts.
|
|
81
85
|
|
|
82
86
|
For selected configurations that exist, it sets `outputPath` to
|
|
83
87
|
`builds/dev`, `builds/stag`, or `builds/prod` only if no output path is
|
|
@@ -97,24 +101,28 @@ Always import the base path from the app, not a stage/prod-specific file:
|
|
|
97
101
|
|
|
98
102
|
```ts
|
|
99
103
|
// src/app/trip.service.ts
|
|
100
|
-
import {
|
|
104
|
+
import { Service } from '@angular/core';
|
|
101
105
|
import { environment } from '../environments/environment';
|
|
102
106
|
|
|
103
|
-
@
|
|
107
|
+
@Service()
|
|
104
108
|
export class TripService {
|
|
105
109
|
readonly apiUrl = environment.apiUrl;
|
|
106
110
|
}
|
|
107
111
|
```
|
|
108
112
|
|
|
109
|
-
|
|
110
|
-
configuration. For example:
|
|
113
|
+
After `ng add @rivinia/env`, build with the short configuration names:
|
|
111
114
|
|
|
112
115
|
```bash
|
|
113
|
-
ng build --configuration
|
|
114
|
-
ng build --configuration
|
|
115
|
-
ng build --configuration
|
|
116
|
+
ng build --configuration dev
|
|
117
|
+
ng build --configuration stag
|
|
118
|
+
ng build --configuration prod
|
|
116
119
|
```
|
|
117
120
|
|
|
121
|
+
These are actual keys in `angular.json`, not Angular CLI aliases. If you
|
|
122
|
+
maintain custom configurations, inspect the selected project's
|
|
123
|
+
`architect.build.configurations` and ensure each key has the intended
|
|
124
|
+
`fileReplacements`; `ng build --configuration` uses the key exactly as written.
|
|
125
|
+
|
|
118
126
|
The generated files are compile-time configuration, not secrets: values
|
|
119
127
|
included in a browser build are visible to its users. `BaseEnvironment`
|
|
120
128
|
requires only readonly `production: boolean` and `version: string`; extend
|
package/package.json
CHANGED
|
@@ -28,8 +28,14 @@ const OUTPUT_DIR = {
|
|
|
28
28
|
stage: 'builds/stag',
|
|
29
29
|
prod: 'builds/prod',
|
|
30
30
|
};
|
|
31
|
-
/**
|
|
31
|
+
/** Public short configuration names used by `ng build --configuration`. */
|
|
32
32
|
const CONFIG_NAME = {
|
|
33
|
+
dev: 'dev',
|
|
34
|
+
stage: 'stag',
|
|
35
|
+
prod: 'prod',
|
|
36
|
+
};
|
|
37
|
+
/** Existing long configuration names remain wired for compatibility. */
|
|
38
|
+
const LEGACY_CONFIG_NAME = {
|
|
33
39
|
dev: 'development',
|
|
34
40
|
stage: 'staging',
|
|
35
41
|
prod: 'production',
|
|
@@ -38,11 +44,11 @@ const fileDefs = (sourceRoot, selected) => {
|
|
|
38
44
|
const files = [
|
|
39
45
|
{ name: `${sourceRoot}/environments/environment.model.ts`, content: () => MODEL_FILE },
|
|
40
46
|
// The base file is always scaffolded: it is the `replace` source for
|
|
41
|
-
// prod/
|
|
47
|
+
// prod/stag swaps (and what dev uses directly).
|
|
42
48
|
{
|
|
43
49
|
name: `${sourceRoot}/environments/environment.ts`,
|
|
44
50
|
content: (version) => `import type { AppEnvironment } from './environment.model';\n\n` +
|
|
45
|
-
`/** Development defaults. Used as-is by the \`
|
|
51
|
+
`/** Development defaults. Used as-is by the \`dev\` configuration. */\n` +
|
|
46
52
|
`export const environment: AppEnvironment = {\n` +
|
|
47
53
|
` production: false,\n` +
|
|
48
54
|
` version: '${version}',\n` +
|
|
@@ -54,7 +60,7 @@ const fileDefs = (sourceRoot, selected) => {
|
|
|
54
60
|
files.push({
|
|
55
61
|
name: `${sourceRoot}/environments/environment.staging.ts`,
|
|
56
62
|
content: (version) => `import type { AppEnvironment } from './environment.model';\n\n` +
|
|
57
|
-
`/** Staging mirrors production. Wired via the \`
|
|
63
|
+
`/** Staging mirrors production. Wired via the \`stag\` configuration. */\n` +
|
|
58
64
|
`export const environment: AppEnvironment = {\n` +
|
|
59
65
|
` production: true,\n` +
|
|
60
66
|
` version: '${version}',\n` +
|
|
@@ -77,14 +83,15 @@ const fileDefs = (sourceRoot, selected) => {
|
|
|
77
83
|
};
|
|
78
84
|
/**
|
|
79
85
|
* `ng add @rivinia/env` — scaffolds typed environment files and wires
|
|
80
|
-
*
|
|
86
|
+
* dev/stag/prod configurations with file replacements.
|
|
81
87
|
*
|
|
82
88
|
* - Never overwrites existing files (each file skipped individually).
|
|
83
89
|
* - Never removes user configuration: only ADDS missing fileReplacements
|
|
84
|
-
* (
|
|
85
|
-
*
|
|
86
|
-
* - `
|
|
87
|
-
* `
|
|
90
|
+
* (one per source file) and creates short configurations from their legacy
|
|
91
|
+
* equivalents when absent.
|
|
92
|
+
* - `dev` intentionally gets no replacement (it uses `environment.ts`).
|
|
93
|
+
* Existing `development`, `staging`, and `production` configurations remain
|
|
94
|
+
* wired for compatibility.
|
|
88
95
|
* - App version is snapshotted from the app's `package.json` (`0.0.0` fallback).
|
|
89
96
|
*/
|
|
90
97
|
function ngAdd(options) {
|
|
@@ -127,14 +134,20 @@ function ngAdd(options) {
|
|
|
127
134
|
created++;
|
|
128
135
|
}
|
|
129
136
|
if (selected.includes('prod')) {
|
|
130
|
-
|
|
137
|
+
ensureNamedConfig(tree, angular, name, 'prod', ['production'], context);
|
|
138
|
+
const replacement = {
|
|
131
139
|
replace: `${sourceRoot}/environments/environment.ts`,
|
|
132
140
|
with: `${sourceRoot}/environments/environment.prod.ts`,
|
|
133
|
-
}
|
|
141
|
+
};
|
|
142
|
+
ensureReplacement(tree, angular, name, 'prod', replacement);
|
|
143
|
+
ensureReplacement(tree, angular, name, 'production', replacement);
|
|
134
144
|
}
|
|
135
145
|
if (selected.includes('stage')) {
|
|
136
146
|
ensureStaging(tree, angular, name, sourceRoot, context);
|
|
137
147
|
}
|
|
148
|
+
if (selected.includes('dev')) {
|
|
149
|
+
ensureNamedConfig(tree, angular, name, 'dev', ['development'], context);
|
|
150
|
+
}
|
|
138
151
|
ensureOutputPaths(tree, angular, name, selected, context);
|
|
139
152
|
if (created > 0 || configsSnapshot() !== configsBefore) {
|
|
140
153
|
tree.overwrite('/angular.json', JSON.stringify(angular, null, 2) + '\n');
|
|
@@ -204,18 +217,25 @@ function ensureReplacement(tree, angular, name, configuration, entry) {
|
|
|
204
217
|
const configurations = getConfigurations(angular, name);
|
|
205
218
|
const target = configurations[configuration];
|
|
206
219
|
if (!target)
|
|
207
|
-
return;
|
|
220
|
+
return;
|
|
208
221
|
const list = target['fileReplacements'];
|
|
209
222
|
if (!Array.isArray(list)) {
|
|
210
223
|
target['fileReplacements'] = [{ ...entry }];
|
|
211
224
|
return;
|
|
212
225
|
}
|
|
213
|
-
const
|
|
226
|
+
const matches = list
|
|
227
|
+
.map((item, index) => ({ item, index }))
|
|
228
|
+
.filter(({ item }) => typeof item === 'object' &&
|
|
214
229
|
item !== null &&
|
|
215
|
-
item['replace'] === entry.replace
|
|
216
|
-
|
|
217
|
-
if (!exists)
|
|
230
|
+
item['replace'] === entry.replace);
|
|
231
|
+
if (matches.length === 0) {
|
|
218
232
|
list.push({ ...entry });
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const firstIndex = matches[0].index;
|
|
236
|
+
list[firstIndex] = { ...entry };
|
|
237
|
+
for (const { index } of matches.slice(1).reverse())
|
|
238
|
+
list.splice(index, 1);
|
|
219
239
|
}
|
|
220
240
|
/**
|
|
221
241
|
* Points each selected configuration at its build directory
|
|
@@ -228,12 +248,13 @@ function ensureOutputPaths(tree, angular, name, selected, context) {
|
|
|
228
248
|
const configurations = getConfigurations(angular, name);
|
|
229
249
|
let touched = false;
|
|
230
250
|
for (const env of selected) {
|
|
231
|
-
const configName
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
251
|
+
for (const configName of [CONFIG_NAME[env], LEGACY_CONFIG_NAME[env]]) {
|
|
252
|
+
const target = configurations[configName];
|
|
253
|
+
if (!target || 'outputPath' in target)
|
|
254
|
+
continue;
|
|
255
|
+
target['outputPath'] = OUTPUT_DIR[env];
|
|
256
|
+
touched = true;
|
|
257
|
+
}
|
|
237
258
|
}
|
|
238
259
|
if (touched)
|
|
239
260
|
ensureGitignore(tree, context);
|
|
@@ -251,17 +272,28 @@ function ensureGitignore(tree, context) {
|
|
|
251
272
|
function ensureStaging(tree, angular, name, sourceRoot, context) {
|
|
252
273
|
if (!tree.exists('/angular.json'))
|
|
253
274
|
return;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const copy = production && typeof production === 'object' ? { ...production } : {};
|
|
258
|
-
delete copy['fileReplacements'];
|
|
259
|
-
delete copy['outputPath'];
|
|
260
|
-
configurations['staging'] = copy;
|
|
261
|
-
context.logger.info('[rivinia/env] created `staging` configuration.');
|
|
262
|
-
}
|
|
263
|
-
ensureReplacement(tree, angular, name, 'staging', {
|
|
275
|
+
ensureNamedConfig(tree, angular, name, 'stag', ['staging', 'production'], context);
|
|
276
|
+
ensureNamedConfig(tree, angular, name, 'staging', ['stag', 'production'], context);
|
|
277
|
+
const replacement = {
|
|
264
278
|
replace: `${sourceRoot}/environments/environment.ts`,
|
|
265
279
|
with: `${sourceRoot}/environments/environment.staging.ts`,
|
|
266
|
-
}
|
|
280
|
+
};
|
|
281
|
+
ensureReplacement(tree, angular, name, 'stag', replacement);
|
|
282
|
+
ensureReplacement(tree, angular, name, 'staging', replacement);
|
|
283
|
+
}
|
|
284
|
+
function ensureNamedConfig(tree, angular, name, configuration, fallbacks, context) {
|
|
285
|
+
if (!tree.exists('/angular.json'))
|
|
286
|
+
return;
|
|
287
|
+
const configurations = getConfigurations(angular, name);
|
|
288
|
+
if (configurations[configuration])
|
|
289
|
+
return;
|
|
290
|
+
const source = fallbacks
|
|
291
|
+
.map((fallback) => configurations[fallback])
|
|
292
|
+
.find((candidate) => candidate && typeof candidate === 'object');
|
|
293
|
+
const copy = source
|
|
294
|
+
? JSON.parse(JSON.stringify(source))
|
|
295
|
+
: {};
|
|
296
|
+
delete copy['outputPath'];
|
|
297
|
+
configurations[configuration] = copy;
|
|
298
|
+
context.logger.info(`[rivinia/env] created \`${configuration}\` configuration.`);
|
|
267
299
|
}
|