@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 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
- - Prepared patch: **0.3.1** (latest published: **0.3.0**)
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 adds a file replacement to an existing `production`
76
- configuration when `prod` is selected. When `stage` is selected, it creates a
77
- `staging` configuration if needed (copying `production` settings except its
78
- `fileReplacements` and `outputPath`) and adds the staging replacement.
79
- `development` intentionally uses `environment.ts` directly. The schematic
80
- does not create a missing `production` or `development` configuration.
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 { Injectable } from '@angular/core';
104
+ import { Service } from '@angular/core';
101
105
  import { environment } from '../environments/environment';
102
106
 
103
- @Injectable({ providedIn: 'root' })
107
+ @Service()
104
108
  export class TripService {
105
109
  readonly apiUrl = environment.apiUrl;
106
110
  }
107
111
  ```
108
112
 
109
- The Angular CLI replaces `environment.ts` for the selected build
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 development
114
- ng build --configuration staging
115
- ng build --configuration production
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rivinia/env",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Typed application environments for Angular (dev/staging/prod) with an ng-add schematic.",
5
5
  "keywords": [
6
6
  "angular",
@@ -28,8 +28,14 @@ const OUTPUT_DIR = {
28
28
  stage: 'builds/stag',
29
29
  prod: 'builds/prod',
30
30
  };
31
- /** Maps an environment name to its Angular CLI configuration name. */
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/staging swaps (and what `development` uses directly).
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 \`development\` configuration. */\n` +
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 \`staging\` configuration. */\n` +
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
- * development/staging/production configurations with file replacements.
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
- * (matched by replace+with pair) and creates a `staging` configuration
85
- * (copied from `production` when present) if absent.
86
- * - `development` intentionally gets no replacement (CLI standard: it uses
87
- * `environment.ts` directly).
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
- ensureReplacement(tree, angular, name, 'production', {
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; // never create production/development for the user
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 exists = list.some((item) => typeof item === 'object' &&
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
- item['with'] === entry.with);
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 = CONFIG_NAME[env];
232
- const target = configurations[configName];
233
- if (!target || 'outputPath' in target)
234
- continue;
235
- target['outputPath'] = OUTPUT_DIR[env];
236
- touched = true;
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
- const configurations = getConfigurations(angular, name);
255
- if (!configurations['staging']) {
256
- const production = configurations['production'];
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
  }