@philkrpp/phase-loader 0.1.0 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +5 -3
  2. package/dist/cli.js +17 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -89,9 +89,11 @@ phase-loader check --offline # .env.example vs env reads in git-tracked files
89
89
  phase-loader check # additionally vs the key names in Phase
90
90
  ```
91
91
 
92
- `check` recognises `process.env.X`, `process.env["X"]`, `import.meta.env.X`, `os.environ["X"]` and `os.getenv("X")`
93
- in tracked `.js/.ts/.vue/.py` files (not under `node_modules`), ignores runtime built-ins such as `NODE_ENV`, prints
94
- names only and exits 1 on any undocumented or orphan name. The personal app is not part of `.env.example`.
92
+ `check` recognises `process.env.X`, `process.env["X"]`, `import.meta.env.X`, `os.environ["X"]`, `os.environ.get("X")`
93
+ and `os.getenv("X")` in tracked `.js/.ts/.vue/.py` files (not under `node_modules`), ignores runtime built-ins such as
94
+ `NODE_ENV`, prints names only and exits 1 on any undocumented or orphan name. A name from `.env.example` that appears
95
+ quoted anywhere in that code (`optional('KEY')`, `env("KEY")`) counts as read, so env helpers cause no orphans; a new
96
+ key read only through such a helper is not reported as undocumented. The personal app is not part of `.env.example`.
95
97
 
96
98
  ## API
97
99
 
package/dist/cli.js CHANGED
@@ -54,8 +54,16 @@ const USAGE$1 = [
54
54
  /process\.env\[\s*["']([A-Za-z_][A-Za-z0-9_]*)["']\s*\]/g,
55
55
  /import\.meta\.env\.([A-Za-z_][A-Za-z0-9_]*)/g,
56
56
  /os\.environ\[\s*["']([A-Za-z_][A-Za-z0-9_]*)["']\s*\]/g,
57
- /os\.getenv\(\s*["']([A-Za-z_][A-Za-z0-9_]*)["']/g
57
+ /os\.getenv\(\s*["']([A-Za-z_][A-Za-z0-9_]*)["']/g,
58
+ /os\.environ\.get\(\s*["']([A-Za-z_][A-Za-z0-9_]*)["']/g
58
59
  ];
60
+ /**
61
+ * A documented name quoted anywhere in tracked code (`optional('KEY')`, `env("KEY")`, a list of names) counts as
62
+ * read, so helpers around `process.env[name]` do not produce orphans.
63
+ * shortcut: undocumented detection still sees only the USAGE patterns, so a new key read only through a helper is
64
+ * not reported; upgrade with a configurable helper list if that bites.
65
+ */
66
+ const QUOTED = /["'`]([A-Z_][A-Z0-9_]*)["'`]/g;
59
67
  /** Provided by the runtime or by phase-loader itself, never documented in `.env.example`. */
60
68
  const BUILT_IN = /* @__PURE__ */ new Set([
61
69
  "NODE_ENV",
@@ -84,6 +92,7 @@ function trackedFiles(dir) {
84
92
  }
85
93
  function usedNames(dir) {
86
94
  const used = /* @__PURE__ */ new Set();
95
+ const quoted = /* @__PURE__ */ new Set();
87
96
  for (const file of trackedFiles(dir)) {
88
97
  if (!SOURCE.test(file) || file.split("/").includes("node_modules")) continue;
89
98
  let text;
@@ -93,8 +102,12 @@ function usedNames(dir) {
93
102
  continue;
94
103
  }
95
104
  for (const pattern of USAGE$1) for (const m of text.matchAll(pattern)) if (!BUILT_IN.has(m[1])) used.add(m[1]);
105
+ for (const m of text.matchAll(QUOTED)) quoted.add(m[1]);
96
106
  }
97
- return used;
107
+ return {
108
+ used,
109
+ quoted
110
+ };
98
111
  }
99
112
  /**
100
113
  * Offline: names in `.env.example` against env reads in tracked source files (undocumented / orphan).
@@ -105,8 +118,8 @@ async function check(opts = {}) {
105
118
  const file = join(dir, EXAMPLE_FILE);
106
119
  if (!existsSync(file)) throw new PhaseLoaderError(`${EXAMPLE_FILE} not found in ${dir} (create it with "phase-loader example --write").`);
107
120
  const documented = new Set(exampleNames(readFileSync(file, "utf8")));
108
- const used = usedNames(dir);
109
- const findings = [[`Used in code but undocumented in ${EXAMPLE_FILE}:`, [...used].filter((n) => !documented.has(n))], [`Orphan in ${EXAMPLE_FILE} (never read in tracked code):`, [...documented].filter((n) => !used.has(n))]];
121
+ const { used, quoted } = usedNames(dir);
122
+ const findings = [[`Used in code but undocumented in ${EXAMPLE_FILE}:`, [...used].filter((n) => !documented.has(n))], [`Orphan in ${EXAMPLE_FILE} (never read in tracked code):`, [...documented].filter((n) => !used.has(n) && !quoted.has(n))]];
110
123
  if (!opts.offline) {
111
124
  const inPhase = new Set((await phaseNames(opts.cwd ?? process.cwd())).sections.flatMap((s) => s.names));
112
125
  findings.push([`In ${EXAMPLE_FILE} but not in Phase:`, [...documented].filter((n) => !inPhase.has(n))], [`In Phase but not in ${EXAMPLE_FILE}:`, [...inPhase].filter((n) => !documented.has(n))]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@philkrpp/phase-loader",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Load env values from a self-hosted Phase secrets manager into a child process or the current process, with layered apps/paths and OS keyring tokens.",
5
5
  "license": "MIT",
6
6
  "repository": {