dotmd-cli 0.14.2 → 0.14.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/package.json +1 -1
- package/src/config.mjs +116 -0
package/package.json
CHANGED
package/src/config.mjs
CHANGED
|
@@ -97,6 +97,118 @@ const DEFAULTS = {
|
|
|
97
97
|
},
|
|
98
98
|
};
|
|
99
99
|
|
|
100
|
+
const VALID_CONTEXT_VALUES = new Set(['expanded', 'listed', 'counted']);
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Normalize rich status definitions (object form) into array form + derived config.
|
|
104
|
+
* When types.<type>.statuses is an object like:
|
|
105
|
+
* { 'active': { context: 'expanded', staleDays: 14, requiresModule: true }, ... }
|
|
106
|
+
* this extracts all behavioral properties, converts statuses to an array for
|
|
107
|
+
* downstream processing, and returns derived values for lifecycle/taxonomy/context.
|
|
108
|
+
*
|
|
109
|
+
* Returns null if no types use object-form statuses.
|
|
110
|
+
*/
|
|
111
|
+
function normalizeRichStatuses(config, userConfig) {
|
|
112
|
+
const derived = {
|
|
113
|
+
archiveStatuses: [],
|
|
114
|
+
skipStaleFor: [],
|
|
115
|
+
skipWarningsFor: [],
|
|
116
|
+
terminalStatuses: [],
|
|
117
|
+
moduleRequiredFor: [],
|
|
118
|
+
staleDays: {},
|
|
119
|
+
statusOrder: [],
|
|
120
|
+
context: { expanded: [], listed: [], counted: [] },
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
let hasRich = false;
|
|
124
|
+
|
|
125
|
+
for (const [typeName, typeDef] of Object.entries(config.types ?? {})) {
|
|
126
|
+
if (!typeDef.statuses || Array.isArray(typeDef.statuses)) continue;
|
|
127
|
+
if (typeof typeDef.statuses !== 'object') continue;
|
|
128
|
+
|
|
129
|
+
hasRich = true;
|
|
130
|
+
const statusNames = [];
|
|
131
|
+
const typeContext = { expanded: [], listed: [], counted: [] };
|
|
132
|
+
const typeStaleDays = {};
|
|
133
|
+
|
|
134
|
+
for (const [name, props] of Object.entries(typeDef.statuses)) {
|
|
135
|
+
const p = props ?? {};
|
|
136
|
+
statusNames.push(name);
|
|
137
|
+
|
|
138
|
+
const ctx = p.context ?? 'counted';
|
|
139
|
+
if (typeContext[ctx]) typeContext[ctx].push(name);
|
|
140
|
+
// Global context: only add if not already in any bucket (first type wins)
|
|
141
|
+
const inGlobal = derived.context.expanded.includes(name) ||
|
|
142
|
+
derived.context.listed.includes(name) || derived.context.counted.includes(name);
|
|
143
|
+
if (!inGlobal && derived.context[ctx]) derived.context[ctx].push(name);
|
|
144
|
+
|
|
145
|
+
if (p.staleDays != null) {
|
|
146
|
+
typeStaleDays[name] = p.staleDays;
|
|
147
|
+
derived.staleDays[name] = p.staleDays;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (p.archive && !derived.archiveStatuses.includes(name)) derived.archiveStatuses.push(name);
|
|
151
|
+
if (p.skipStale && !derived.skipStaleFor.includes(name)) derived.skipStaleFor.push(name);
|
|
152
|
+
if (p.skipWarnings && !derived.skipWarningsFor.includes(name)) derived.skipWarningsFor.push(name);
|
|
153
|
+
if (p.terminal && !derived.terminalStatuses.includes(name)) derived.terminalStatuses.push(name);
|
|
154
|
+
if (p.requiresModule && !derived.moduleRequiredFor.includes(name)) derived.moduleRequiredFor.push(name);
|
|
155
|
+
|
|
156
|
+
if (!derived.statusOrder.includes(name)) derived.statusOrder.push(name);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Convert to array form for downstream pipeline
|
|
160
|
+
typeDef.statuses = statusNames;
|
|
161
|
+
|
|
162
|
+
// Derive type-level context/staleDays unless user explicitly provided them
|
|
163
|
+
const userTypeDef = userConfig.types?.[typeName];
|
|
164
|
+
if (!userTypeDef?.context) typeDef.context = typeContext;
|
|
165
|
+
if (!userTypeDef?.staleDays) typeDef.staleDays = typeStaleDays;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!hasRich) return null;
|
|
169
|
+
return derived;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Apply derived values from rich status definitions into the merged config.
|
|
174
|
+
* Explicit user config always wins over derived values.
|
|
175
|
+
*/
|
|
176
|
+
function applyDerivedConfig(config, userConfig, derived) {
|
|
177
|
+
// statuses.order — derive from types if user didn't explicitly set
|
|
178
|
+
if (!userConfig.statuses?.order && derived.statusOrder.length) {
|
|
179
|
+
config.statuses.order = derived.statusOrder;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// statuses.staleDays — merge derived as base, user overrides
|
|
183
|
+
if (!userConfig.statuses?.staleDays && Object.keys(derived.staleDays).length) {
|
|
184
|
+
config.statuses.staleDays = derived.staleDays;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// lifecycle — each sub-key independently
|
|
188
|
+
if (!userConfig.lifecycle?.archiveStatuses && derived.archiveStatuses.length) {
|
|
189
|
+
config.lifecycle.archiveStatuses = derived.archiveStatuses;
|
|
190
|
+
}
|
|
191
|
+
if (!userConfig.lifecycle?.skipStaleFor && derived.skipStaleFor.length) {
|
|
192
|
+
config.lifecycle.skipStaleFor = derived.skipStaleFor;
|
|
193
|
+
}
|
|
194
|
+
if (!userConfig.lifecycle?.skipWarningsFor && derived.skipWarningsFor.length) {
|
|
195
|
+
config.lifecycle.skipWarningsFor = derived.skipWarningsFor;
|
|
196
|
+
}
|
|
197
|
+
if (!userConfig.lifecycle?.terminalStatuses && derived.terminalStatuses.length) {
|
|
198
|
+
config.lifecycle.terminalStatuses = derived.terminalStatuses;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// taxonomy.moduleRequiredFor
|
|
202
|
+
if (!userConfig.taxonomy?.moduleRequiredFor && derived.moduleRequiredFor.length) {
|
|
203
|
+
config.taxonomy.moduleRequiredFor = derived.moduleRequiredFor;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// context — only the status-related arrays, not recentDays/recentLimit/etc
|
|
207
|
+
if (!userConfig.context?.expanded) config.context.expanded = derived.context.expanded;
|
|
208
|
+
if (!userConfig.context?.listed) config.context.listed = derived.context.listed;
|
|
209
|
+
if (!userConfig.context?.counted) config.context.counted = derived.context.counted;
|
|
210
|
+
}
|
|
211
|
+
|
|
100
212
|
function findConfigFile(startDir) {
|
|
101
213
|
let dir = path.resolve(startDir);
|
|
102
214
|
const root = path.parse(dir).root;
|
|
@@ -218,6 +330,10 @@ export async function resolveConfig(cwd, explicitConfigPath) {
|
|
|
218
330
|
|
|
219
331
|
const config = deepMerge(DEFAULTS, userConfig);
|
|
220
332
|
|
|
333
|
+
// Normalize rich status definitions (object form → array + derived config)
|
|
334
|
+
const derived = normalizeRichStatuses(config, userConfig);
|
|
335
|
+
if (derived) applyDerivedConfig(config, userConfig, derived);
|
|
336
|
+
|
|
221
337
|
const rootPaths = Array.isArray(config.root) ? config.root : [config.root];
|
|
222
338
|
const docsRoots = rootPaths.map(r => path.resolve(configDir, r));
|
|
223
339
|
const docsRoot = docsRoots[0]; // primary root for backwards compat
|