configre 1.2.2 → 1.3.0

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
@@ -38,13 +38,25 @@ To get started with Configre, follow these steps:
38
38
 
39
39
  3. **Use Configre in Your Project**
40
40
 
41
- Import and use Configre to load your configurations:
41
+ Import and use Configre to load your configurations. The configuration path is required:
42
42
 
43
43
  ```javascript
44
- const cfg = require("Configre")(); // default dir '/config'
44
+ const path = require("path");
45
+ const cfg = require("configre")(path.join(__dirname, "config"));
45
46
  console.log(cfg.db); // Access your db configuration
46
47
  ```
47
48
 
49
+ Prefer an absolute path anchored to the module, as above. Avoid deriving it from `process.cwd()` (for example, `path.join(process.cwd(), "config")`), because that can point to a different location depending on where the process is started.
50
+
51
+ In ESM, anchor the path to `import.meta.dirname`:
52
+
53
+ ```javascript
54
+ import Configre from "configre";
55
+ import { join } from "node:path";
56
+
57
+ const cfg = Configre(join(import.meta.dirname, "config"));
58
+ ```
59
+
48
60
  ## 📚 Example
49
61
 
50
62
  Imagine you have the following structure in your `config` directory:
@@ -114,4 +126,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
114
126
 
115
127
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
116
128
 
117
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
129
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
File without changes
File without changes
@@ -1,12 +1,8 @@
1
1
  import Configre from "../../index.js";
2
2
  import lemonlog from "lemonlog";
3
- import { fileURLToPath } from "url";
4
- import { dirname, join } from "path";
3
+ import { join } from "node:path";
5
4
 
6
5
  const log = lemonlog("Configre");
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = dirname(__filename);
9
6
 
10
- const cfg = Configre(join(__dirname, "config"));
7
+ const cfg = Configre(join(import.meta.dirname, "config"));
11
8
  log.info(cfg.db);
12
-
package/index.js CHANGED
@@ -1,11 +1,15 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
3
  const os = require("os");
4
- const obj = require("lodash/object");
4
+ const obj = require("./merge");
5
5
  const log = require("lemonlog")("Configre");
6
6
 
