molex-env 0.3.1 → 0.3.3

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/src/lib/errors.js CHANGED
@@ -1,98 +1,66 @@
1
- 'use strict';
2
-
3
- /**
4
- * Base error type for molex-env.
5
- */
6
- class MenvError extends Error
7
- {
8
- constructor(message, details)
9
- {
10
- super(message);
11
- this.name = 'MenvError';
12
- if (details)
13
- {
14
- this.details = details;
15
- }
16
- }
17
- }
18
-
19
- /**
20
- * Format a file/line suffix for error messages.
21
- * @param {string} file
22
- * @param {number} line
23
- * @returns {string}
24
- */
25
- function formatLocation(file, line)
26
- {
27
- if (!file) return '';
28
- if (!line) return ` (${file})`;
29
- return ` (${file}:${line})`;
30
- }
31
-
32
- /**
33
- * Create an invalid line error.
34
- * @param {number} line
35
- * @param {string} rawLine
36
- * @param {string} file
37
- * @returns {MenvError}
38
- */
39
- function invalidLineError(line, rawLine, file)
40
- {
41
- return new MenvError(`Invalid line ${line}: ${rawLine}${formatLocation(file)}`);
42
- }
43
-
44
- /**
45
- * Create an unknown key error.
46
- * @param {string} key
47
- * @param {string} file
48
- * @param {number} line
49
- * @returns {MenvError}
50
- */
51
- function unknownKeyError(key, file, line)
52
- {
53
- return new MenvError(`Unknown key: ${key}${formatLocation(file, line)}`);
54
- }
55
-
56
- /**
57
- * Create a duplicate key error.
58
- * @param {string} key
59
- * @param {string} file
60
- * @param {number} line
61
- * @returns {MenvError}
62
- */
63
- function duplicateKeyError(key, file, line)
64
- {
65
- return new MenvError(`Duplicate key: ${key}${formatLocation(file, line)}`);
66
- }
67
-
68
- /**
69
- * Create a missing required key error.
70
- * @param {string} key
71
- * @returns {MenvError}
72
- */
73
- function missingRequiredError(key)
74
- {
75
- return new MenvError(`Missing required key: ${key}`);
76
- }
77
-
78
- /**
79
- * Create an invalid type error.
80
- * @param {string} type
81
- * @param {string} raw
82
- * @param {string} file
83
- * @param {number} line
84
- * @returns {MenvError}
85
- */
86
- function invalidTypeError(type, raw, file, line)
87
- {
88
- return new MenvError(`Invalid ${type}: ${raw}${formatLocation(file, line)}`);
89
- }
90
-
91
- module.exports = {
92
- MenvError,
93
- invalidLineError,
94
- unknownKeyError,
95
- duplicateKeyError,
96
- missingRequiredError,
97
- invalidTypeError
98
- };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Base error type for molex-env.
5
+ */
6
+ class MenvError extends Error
7
+ {
8
+ constructor(message, details)
9
+ {
10
+ super(message);
11
+ this.name = 'MenvError';
12
+ if (details) this.details = details;
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Format a file/line suffix for error messages.
18
+ * @param {string} file
19
+ * @param {number} line
20
+ * @returns {string}
21
+ */
22
+ function formatLocation(file, line)
23
+ {
24
+ if (!file) return '';
25
+ if (!line) return ` (${file})`;
26
+ return ` (${file}:${line})`;
27
+ }
28
+
29
+ /** @returns {MenvError} */
30
+ function invalidLineError(line, rawLine, file)
31
+ {
32
+ return new MenvError(`Invalid line ${line}: ${rawLine}${formatLocation(file)}`);
33
+ }
34
+
35
+ /** @returns {MenvError} */
36
+ function unknownKeyError(key, file, line)
37
+ {
38
+ return new MenvError(`Unknown key: ${key}${formatLocation(file, line)}`);
39
+ }
40
+
41
+ /** @returns {MenvError} */
42
+ function duplicateKeyError(key, file, line)
43
+ {
44
+ return new MenvError(`Duplicate key: ${key}${formatLocation(file, line)}`);
45
+ }
46
+
47
+ /** @returns {MenvError} */
48
+ function missingRequiredError(key)
49
+ {
50
+ return new MenvError(`Missing required key: ${key}`);
51
+ }
52
+
53
+ /** @returns {MenvError} */
54
+ function invalidTypeError(type, raw, file, line)
55
+ {
56
+ return new MenvError(`Invalid ${type}: ${raw}${formatLocation(file, line)}`);
57
+ }
58
+
59
+ module.exports = {
60
+ MenvError,
61
+ invalidLineError,
62
+ unknownKeyError,
63
+ duplicateKeyError,
64
+ missingRequiredError,
65
+ invalidTypeError,
66
+ };
package/src/lib/files.js CHANGED
@@ -1,36 +1,35 @@
1
- 'use strict';
2
-
3
- const path = require('path');
4
-
5
- /**
6
- * Resolve .menv file paths based on options.
7
- * @param {{cwd?: string, files?: string[], profile?: string}} options
8
- * @returns {string[]}
9
- */
10
- function resolveFiles(options)
11
- {
12
- const cwd = options.cwd || process.cwd();
13
- if (Array.isArray(options.files) && options.files.length > 0)
14
- {
15
- return options.files.map((file) => (path.isAbsolute(file) ? file : path.join(cwd, file)));
16
- }
17
-
18
- const files = [
19
- path.join(cwd, '.menv'),
20
- path.join(cwd, '.menv.local')
21
- ];
22
-
23
- if (options.profile)
24
- {
25
- files.push(
26
- path.join(cwd, `.menv.${options.profile}`),
27
- path.join(cwd, `.menv.${options.profile}.local`)
28
- );
29
- }
30
-
31
- return files;
32
- }
33
-
34
- module.exports = {
35
- resolveFiles
36
- };
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+
5
+ /**
6
+ * Resolve .menv file paths based on options.
7
+ * @param {{cwd?: string, files?: string[], profile?: string}} options
8
+ * @returns {string[]}
9
+ */
10
+ function resolveFiles(options = {})
11
+ {
12
+ const cwd = options.cwd || process.cwd();
13
+
14
+ if (Array.isArray(options.files) && options.files.length > 0)
15
+ {
16
+ return options.files.map((f) => (path.isAbsolute(f) ? f : path.join(cwd, f)));
17
+ }
18
+
19
+ const files = [
20
+ path.join(cwd, '.menv'),
21
+ path.join(cwd, '.menv.local'),
22
+ ];
23
+
24
+ if (options.profile)
25
+ {
26
+ files.push(
27
+ path.join(cwd, `.menv.${options.profile}`),
28
+ path.join(cwd, `.menv.${options.profile}.local`),
29
+ );
30
+ }
31
+
32
+ return files;
33
+ }
34
+
35
+ module.exports = { resolveFiles };
package/src/lib/parser.js CHANGED
@@ -1,115 +1,96 @@
1
- 'use strict';
2
-
3
- const { invalidLineError } = require('./errors');
4
-
5
- /**
6
- * Strip inline comments while preserving quoted values.
7
- * @param {string} line
8
- * @returns {string}
9
- */
10
- function stripInlineComment(line)
11
- {
12
- let inSingle = false;
13
- let inDouble = false;
14
- let escaped = false;
15
- for (let i = 0; i < line.length; i += 1)
16
- {
17
- const ch = line[i];
18
- if (escaped)
19
- {
20
- escaped = false;
21
- continue;
22
- }
23
- if (ch === '\\')
24
- {
25
- escaped = true;
26
- continue;
27
- }
28
- if (ch === '"' && !inSingle)
29
- {
30
- inDouble = !inDouble;
31
- continue;
32
- }
33
- if (ch === "'" && !inDouble)
34
- {
35
- inSingle = !inSingle;
36
- continue;
37
- }
38
- if (ch === '#' && !inSingle && !inDouble)
39
- {
40
- return line.slice(0, i);
41
- }
42
- }
43
- return line;
44
- }
45
-
46
- /**
47
- * Remove wrapping quotes and unescape common sequences.
48
- * @param {string} value
49
- * @returns {string}
50
- */
51
- function stripQuotes(value)
52
- {
53
- const trimmed = value.trim();
54
- if (trimmed.length >= 2)
55
- {
56
- const first = trimmed[0];
57
- const last = trimmed[trimmed.length - 1];
58
- if ((first === '"' && last === '"') || (first === "'" && last === "'"))
59
- {
60
- return trimmed
61
- .slice(1, -1)
62
- .replace(/\\n/g, '\n')
63
- .replace(/\\r/g, '\r')
64
- .replace(/\\t/g, '\t')
65
- .replace(/\\\\/g, '\\');
66
- }
67
- }
68
- return trimmed;
69
- }
70
-
71
- /**
72
- * Parse raw text into key/value entries.
73
- * @param {string} text
74
- * @param {{strict?: boolean, filePath?: string}} options
75
- * @returns {{key: string, raw: string, line: number}[]}
76
- */
77
- function parseEntries(text, options)
78
- {
79
- const strict = Boolean(options.strict);
80
- const filePath = options.filePath;
81
- const lines = text.split(/\r?\n/);
82
- const entries = [];
83
-
84
- for (let i = 0; i < lines.length; i += 1)
85
- {
86
- const rawLine = lines[i];
87
- const cleaned = stripInlineComment(rawLine);
88
- if (!cleaned.trim()) continue;
89
-
90
- const match = cleaned.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)\s*$/);
91
- if (!match)
92
- {
93
- if (strict)
94
- {
95
- throw invalidLineError(i + 1, rawLine, filePath);
96
- }
97
- continue;
98
- }
99
-
100
- const key = match[1];
101
- const rawValue = stripQuotes(match[2] || '');
102
-
103
- entries.push({
104
- key,
105
- raw: rawValue,
106
- line: i + 1
107
- });
108
- }
109
-
110
- return entries;
111
- }
112
-
113
- module.exports = {
114
- parseEntries
115
- };
1
+ 'use strict';
2
+
3
+ const { invalidLineError } = require('./errors');
4
+
5
+ const KEY_VALUE_RE = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)\s*$/;
6
+
7
+ /**
8
+ * Strip inline comments while preserving quoted values.
9
+ * @param {string} line
10
+ * @returns {string}
11
+ */
12
+ function stripInlineComment(line)
13
+ {
14
+ let inSingle = false;
15
+ let inDouble = false;
16
+ let escaped = false;
17
+
18
+ for (let i = 0; i < line.length; i += 1)
19
+ {
20
+ const ch = line[i];
21
+ if (escaped) { escaped = false; continue; }
22
+ if (ch === '\\') { escaped = true; continue; }
23
+ if (ch === '"' && !inSingle) { inDouble = !inDouble; continue; }
24
+ if (ch === "'" && !inDouble) { inSingle = !inSingle; continue; }
25
+ if (ch === '#' && !inSingle && !inDouble) return line.slice(0, i);
26
+ }
27
+
28
+ return line;
29
+ }
30
+
31
+ /**
32
+ * Remove wrapping quotes and unescape common sequences.
33
+ * @param {string} value
34
+ * @returns {string}
35
+ */
36
+ function stripQuotes(value)
37
+ {
38
+ const trimmed = value.trim();
39
+ if (trimmed.length < 2) return trimmed;
40
+
41
+ const first = trimmed[0];
42
+ const last = trimmed[trimmed.length - 1];
43
+ if ((first === '"' && last === '"') || (first === "'" && last === "'"))
44
+ {
45
+ return trimmed.slice(1, -1).replace(/\\(.)/g, (_, ch) =>
46
+ {
47
+ switch (ch)
48
+ {
49
+ case 'n': return '\n';
50
+ case 'r': return '\r';
51
+ case 't': return '\t';
52
+ case '\\': return '\\';
53
+ default: return ch;
54
+ }
55
+ });
56
+ }
57
+
58
+ return trimmed;
59
+ }
60
+
61
+ /**
62
+ * Parse raw text into key/value entries.
63
+ * @param {string} text
64
+ * @param {{strict?: boolean, filePath?: string}} options
65
+ * @returns {{key: string, raw: string, line: number}[]}
66
+ */
67
+ function parseEntries(text, options = {})
68
+ {
69
+ const { strict, filePath } = options;
70
+ const lines = text.split(/\r?\n/);
71
+ const entries = [];
72
+
73
+ for (let i = 0; i < lines.length; i += 1)
74
+ {
75
+ const rawLine = lines[i];
76
+ const cleaned = stripInlineComment(rawLine).trim();
77
+ if (!cleaned) continue;
78
+
79
+ const match = cleaned.match(KEY_VALUE_RE);
80
+ if (!match)
81
+ {
82
+ if (strict) throw invalidLineError(i + 1, rawLine, filePath);
83
+ continue;
84
+ }
85
+
86
+ entries.push({
87
+ key: match[1],
88
+ raw: stripQuotes(match[2] || ''),
89
+ line: i + 1,
90
+ });
91
+ }
92
+
93
+ return entries;
94
+ }
95
+
96
+ module.exports = { parseEntries };
package/src/lib/schema.js CHANGED
@@ -1,59 +1,48 @@
1
- 'use strict';
2
-
3
- const { missingRequiredError } = require('./errors');
4
-
5
- /**
6
- * Normalize schema definitions into objects.
7
- * @param {object} schema
8
- * @returns {object|null}
9
- */
10
- function normalizeSchema(schema)
11
- {
12
- if (!schema) return null;
13
- const normalized = {};
14
- for (const key of Object.keys(schema))
15
- {
16
- const def = schema[key];
17
- if (typeof def === 'string')
18
- {
19
- normalized[key] = { type: def };
20
- } else if (def && typeof def === 'object')
21
- {
22
- normalized[key] = { ...def };
23
- }
24
- }
25
- return normalized;
26
- }
27
-
28
- /**
29
- * Apply defaults and required checks from schema.
30
- * @param {object} values
31
- * @param {object} origins
32
- * @param {object|null} schema
33
- * @param {boolean} strict
34
- * @returns {void}
35
- */
36
- function applySchemaDefaults(values, origins, schema, strict)
37
- {
38
- if (!schema) return;
39
- for (const key of Object.keys(schema))
40
- {
41
- const def = schema[key];
42
- if (values[key] === undefined)
43
- {
44
- if (def && Object.prototype.hasOwnProperty.call(def, 'default'))
45
- {
46
- values[key] = def.default;
47
- origins[key] = { file: '<default>', line: 0, raw: undefined };
48
- } else if (strict && def && def.required)
49
- {
50
- throw missingRequiredError(key);
51
- }
52
- }
53
- }
54
- }
55
-
56
- module.exports = {
57
- normalizeSchema,
58
- applySchemaDefaults
59
- };
1
+ 'use strict';
2
+
3
+ const { missingRequiredError } = require('./errors');
4
+
5
+ /**
6
+ * Normalize schema definitions into consistent objects.
7
+ * @param {object} schema
8
+ * @returns {object|null}
9
+ */
10
+ function normalizeSchema(schema)
11
+ {
12
+ if (!schema) return null;
13
+ const normalized = {};
14
+ for (const key of Object.keys(schema))
15
+ {
16
+ const def = schema[key];
17
+ normalized[key] = typeof def === 'string' ? { type: def } : { ...def };
18
+ }
19
+ return normalized;
20
+ }
21
+
22
+ /**
23
+ * Apply defaults and enforce required keys from schema.
24
+ * @param {object} values
25
+ * @param {object} origins
26
+ * @param {object|null} schema
27
+ * @param {boolean} strict
28
+ */
29
+ function applySchemaDefaults(values, origins, schema, strict)
30
+ {
31
+ if (!schema) return;
32
+ for (const key of Object.keys(schema))
33
+ {
34
+ const def = schema[key];
35
+ if (values[key] !== undefined) continue;
36
+
37
+ if (def && Object.prototype.hasOwnProperty.call(def, 'default'))
38
+ {
39
+ values[key] = def.default;
40
+ origins[key] = { file: '<default>', line: 0, raw: undefined };
41
+ } else if (strict && def && def.required)
42
+ {
43
+ throw missingRequiredError(key);
44
+ }
45
+ }
46
+ }
47
+
48
+ module.exports = { normalizeSchema, applySchemaDefaults };
package/src/lib/utils.js CHANGED
@@ -1,21 +1,19 @@
1
- 'use strict';
2
-
3
- /**
4
- * Recursively freeze an object graph.
5
- * @param {object} obj
6
- * @returns {object}
7
- */
8
- function deepFreeze(obj)
9
- {
10
- if (!obj || typeof obj !== 'object' || Object.isFrozen(obj)) return obj;
11
- Object.freeze(obj);
12
- for (const key of Object.keys(obj))
13
- {
14
- deepFreeze(obj[key]);
15
- }
16
- return obj;
17
- }
18
-
19
- module.exports = {
20
- deepFreeze
21
- };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Recursively freeze an object graph.
5
+ * @param {object} obj
6
+ * @returns {object}
7
+ */
8
+ function deepFreeze(obj)
9
+ {
10
+ if (!obj || typeof obj !== 'object' || Object.isFrozen(obj)) return obj;
11
+ Object.freeze(obj);
12
+ for (const key of Object.keys(obj))
13
+ {
14
+ deepFreeze(obj[key]);
15
+ }
16
+ return obj;
17
+ }
18
+
19
+ module.exports = { deepFreeze };