@reliverse/rempts-plugin-config 2.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 ADDED
@@ -0,0 +1,174 @@
1
+ # rempts-plugin-config
2
+
3
+ Configuration file merger plugin for Rempts CLI framework. Loads and merges configuration from multiple sources including user home directory and project-specific config files.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add rempts-plugin-config
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { createCLI } from '@reliverse/rempts-core'
15
+ import { configMergerPlugin } from '@reliverse/rempts-plugin-config'
16
+
17
+ const cli = await createCLI({
18
+ name: 'my-cli',
19
+ version: '1.0.0',
20
+ plugins: [
21
+ configMergerPlugin({
22
+ sources: [
23
+ '~/.config/{{name}}/config.json',
24
+ '.{{name}}rc.json',
25
+ '.{{name}}rc',
26
+ 'package.json'
27
+ ]
28
+ })
29
+ ]
30
+ })
31
+
32
+ // Config is automatically merged into your CLI configuration
33
+ ```
34
+
35
+ ## Options
36
+
37
+ ```typescript
38
+ interface ConfigMergerOptions {
39
+ /**
40
+ * List of config file paths to load
41
+ * Supports {{name}} template which is replaced with CLI name
42
+ * Paths starting with ~ are expanded to home directory
43
+ */
44
+ sources: string[]
45
+
46
+ /**
47
+ * Merge strategy for combining configs
48
+ * - 'deep': Recursively merge objects (default)
49
+ * - 'shallow': Only merge top-level properties
50
+ */
51
+ mergeStrategy?: 'deep' | 'shallow'
52
+
53
+ /**
54
+ * Stop after finding the first config file
55
+ * Default: false (loads and merges all found configs)
56
+ */
57
+ stopOnFirst?: boolean
58
+
59
+ /**
60
+ * Custom config parser (e.g., for YAML, TOML)
61
+ * Default: JSON.parse
62
+ */
63
+ parser?: (content: string) => any
64
+
65
+ /**
66
+ * Transform config after loading
67
+ */
68
+ transform?: (config: any) => any
69
+ }
70
+ ```
71
+
72
+ ## Config File Formats
73
+
74
+ By default, the plugin supports JSON files. Common patterns:
75
+
76
+ ### RC Files
77
+
78
+ ```bash
79
+ # These are equivalent for a CLI named "my-cli"
80
+ .my-clirc
81
+ .my-clirc.json
82
+ ```
83
+
84
+ ### Home Directory Config
85
+
86
+ ```bash
87
+ ~/.config/my-cli/config.json
88
+ ~/.my-clirc
89
+ ```
90
+
91
+ ### Package.json
92
+
93
+ ```json
94
+ {
95
+ "name": "my-project",
96
+ "version": "1.0.0",
97
+ "my-cli": {
98
+ "apiKey": "secret",
99
+ "theme": "dark"
100
+ }
101
+ }
102
+ ```
103
+
104
+ ## Template Variables
105
+
106
+ - `{{name}}` - Replaced with your CLI's name
107
+
108
+ ## Load Order
109
+
110
+ Configs are loaded in the order specified and merged together. Later configs override earlier ones.
111
+
112
+ ```typescript
113
+ // Example: User config overrides defaults
114
+ configMergerPlugin({
115
+ sources: [
116
+ '/etc/my-cli/defaults.json', // System defaults
117
+ '~/.config/my-cli/config.json', // User config
118
+ '.my-clirc' // Project config
119
+ ]
120
+ })
121
+ ```
122
+
123
+ ## Stop on First
124
+
125
+ Use `stopOnFirst` to implement fallback behavior:
126
+
127
+ ```typescript
128
+ configMergerPlugin({
129
+ sources: [
130
+ '.my-clirc', // Check project first
131
+ '~/.config/my-cli/config.json', // Then user
132
+ '/etc/my-cli/defaults.json' // Finally system
133
+ ],
134
+ stopOnFirst: true // Use only the first found
135
+ })
136
+ ```
137
+
138
+ ## Custom Parsers
139
+
140
+ Support other formats with custom parsers:
141
+
142
+ ```typescript
143
+ import { parse as parseYAML } from 'yaml'
144
+
145
+ configMergerPlugin({
146
+ sources: ['.my-cli.yml', '.my-cli.yaml'],
147
+ parser: parseYAML
148
+ })
149
+ ```
150
+
151
+ ## Transform Configs
152
+
153
+ Apply transformations after loading:
154
+
155
+ ```typescript
156
+ configMergerPlugin({
157
+ sources: ['.my-clirc'],
158
+ transform: (config) => {
159
+ // Expand environment variables
160
+ if (config.apiKey === '$API_KEY') {
161
+ config.apiKey = process.env.API_KEY
162
+ }
163
+ return config
164
+ }
165
+ })
166
+ ```
167
+
168
+ ## Error Handling
169
+
170
+ Missing config files are silently ignored. Parse errors are logged but don't crash the CLI.
171
+
172
+ ## License
173
+
174
+ MIT © blefnk
package/dist/mod.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Config merger plugin for Rempts
3
+ * Loads configuration from multiple sources and merges them
4
+ */
5
+ export interface ConfigPluginOptions {
6
+ /**
7
+ * Config file sources to load
8
+ * Supports template variables: {{name}} for app name
9
+ * Default: ['~/.config/{{name}}/config.json', '.{{name}}rc', '.{{name}}rc.json']
10
+ */
11
+ sources?: string[];
12
+ /**
13
+ * Merge strategy
14
+ * - 'deep': Recursively merge objects (default)
15
+ * - 'shallow': Only merge top-level properties
16
+ */
17
+ mergeStrategy?: "shallow" | "deep";
18
+ /**
19
+ * Whether to stop on first found config
20
+ * Default: false (loads and merges all found configs)
21
+ */
22
+ stopOnFirst?: boolean;
23
+ }
24
+ /**
25
+ * Config merger plugin factory
26
+ */
27
+ export declare const configMergerPlugin: any;
28
+ export default configMergerPlugin;
package/dist/mod.js ADDED
@@ -0,0 +1,54 @@
1
+ import { access, readFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { createPlugin } from "@reliverse/rempts-core/plugin";
5
+ import { deepMerge } from "@reliverse/rempts-core/utils";
6
+ export const configMergerPlugin = createPlugin((options = {}) => {
7
+ const sources = options.sources || [
8
+ "~/.config/{{name}}/config.json",
9
+ ".{{name}}rc",
10
+ ".{{name}}rc.json",
11
+ ".config/{{name}}.json"
12
+ ];
13
+ return () => ({
14
+ async setup(context) {
15
+ const appName = context.config.name || "rempts";
16
+ const configs = [];
17
+ for (const source of sources) {
18
+ let path = source.replace(/^~/, homedir()).replace(/\{\{name\}\}/g, appName);
19
+ if (!(path.startsWith("/") || path.startsWith(homedir()))) {
20
+ path = join(context.paths.cwd, path);
21
+ }
22
+ try {
23
+ await access(path);
24
+ const content = await readFile(path, "utf-8");
25
+ let config;
26
+ try {
27
+ config = JSON.parse(content);
28
+ } catch (parseError) {
29
+ context.logger.warn(`Failed to parse config file ${path}: ${parseError}`);
30
+ continue;
31
+ }
32
+ configs.push(config);
33
+ context.logger.debug(`Loaded config from ${path}`);
34
+ if (options.stopOnFirst) {
35
+ break;
36
+ }
37
+ } catch {
38
+ context.logger.debug(`Config file not found: ${path}`);
39
+ }
40
+ }
41
+ if (configs.length > 0) {
42
+ let merged;
43
+ if (options.mergeStrategy === "shallow") {
44
+ merged = Object.assign({}, ...configs);
45
+ } else {
46
+ merged = deepMerge(...configs);
47
+ }
48
+ context.updateConfig(merged);
49
+ context.logger.info(`Merged ${configs.length} config file(s)`);
50
+ }
51
+ }
52
+ });
53
+ });
54
+ export default configMergerPlugin;
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@reliverse/rempts-plugin-config",
3
+ "version": "2.3.1",
4
+ "description": "Config merger plugin for Rempts - loads config from multiple sources",
5
+ "keywords": [
6
+ "cli",
7
+ "config",
8
+ "plugin",
9
+ "rempts"
10
+ ],
11
+ "license": "MIT",
12
+ "author": "bun dler Team",
13
+ "files": [
14
+ "dist",
15
+ "src"
16
+ ],
17
+ "type": "module",
18
+ "main": "./src/mod.ts",
19
+ "types": "./src/mod.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/mod.d.ts",
23
+ "default": "./dist/mod.js"
24
+ }
25
+ },
26
+ "dependencies": {
27
+ "@reliverse/rempts-core": "2.3.1",
28
+ "@reliverse/rempts-utils": "2.3.1"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ }
33
+ }
package/src/mod.ts ADDED
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Config merger plugin for Rempts
3
+ * Loads configuration from multiple sources and merges them
4
+ */
5
+
6
+ import { access, readFile } from "node:fs/promises";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { createPlugin } from "@reliverse/rempts-core/plugin";
10
+ import { deepMerge } from "@reliverse/rempts-core/utils";
11
+
12
+ export interface ConfigPluginOptions {
13
+ /**
14
+ * Config file sources to load
15
+ * Supports template variables: {{name}} for app name
16
+ * Default: ['~/.config/{{name}}/config.json', '.{{name}}rc', '.{{name}}rc.json']
17
+ */
18
+ sources?: string[];
19
+
20
+ /**
21
+ * Merge strategy
22
+ * - 'deep': Recursively merge objects (default)
23
+ * - 'shallow': Only merge top-level properties
24
+ */
25
+ mergeStrategy?: "shallow" | "deep";
26
+
27
+ /**
28
+ * Whether to stop on first found config
29
+ * Default: false (loads and merges all found configs)
30
+ */
31
+ stopOnFirst?: boolean;
32
+ }
33
+
34
+ /**
35
+ * Config merger plugin factory
36
+ */
37
+ export const configMergerPlugin = createPlugin<ConfigPluginOptions, {}>((options = {}) => {
38
+ const sources = options.sources || [
39
+ "~/.config/{{name}}/config.json",
40
+ ".{{name}}rc",
41
+ ".{{name}}rc.json",
42
+ ".config/{{name}}.json",
43
+ ];
44
+
45
+ return () => ({
46
+ async setup(context) {
47
+ const appName = context.config.name || "rempts";
48
+ const configs: any[] = [];
49
+
50
+ for (const source of sources) {
51
+ // Resolve template variables and home directory
52
+ let path = source.replace(/^~/, homedir()).replace(/\{\{name\}\}/g, appName);
53
+
54
+ // Resolve relative paths from context cwd
55
+ if (!(path.startsWith("/") || path.startsWith(homedir()))) {
56
+ path = join(context.paths.cwd, path);
57
+ }
58
+
59
+ try {
60
+ // Check if file exists
61
+ await access(path);
62
+
63
+ // Read and parse config
64
+ const content = await readFile(path, "utf-8");
65
+ let config: any;
66
+
67
+ try {
68
+ config = JSON.parse(content);
69
+ } catch (parseError) {
70
+ context.logger.warn(`Failed to parse config file ${path}: ${parseError}`);
71
+ continue;
72
+ }
73
+
74
+ configs.push(config);
75
+ context.logger.debug(`Loaded config from ${path}`);
76
+
77
+ // Stop if requested
78
+ if (options.stopOnFirst) {
79
+ break;
80
+ }
81
+ } catch {
82
+ // File doesn't exist, skip silently
83
+ context.logger.debug(`Config file not found: ${path}`);
84
+ }
85
+ }
86
+
87
+ if (configs.length > 0) {
88
+ // Merge all found configs
89
+ let merged: any;
90
+
91
+ if (options.mergeStrategy === "shallow") {
92
+ merged = Object.assign({}, ...configs);
93
+ } else {
94
+ // Deep merge is already available
95
+ merged = deepMerge(...configs);
96
+ }
97
+
98
+ context.updateConfig(merged);
99
+ context.logger.info(`Merged ${configs.length} config file(s)`);
100
+ }
101
+ },
102
+ });
103
+ });
104
+
105
+ // Default export for convenience
106
+ export default configMergerPlugin;