7
7
  class ConfigreClass {
8
- constructor(pathOrDir = __dirname + "/../../config") {
8
+ constructor(pathOrDir) {
9
+ if (typeof pathOrDir !== "string" || pathOrDir.length === 0) {
10
+ throw new TypeError("Configre path must be a non-empty string");
11
+ }
12
+
9
13
  const dir = path.join(pathOrDir);
10
14
  const isNested = (ConfigreClass._nesting || 0) > 0;
11
15
  ConfigreClass._nesting = (ConfigreClass._nesting || 0) + 1;
package/merge.js ADDED
@@ -0,0 +1,81 @@
1
+ function isPlainObject(value) {
2
+ if (value === null || typeof value !== "object") {
3
+ return false;
4
+ }
5
+ const proto = Object.getPrototypeOf(value);
6
+ return proto === Object.prototype || proto === null;
7
+ }
8
+
9
+ function isUnsafeKey(key) {
10
+ return key === "__proto__" || key === "constructor" || key === "prototype";
11
+ }
12
+
13
+ function mergeArray(target, source) {
14
+ const result = target.slice();
15
+
16
+ for (let index = 0; index < source.length; index += 1) {
17
+ const srcValue = source[index];
18
+ const objValue = result[index];
19
+
20
+ if (Array.isArray(srcValue)) {
21
+ result[index] = mergeArray(Array.isArray(objValue) ? objValue : [], srcValue);
22
+ continue;
23
+ }
24
+
25
+ if (isPlainObject(srcValue)) {
26
+ result[index] = baseMerge(isPlainObject(objValue) ? objValue : {}, srcValue);
27
+ continue;
28
+ }
29
+
30
+ if (srcValue !== undefined || result[index] === undefined) {
31
+ result[index] = srcValue;
32
+ }
33
+ }
34
+
35
+ return result;
36
+ }
37
+
38
+ function baseMerge(object, source) {
39
+ if (object === source || source === null || typeof source !== "object") {
40
+ return object;
41
+ }
42
+
43
+ for (const key in source) {
44
+ if (isUnsafeKey(key)) {
45
+ continue;
46
+ }
47
+
48
+ const srcValue = source[key];
49
+ const objValue = object[key];
50
+
51
+ if (Array.isArray(srcValue)) {
52
+ object[key] = mergeArray(Array.isArray(objValue) ? objValue : [], srcValue);
53
+ continue;
54
+ }
55
+
56
+ if (isPlainObject(srcValue)) {
57
+ object[key] = baseMerge(isPlainObject(objValue) ? objValue : {}, srcValue);
58
+ continue;
59
+ }
60
+
61
+ if (srcValue !== undefined || object[key] === undefined) {
62
+ object[key] = srcValue;
63
+ }
64
+ }
65
+
66
+ return object;
67
+ }
68
+
69
+ function merge(object, ...sources) {
70
+ const output = (object !== null && typeof object === "object") ? object : {};
71
+
72
+ for (const source of sources) {
73
+ baseMerge(output, source);
74
+ }
75
+
76
+ return output;
77
+ }
78
+
79
+ module.exports = {
80
+ merge
81
+ };
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "configre",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "🔧 Effortlessly Tailor Your Settings",
5
5
  "dependencies": {
6
- "lemonlog": "^1.0.2",
7
- "lodash": "^4.17.21"
6
+ "lemonlog": "^1.2.2"
8
7
  },
9
8
  "main": "index.js",
10
9
  "keywords": [
@@ -33,5 +32,8 @@
33
32
  "bugs": {
34
33
  "url": "https://github.com/clasen/Configre/issues"
35
34
  },
36
- "homepage": "https://github.com/clasen/Configre#readme"
35
+ "homepage": "https://github.com/clasen/Configre#readme",
36
+ "scripts": {
37
+ "test": "node --test"
38
+ }
37
39
  }
@@ -63,13 +63,16 @@ module.exports = {
63
63
  ### Step 5: Load the configuration
64
64
 
65
65
  ```javascript
66
- const cfg = require("configre")();
66
+ const path = require("path");
67
+ const cfg = require("configre")(path.join(__dirname, "config"));
67
68
 
68
69
  console.log(cfg.db.host); // from default
69
70
  console.log(cfg.db.user); // from host override
70
71
  ```
71
72
 
72
- To use a custom config directory:
73
+ The path argument is required. Prefer an absolute path anchored to the module. Do not recommend paths derived from `process.cwd()`, such as `path.join(process.cwd(), "config")`: they can point somewhere else when the process is launched from a different directory.
74
+
75
+ To use a different config directory:
73
76
 
74
77
  ```javascript
75
78
  const cfg = require("configre")(__dirname + "/settings");
@@ -96,7 +99,7 @@ Using `--config=` (instead of a positional argument) avoids conflicts with other
96
99
 
97
100
  **Example 1: Basic setup**
98
101
  User says: "Add configuration management to my Node.js project"
99
- Actions: install configre, create `config/index.cjs` with project defaults, load with `require("configre")()`
102
+ Actions: install configre, create `config/index.cjs` with project defaults, load with `require("configre")(path.join(__dirname, "config"))`
100
103
  Result: merged config object ready to use
101
104
 
102
105
  **Example 2: Multi-environment**
@@ -113,4 +116,5 @@ Result: staging overrides are merged over defaults, without conflicting with oth
113
116
 
114
117
  - **Deep merge**: nested objects merge recursively via lodash `_.merge`
115
118
  - **Config files must use the `.cjs` extension** (`.js` is not accepted; works in both CommonJS and ESM projects)
119
+ - **Required path**: always pass the config directory or file path; prefer an absolute module-relative path over a process-relative path
116
120
  - **Function vs constructor**: `Configre(path)` returns the merged config directly; `new Configre(path)` returns the instance (use `.get()` to retrieve config)
@@ -0,0 +1,28 @@
1
+ const assert = require("node:assert/strict");
2
+ const path = require("node:path");
3
+ const test = require("node:test");
4
+ const Configre = require("../index");
5
+
6
+ test("requires a config path", () => {
7
+ assert.throws(
8
+ () => Configre(),
9
+ {
10
+ name: "TypeError",
11
+ message: "Configre path must be a non-empty string"
12
+ }
13
+ );
14
+ assert.throws(
15
+ () => new Configre(""),
16
+ {
17
+ name: "TypeError",
18
+ message: "Configre path must be a non-empty string"
19
+ }
20
+ );
21
+ });
22
+
23
+ test("loads config from an explicit module-relative path", () => {
24
+ const configPath = path.join(__dirname, "..", "demo", "config");
25
+ const config = Configre(configPath);
26
+
27
+ assert.equal(config.db.host, "localhost");
28
+ });