@rivinia/env 0.2.0 → 0.3.1

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
@@ -3,50 +3,49 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@rivinia/env.svg)](https://www.npmjs.com/package/@rivinia/env)
4
4
  [![license](https://img.shields.io/npm/l/@rivinia/env.svg)](https://www.npmjs.com/package/@rivinia/env)
5
5
 
6
- Typed application environments for Angular: dev/staging/prod file layout, a
7
- `BaseEnvironment` contract, and an `ng-add` schematic that scaffolds the
8
- files and wires the build configurations. Zero runtime code — environments are
9
- build-time constants; this package ships types plus setup tooling only.
6
+ `@rivinia/env` provides the `BaseEnvironment` TypeScript contract and an
7
+ Angular CLI `ng add` schematic. It does not read environment values at
8
+ runtime: Angular's build file replacements select a statically imported
9
+ environment file.
10
10
 
11
- - Version: **0.2.0**
12
- - Requires: nothing at runtime (types only). Schematic runs under Angular CLI 22+.
11
+ - Prepared patch: **0.3.1** (latest published: **0.3.0**)
12
+ - No runtime providers or services; the package supplies a type and CLI tooling.
13
+ - The generated model imports `BaseEnvironment` from this package.
13
14
 
14
- ## Install
15
+ ## Install and scaffold
16
+
17
+ From an Angular CLI workspace:
15
18
 
16
19
  ```bash
17
20
  npm install @rivinia/env
18
- ng add @rivinia/env # multi-select: development / staging / production
21
+ ng add @rivinia/env
19
22
  ```
20
23
 
21
- Interactive runs ask which environments to scaffold (checkbox, all checked by
22
- default); non-interactive runs take all. From flags, comma-separate (short
23
- aliases work: `dev`, `prod`, `stag`):
24
+ The schematic prompts for environments when interactive; the default and
25
+ non-interactive selection is all three (`dev`, `stage`, `prod`). Select only
26
+ what you need with a comma-separated option:
24
27
 
25
28
  ```bash
26
- ng add @rivinia/env --environments staging,production
29
+ ng add @rivinia/env --environments stage,prod
27
30
  ```
28
31
 
29
- Only the selection is scaffolded and wired (model + base file always — the
30
- base is the replacement source). Each wired configuration gets its output
31
- directory (`builds/dev`, `builds/stag`, `builds/prod`) unless it already
32
- defines one, and `/builds` is appended to `.gitignore` once. Re-runs never
33
- overwrite files or duplicate configuration.
32
+ Accepted names are `dev`, `stage` (or `stag`), and `prod`. `--project <name>`
33
+ selects a project in a multi-project workspace; otherwise it uses
34
+ `defaultProject` from `angular.json`, falling back to the first project.
35
+ The schematic uses that project's `sourceRoot` (or `<root>/src`, then `src`).
34
36
 
35
- `ng add` creates (never overwrites) `src/environments/`:
37
+ The model and development base file are always scaffolded. The selected
38
+ environment files are created only if they do not already exist:
36
39
 
40
+ ```text
41
+ src/environments/
42
+ environment.model.ts # AppEnvironment extends BaseEnvironment
43
+ environment.ts # development defaults; not replaced by the CLI
44
+ environment.staging.ts # only when stage is selected
45
+ environment.prod.ts # only when prod is selected
37
46
  ```
38
- environment.model.ts # AppEnvironment extends BaseEnvironment — extend it
39
- environment.ts # development defaults (used as-is by `development`)
40
- environment.staging.ts # staging values (production: true)
41
- environment.prod.ts # production values
42
- ```
43
-
44
- and wires `angular.json`: `production` gains its replacement if missing, and a
45
- `staging` configuration (copied from `production`) is created if absent.
46
- `development` intentionally gets no replacement (CLI standard). The app version
47
- is snapshotted from your `package.json` into each file (`0.0.0` fallback).
48
47
 
49
- ## Usage pattern (app-owned)
48
+ The generated contract and values have this shape:
50
49
 
51
50
  ```ts
52
51
  // environment.model.ts
@@ -55,14 +54,77 @@ import type { BaseEnvironment } from '@rivinia/env';
55
54
  export interface AppEnvironment extends BaseEnvironment {
56
55
  readonly apiUrl: string;
57
56
  }
57
+ ```
58
+
59
+ ```ts
60
+ // environment.ts (generated development defaults)
61
+ import type { AppEnvironment } from './environment.model';
62
+
63
+ export const environment: AppEnvironment = {
64
+ production: false,
65
+ version: '1.2.3',
66
+ apiUrl: 'http://localhost:3000/api',
67
+ };
68
+ ```
69
+
70
+ Staging and production files have the same fields, with `production: true`;
71
+ their generated API URLs are `https://staging.example.com/api` and
72
+ `https://api.example.com/api`. Replace these example URLs and any other
73
+ generated values with the app's real configuration.
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.
81
+
82
+ For selected configurations that exist, it sets `outputPath` to
83
+ `builds/dev`, `builds/stag`, or `builds/prod` only if no output path is
84
+ already configured. If that adds an output path and a root `.gitignore`
85
+ exists, `/builds` is appended once. Existing output paths and files are never
86
+ overwritten; repeated runs do not duplicate replacements.
87
+
88
+ The version in generated files is copied from the workspace-root
89
+ `package.json` when each file is first created (`0.0.0` if unavailable).
90
+ Since the schematic never overwrites existing files, update `version` in
91
+ those environment files manually when releasing a new app version; rerunning
92
+ `ng add` will not refresh it.
58
93
 
59
- // anywhere:
94
+ ## Use the selected environment
95
+
96
+ Always import the base path from the app, not a stage/prod-specific file:
97
+
98
+ ```ts
99
+ // src/app/trip.service.ts
100
+ import { Injectable } from '@angular/core';
60
101
  import { environment } from '../environments/environment';
61
- fetch(`${environment.apiUrl}/trips`);
62
- console.log(environment.version); // e.g. footer, about page, cache keys
102
+
103
+ @Injectable({ providedIn: 'root' })
104
+ export class TripService {
105
+ readonly apiUrl = environment.apiUrl;
106
+ }
63
107
  ```
64
108
 
65
- Re-run the schematic (or edit by hand) when the app version changes.
109
+ The Angular CLI replaces `environment.ts` for the selected build
110
+ configuration. For example:
111
+
112
+ ```bash
113
+ ng build --configuration development
114
+ ng build --configuration staging
115
+ ng build --configuration production
116
+ ```
117
+
118
+ The generated files are compile-time configuration, not secrets: values
119
+ included in a browser build are visible to its users. `BaseEnvironment`
120
+ requires only readonly `production: boolean` and `version: string`; extend
121
+ AppEnvironment with the fields your app needs.
122
+
123
+ ## Public API
124
+
125
+ | Export | Kind | Purpose |
126
+ | ----------------- | ---- | ---------------------------------------------------------------------- |
127
+ | `BaseEnvironment` | type | Required `production` and `version` fields for app environment models. |
66
128
 
67
129
  ## License
68
130
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rivinia/env",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Typed application environments for Angular (dev/staging/prod) with an ng-add schematic.",
5
5
  "keywords": [
6
6
  "angular",
@@ -2,7 +2,7 @@
2
2
  "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
3
3
  "schematics": {
4
4
  "ng-add": {
5
- "description": "Scaffold typed environment files and wire dev/staging/production configurations.",
5
+ "description": "Scaffold typed environment files and wire dev/stage/prod configurations.",
6
6
  "factory": "./ng-add/index.cjs#ngAdd",
7
7
  "schema": "./ng-add/schema.json"
8
8
  }
@@ -14,19 +14,25 @@ export interface AppEnvironment extends BaseEnvironment {
14
14
  readonly apiUrl: string;
15
15
  }
16
16
  `;
17
- const ALL_ENVIRONMENTS = ['development', 'staging', 'production'];
17
+ const ALL_ENVIRONMENTS = ['dev', 'stage', 'prod'];
18
18
  /** Short aliases users actually type (`prod`, `dev`, `stag`). */
19
19
  const ENVIRONMENT_ALIASES = {
20
- dev: 'development',
21
- prod: 'production',
22
- stag: 'staging',
23
- stage: 'staging',
20
+ dev: 'dev',
21
+ prod: 'prod',
22
+ stag: 'stage',
23
+ stage: 'stage',
24
24
  };
25
25
  /** Output directory short names: builds/dev, builds/prod, builds/stag. */
26
26
  const OUTPUT_DIR = {
27
- development: 'builds/dev',
28
- staging: 'builds/stag',
29
- production: 'builds/prod',
27
+ dev: 'builds/dev',
28
+ stage: 'builds/stag',
29
+ prod: 'builds/prod',
30
+ };
31
+ /** Maps an environment name to its Angular CLI configuration name. */
32
+ const CONFIG_NAME = {
33
+ dev: 'development',
34
+ stage: 'staging',
35
+ prod: 'production',
30
36
  };
31
37
  const fileDefs = (sourceRoot, selected) => {
32
38
  const files = [
@@ -44,7 +50,7 @@ const fileDefs = (sourceRoot, selected) => {
44
50
  `};\n`,
45
51
  },
46
52
  ];
47
- if (selected.includes('staging')) {
53
+ if (selected.includes('stage')) {
48
54
  files.push({
49
55
  name: `${sourceRoot}/environments/environment.staging.ts`,
50
56
  content: (version) => `import type { AppEnvironment } from './environment.model';\n\n` +
@@ -56,7 +62,7 @@ const fileDefs = (sourceRoot, selected) => {
56
62
  `};\n`,
57
63
  });
58
64
  }
59
- if (selected.includes('production')) {
65
+ if (selected.includes('prod')) {
60
66
  files.push({
61
67
  name: `${sourceRoot}/environments/environment.prod.ts`,
62
68
  content: (version) => `import type { AppEnvironment } from './environment.model';\n\n` +
@@ -105,11 +111,9 @@ function ngAdd(options) {
105
111
  const unknown = [...new Set(cleaned)].filter((e) => !ALL_ENVIRONMENTS.includes(e));
106
112
  if (unknown.length > 0) {
107
113
  throw new schematics_1.SchematicsException(`[rivinia/env] unknown environment(s): ${unknown.join(', ')}. ` +
108
- `Choose from development (dev), staging (stag), production (prod).`);
114
+ `Choose from dev, stage (stag), prod.`);
109
115
  }
110
- const selected = cleaned.length > 0
111
- ? [...new Set(cleaned)]
112
- : [...ALL_ENVIRONMENTS];
116
+ const selected = cleaned.length > 0 ? [...new Set(cleaned)] : [...ALL_ENVIRONMENTS];
113
117
  const configsSnapshot = () => JSON.stringify(project.architect?.build?.configurations ?? null);
114
118
  const configsBefore = configsSnapshot();
115
119
  let created = 0;
@@ -122,13 +126,13 @@ function ngAdd(options) {
122
126
  tree.create(path, file.content(version));
123
127
  created++;
124
128
  }
125
- if (selected.includes('production')) {
129
+ if (selected.includes('prod')) {
126
130
  ensureReplacement(tree, angular, name, 'production', {
127
131
  replace: `${sourceRoot}/environments/environment.ts`,
128
132
  with: `${sourceRoot}/environments/environment.prod.ts`,
129
133
  });
130
134
  }
131
- if (selected.includes('staging')) {
135
+ if (selected.includes('stage')) {
132
136
  ensureStaging(tree, angular, name, sourceRoot, context);
133
137
  }
134
138
  ensureOutputPaths(tree, angular, name, selected, context);
@@ -224,7 +228,8 @@ function ensureOutputPaths(tree, angular, name, selected, context) {
224
228
  const configurations = getConfigurations(angular, name);
225
229
  let touched = false;
226
230
  for (const env of selected) {
227
- const target = configurations[env];
231
+ const configName = CONFIG_NAME[env];
232
+ const target = configurations[configName];
228
233
  if (!target || 'outputPath' in target)
229
234
  continue;
230
235
  target['outputPath'] = OUTPUT_DIR[env];
@@ -6,23 +6,35 @@
6
6
  "properties": {
7
7
  "environments": {
8
8
  "type": ["array", "string"],
9
- "description": "Which environments to scaffold and wire: array, or comma-separated string on the CLI (--environments staging,production). Model + base file always. Omit for all three.",
10
- "default": ["development", "staging", "production"],
11
- "items": { "type": "string", "enum": ["development", "staging", "production", "dev", "prod", "stag", "stage"] },
9
+ "description": "Which environments to scaffold and wire: array, or comma-separated string on the CLI (--environments stage,prod). Model + base file always. Omit for all three.",
10
+ "default": ["dev", "stage", "prod"],
11
+ "items": {
12
+ "type": "string",
13
+ "enum": ["dev", "stage", "prod", "stag"]
14
+ },
12
15
  "x-prompt": {
13
16
  "message": "Which environments should ng-add scaffold and wire?",
14
17
  "type": "list",
15
18
  "multiselect": true,
16
19
  "items": [
17
- { "value": "development", "label": "development — base file, no replacement" },
18
- { "value": "staging", "label": "staging — staging file + configuration" },
19
- { "value": "production", "label": "production — prod file + replacement" }
20
+ {
21
+ "value": "dev",
22
+ "label": "dev \u2014 base file, no replacement"
23
+ },
24
+ {
25
+ "value": "stage",
26
+ "label": "stage \u2014 staging file + configuration"
27
+ },
28
+ {
29
+ "value": "prod",
30
+ "label": "prod \u2014 prod file + replacement"
31
+ }
20
32
  ]
21
33
  }
22
34
  },
23
35
  "project": {
24
36
  "type": "string",
25
- "description": "Angular CLI project name — only for multi-app workspaces. Defaults to the workspace default project.",
37
+ "description": "Angular CLI project name \u2014 only for multi-app workspaces. Defaults to the workspace default project.",
26
38
  "default": ""
27
39
  }
28
40
  },