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/README.md +25 -14
- package/index.d.ts +134 -0
- package/package.json +46 -35
- package/src/index.js +27 -26
- package/src/lib/apply.js +61 -75
- package/src/lib/cast.js +133 -124
- package/src/lib/core.js +161 -148
- package/src/lib/errors.js +66 -98
- package/src/lib/files.js +35 -36
- package/src/lib/parser.js +96 -115
- package/src/lib/schema.js +48 -59
- package/src/lib/utils.js +19 -21
- package/src/lib/watch.js +128 -110
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
path.join(cwd, '.menv
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
path.join(cwd, `.menv.${options.profile}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
let
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
if (ch === '
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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 };
|