@oamm/textor 1.0.2 → 1.0.4
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 +130 -9
- package/dist/bin/textor.js +409 -30
- package/dist/bin/textor.js.map +1 -1
- package/dist/index.cjs +253 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +252 -31
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -9,8 +9,10 @@ var util = require('util');
|
|
|
9
9
|
|
|
10
10
|
const CONFIG_DIR$1 = '.textor';
|
|
11
11
|
const CONFIG_FILE = 'config.json';
|
|
12
|
+
const CURRENT_CONFIG_VERSION = 2;
|
|
12
13
|
/**
|
|
13
14
|
* @typedef {Object} TextorConfig
|
|
15
|
+
* @property {number} configVersion
|
|
14
16
|
* @property {Object} paths
|
|
15
17
|
* @property {string} paths.pages
|
|
16
18
|
* @property {string} paths.features
|
|
@@ -47,6 +49,9 @@ const CONFIG_FILE = 'config.json';
|
|
|
47
49
|
* @property {boolean} components.createTypes
|
|
48
50
|
* @property {Object} formatting
|
|
49
51
|
* @property {string} formatting.tool
|
|
52
|
+
* @property {Object} filePatterns
|
|
53
|
+
* @property {Object} filePatterns.features
|
|
54
|
+
* @property {Object} filePatterns.components
|
|
50
55
|
* @property {Object} git
|
|
51
56
|
* @property {boolean} git.requireCleanRepo
|
|
52
57
|
* @property {boolean} git.stageChanges
|
|
@@ -58,6 +63,7 @@ const CONFIG_FILE = 'config.json';
|
|
|
58
63
|
* @type {TextorConfig}
|
|
59
64
|
*/
|
|
60
65
|
const DEFAULT_CONFIG = {
|
|
66
|
+
configVersion: CURRENT_CONFIG_VERSION,
|
|
61
67
|
paths: {
|
|
62
68
|
pages: 'src/pages',
|
|
63
69
|
features: 'src/features',
|
|
@@ -117,6 +123,34 @@ const DEFAULT_CONFIG = {
|
|
|
117
123
|
formatting: {
|
|
118
124
|
tool: 'none' // 'prettier' | 'biome' | 'none'
|
|
119
125
|
},
|
|
126
|
+
filePatterns: {
|
|
127
|
+
features: {
|
|
128
|
+
index: 'index.ts',
|
|
129
|
+
types: 'index.ts',
|
|
130
|
+
api: 'index.ts',
|
|
131
|
+
services: 'index.ts',
|
|
132
|
+
schemas: 'index.ts',
|
|
133
|
+
hook: '{{hookName}}{{hookExtension}}',
|
|
134
|
+
context: '{{componentName}}Context.tsx',
|
|
135
|
+
test: '{{componentName}}{{testExtension}}',
|
|
136
|
+
readme: 'README.md',
|
|
137
|
+
stories: '{{componentName}}.stories.tsx'
|
|
138
|
+
},
|
|
139
|
+
components: {
|
|
140
|
+
index: 'index.ts',
|
|
141
|
+
types: 'index.ts',
|
|
142
|
+
api: 'index.ts',
|
|
143
|
+
services: 'index.ts',
|
|
144
|
+
schemas: 'index.ts',
|
|
145
|
+
hook: '{{hookName}}{{hookExtension}}',
|
|
146
|
+
context: '{{componentName}}Context.tsx',
|
|
147
|
+
test: '{{componentName}}{{testExtension}}',
|
|
148
|
+
config: 'index.ts',
|
|
149
|
+
constants: 'index.ts',
|
|
150
|
+
readme: 'README.md',
|
|
151
|
+
stories: '{{componentName}}.stories.tsx'
|
|
152
|
+
}
|
|
153
|
+
},
|
|
120
154
|
hashing: {
|
|
121
155
|
normalization: 'normalizeEOL', // 'none' | 'normalizeEOL' | 'stripGeneratedRegions'
|
|
122
156
|
useMarkers: false
|
|
@@ -218,7 +252,7 @@ async function loadConfig() {
|
|
|
218
252
|
try {
|
|
219
253
|
const content = await promises.readFile(configPath, 'utf-8');
|
|
220
254
|
const config = JSON.parse(content);
|
|
221
|
-
const merged =
|
|
255
|
+
const merged = mergeConfig(DEFAULT_CONFIG, config);
|
|
222
256
|
validateConfig(merged);
|
|
223
257
|
return merged;
|
|
224
258
|
}
|
|
@@ -250,6 +284,34 @@ async function saveConfig(config, force = false) {
|
|
|
250
284
|
await promises.writeFile(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
251
285
|
return configPath;
|
|
252
286
|
}
|
|
287
|
+
function mergeConfig(defaults, config) {
|
|
288
|
+
return deepMerge(defaults, config);
|
|
289
|
+
}
|
|
290
|
+
function normalizeConfigVersion(config) {
|
|
291
|
+
if (!config || typeof config !== 'object')
|
|
292
|
+
return config;
|
|
293
|
+
if (typeof config.configVersion !== 'number') {
|
|
294
|
+
return { ...config, configVersion: 1 };
|
|
295
|
+
}
|
|
296
|
+
return config;
|
|
297
|
+
}
|
|
298
|
+
function applyConfigMigrations(config) {
|
|
299
|
+
let current = normalizeConfigVersion(config);
|
|
300
|
+
let version = current.configVersion || 1;
|
|
301
|
+
let migrated = { ...current };
|
|
302
|
+
while (version < CURRENT_CONFIG_VERSION) {
|
|
303
|
+
if (version === 1) {
|
|
304
|
+
migrated = { ...migrated, configVersion: 2 };
|
|
305
|
+
version = 2;
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
if (migrated.configVersion !== CURRENT_CONFIG_VERSION) {
|
|
311
|
+
migrated.configVersion = CURRENT_CONFIG_VERSION;
|
|
312
|
+
}
|
|
313
|
+
return migrated;
|
|
314
|
+
}
|
|
253
315
|
/**
|
|
254
316
|
* Deeply merges source object into target object.
|
|
255
317
|
* @param {Object} target
|
|
@@ -279,6 +341,9 @@ function validateConfig(config) {
|
|
|
279
341
|
if (!config || typeof config !== 'object') {
|
|
280
342
|
throw new Error('Invalid configuration: must be an object');
|
|
281
343
|
}
|
|
344
|
+
if (config.configVersion !== undefined && typeof config.configVersion !== 'number') {
|
|
345
|
+
throw new Error('Invalid configuration: "configVersion" must be a number');
|
|
346
|
+
}
|
|
282
347
|
const requiredSections = ['paths', 'naming', 'signatures', 'importAliases'];
|
|
283
348
|
for (const section of requiredSections) {
|
|
284
349
|
if (!config[section] || typeof config[section] !== 'object') {
|
|
@@ -288,6 +353,15 @@ function validateConfig(config) {
|
|
|
288
353
|
if (config.kindRules && !Array.isArray(config.kindRules)) {
|
|
289
354
|
throw new Error('Invalid configuration: "kindRules" must be an array');
|
|
290
355
|
}
|
|
356
|
+
if (config.filePatterns && typeof config.filePatterns !== 'object') {
|
|
357
|
+
throw new Error('Invalid configuration: "filePatterns" must be an object');
|
|
358
|
+
}
|
|
359
|
+
if (config.filePatterns?.features && typeof config.filePatterns.features !== 'object') {
|
|
360
|
+
throw new Error('Invalid configuration: "filePatterns.features" must be an object');
|
|
361
|
+
}
|
|
362
|
+
if (config.filePatterns?.components && typeof config.filePatterns.components !== 'object') {
|
|
363
|
+
throw new Error('Invalid configuration: "filePatterns.components" must be an object');
|
|
364
|
+
}
|
|
291
365
|
// Validate paths are strings
|
|
292
366
|
for (const [key, value] of Object.entries(config.paths)) {
|
|
293
367
|
if (typeof value !== 'string') {
|
|
@@ -799,6 +873,33 @@ var filesystem = /*#__PURE__*/Object.freeze({
|
|
|
799
873
|
writeFileWithSignature: writeFileWithSignature
|
|
800
874
|
});
|
|
801
875
|
|
|
876
|
+
function renderNamePattern(pattern, data = {}, label = 'pattern') {
|
|
877
|
+
if (typeof pattern !== 'string')
|
|
878
|
+
return null;
|
|
879
|
+
const trimmed = pattern.trim();
|
|
880
|
+
if (!trimmed)
|
|
881
|
+
return null;
|
|
882
|
+
const missing = new Set();
|
|
883
|
+
const rendered = trimmed.replace(/{{\s*([a-zA-Z0-9_]+)\s*}}/g, (match, key) => {
|
|
884
|
+
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
|
885
|
+
missing.add(key);
|
|
886
|
+
return '';
|
|
887
|
+
}
|
|
888
|
+
return String(data[key]);
|
|
889
|
+
});
|
|
890
|
+
if (missing.size > 0) {
|
|
891
|
+
throw new Error(`Invalid ${label}: missing values for ${Array.from(missing).join(', ')}`);
|
|
892
|
+
}
|
|
893
|
+
return rendered;
|
|
894
|
+
}
|
|
895
|
+
function resolvePatternedPath(baseDir, pattern, data, fallback, label) {
|
|
896
|
+
const fileName = renderNamePattern(pattern, data, label) || fallback;
|
|
897
|
+
if (!fileName) {
|
|
898
|
+
throw new Error(`Invalid ${label}: resolved to empty file name`);
|
|
899
|
+
}
|
|
900
|
+
return secureJoin(baseDir, fileName);
|
|
901
|
+
}
|
|
902
|
+
|
|
802
903
|
function getTemplateOverride(templateName, extension, data = {}) {
|
|
803
904
|
const overridePath = path.join(process.cwd(), '.textor', 'templates', `${templateName}${extension}`);
|
|
804
905
|
if (fs.existsSync(overridePath)) {
|
|
@@ -1190,6 +1291,7 @@ async function loadState() {
|
|
|
1190
1291
|
const state = JSON.parse(content);
|
|
1191
1292
|
if (!state.files)
|
|
1192
1293
|
state.files = {};
|
|
1294
|
+
normalizeStatePaths(state);
|
|
1193
1295
|
return state;
|
|
1194
1296
|
}
|
|
1195
1297
|
catch (error) {
|
|
@@ -1230,23 +1332,67 @@ async function registerFile(filePath, { kind, template, hash, templateVersion =
|
|
|
1230
1332
|
}
|
|
1231
1333
|
async function addSectionToState(section) {
|
|
1232
1334
|
const state = await loadState();
|
|
1335
|
+
const normalizedSection = { ...section };
|
|
1336
|
+
if (normalizedSection.featurePath) {
|
|
1337
|
+
normalizedSection.featurePath = normalizeStatePath(normalizedSection.featurePath);
|
|
1338
|
+
}
|
|
1233
1339
|
// Avoid duplicates by route OR by featurePath if route is null
|
|
1234
|
-
if (
|
|
1235
|
-
state.sections = state.sections.filter(s => s.route !==
|
|
1340
|
+
if (normalizedSection.route) {
|
|
1341
|
+
state.sections = state.sections.filter(s => s.route !== normalizedSection.route);
|
|
1236
1342
|
}
|
|
1237
1343
|
else {
|
|
1238
|
-
state.sections = state.sections.filter(s => s.featurePath !==
|
|
1344
|
+
state.sections = state.sections.filter(s => s.featurePath !== normalizedSection.featurePath || s.route);
|
|
1239
1345
|
}
|
|
1240
|
-
state.sections.push(
|
|
1346
|
+
state.sections.push(normalizedSection);
|
|
1241
1347
|
await saveState(state);
|
|
1242
1348
|
}
|
|
1243
1349
|
async function addComponentToState(component) {
|
|
1244
1350
|
const state = await loadState();
|
|
1351
|
+
const normalizedComponent = { ...component };
|
|
1352
|
+
if (normalizedComponent.path) {
|
|
1353
|
+
normalizedComponent.path = normalizeStatePath(normalizedComponent.path);
|
|
1354
|
+
}
|
|
1245
1355
|
// Avoid duplicates by name
|
|
1246
|
-
state.components = state.components.filter(c => c.name !==
|
|
1247
|
-
state.components.push(
|
|
1356
|
+
state.components = state.components.filter(c => c.name !== normalizedComponent.name);
|
|
1357
|
+
state.components.push(normalizedComponent);
|
|
1248
1358
|
await saveState(state);
|
|
1249
1359
|
}
|
|
1360
|
+
function normalizeStatePath(filePath) {
|
|
1361
|
+
if (!filePath || typeof filePath !== 'string')
|
|
1362
|
+
return filePath;
|
|
1363
|
+
const relative = path.isAbsolute(filePath)
|
|
1364
|
+
? path.relative(process.cwd(), filePath)
|
|
1365
|
+
: filePath;
|
|
1366
|
+
return relative.replace(/\\/g, '/');
|
|
1367
|
+
}
|
|
1368
|
+
function normalizeStatePaths(state) {
|
|
1369
|
+
if (!state || typeof state !== 'object')
|
|
1370
|
+
return;
|
|
1371
|
+
if (Array.isArray(state.sections)) {
|
|
1372
|
+
state.sections = state.sections.map(section => {
|
|
1373
|
+
if (!section || typeof section !== 'object')
|
|
1374
|
+
return section;
|
|
1375
|
+
if (!section.featurePath)
|
|
1376
|
+
return section;
|
|
1377
|
+
const normalized = normalizeStatePath(section.featurePath);
|
|
1378
|
+
if (normalized === section.featurePath)
|
|
1379
|
+
return section;
|
|
1380
|
+
return { ...section, featurePath: normalized };
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
if (Array.isArray(state.components)) {
|
|
1384
|
+
state.components = state.components.map(component => {
|
|
1385
|
+
if (!component || typeof component !== 'object')
|
|
1386
|
+
return component;
|
|
1387
|
+
if (!component.path)
|
|
1388
|
+
return component;
|
|
1389
|
+
const normalized = normalizeStatePath(component.path);
|
|
1390
|
+
if (normalized === component.path)
|
|
1391
|
+
return component;
|
|
1392
|
+
return { ...component, path: normalized };
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1250
1396
|
function findSection(state, identifier) {
|
|
1251
1397
|
return state.sections.find(s => s.route === identifier || s.name === identifier || s.featurePath === identifier);
|
|
1252
1398
|
}
|
|
@@ -1407,16 +1553,25 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1407
1553
|
const servicesDirInside = secureJoin(featureDirPath, 'services');
|
|
1408
1554
|
const schemasDirInside = secureJoin(featureDirPath, 'schemas');
|
|
1409
1555
|
const { framework, createSubComponentsDir: shouldCreateSubComponentsDir, createScriptsDir: shouldCreateScriptsDir, createApi: shouldCreateApi, createServices: shouldCreateServices, createSchemas: shouldCreateSchemas, createHooks: shouldCreateHooks, createContext: shouldCreateContext, createTests: shouldCreateTests, createTypes: shouldCreateTypes, createReadme: shouldCreateReadme, createStories: shouldCreateStories, createIndex: shouldCreateIndex } = effectiveOptions;
|
|
1410
|
-
const
|
|
1411
|
-
const
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
const
|
|
1556
|
+
const featurePatterns = config.filePatterns?.features || {};
|
|
1557
|
+
const patternData = {
|
|
1558
|
+
componentName: featureComponentName,
|
|
1559
|
+
hookName: getHookFunctionName(featureComponentName),
|
|
1560
|
+
hookExtension: config.naming.hookExtension,
|
|
1561
|
+
testExtension: config.naming.testExtension,
|
|
1562
|
+
featureExtension: config.naming.featureExtension,
|
|
1563
|
+
componentExtension: config.naming.componentExtension
|
|
1564
|
+
};
|
|
1565
|
+
const indexFilePath = resolvePatternedPath(featureDirPath, featurePatterns.index, patternData, 'index.ts', 'filePatterns.features.index');
|
|
1566
|
+
const contextFilePath = resolvePatternedPath(contextDirInside, featurePatterns.context, patternData, `${featureComponentName}Context.tsx`, 'filePatterns.features.context');
|
|
1567
|
+
const hookFilePath = resolvePatternedPath(hooksDirInside, featurePatterns.hook, patternData, getHookFileName(featureComponentName, config.naming.hookExtension), 'filePatterns.features.hook');
|
|
1568
|
+
const testFilePath = resolvePatternedPath(testsDir, featurePatterns.test, patternData, `${featureComponentName}${config.naming.testExtension}`, 'filePatterns.features.test');
|
|
1569
|
+
const typesFilePath = resolvePatternedPath(typesDirInside, featurePatterns.types, patternData, 'index.ts', 'filePatterns.features.types');
|
|
1570
|
+
const apiFilePath = resolvePatternedPath(apiDirInside, featurePatterns.api, patternData, 'index.ts', 'filePatterns.features.api');
|
|
1571
|
+
const servicesFilePath = resolvePatternedPath(servicesDirInside, featurePatterns.services, patternData, 'index.ts', 'filePatterns.features.services');
|
|
1572
|
+
const schemasFilePath = resolvePatternedPath(schemasDirInside, featurePatterns.schemas, patternData, 'index.ts', 'filePatterns.features.schemas');
|
|
1573
|
+
const readmeFilePath = resolvePatternedPath(featureDirPath, featurePatterns.readme, patternData, 'README.md', 'filePatterns.features.readme');
|
|
1574
|
+
const storiesFilePath = resolvePatternedPath(featureDirPath, featurePatterns.stories, patternData, `${featureComponentName}.stories.tsx`, 'filePatterns.features.stories');
|
|
1420
1575
|
const routeParts = normalizedRoute ? normalizedRoute.split('/').filter(Boolean) : [];
|
|
1421
1576
|
const reorganizations = [];
|
|
1422
1577
|
if (normalizedRoute && routeParts.length > 1 && config.routing.mode === 'flat') {
|
|
@@ -2304,19 +2459,28 @@ async function createComponentCommand(componentName, options) {
|
|
|
2304
2459
|
const servicesDirInside = secureJoin(componentDir, 'services');
|
|
2305
2460
|
const schemasDirInside = secureJoin(componentDir, 'schemas');
|
|
2306
2461
|
const { framework, createContext: shouldCreateContext, createHook: shouldCreateHook, createTests: shouldCreateTests, createConfig: shouldCreateConfig, createConstants: shouldCreateConstants, createTypes: shouldCreateTypes, createSubComponentsDir: shouldCreateSubComponentsDir, createApi: shouldCreateApi, createServices: shouldCreateServices, createSchemas: shouldCreateSchemas, createReadme: shouldCreateReadme, createStories: shouldCreateStories } = effectiveOptions;
|
|
2462
|
+
const componentPatterns = config.filePatterns?.components || {};
|
|
2463
|
+
const patternData = {
|
|
2464
|
+
componentName: normalizedName,
|
|
2465
|
+
hookName: getHookFunctionName(normalizedName),
|
|
2466
|
+
hookExtension: config.naming.hookExtension,
|
|
2467
|
+
testExtension: config.naming.testExtension,
|
|
2468
|
+
componentExtension: config.naming.componentExtension,
|
|
2469
|
+
featureExtension: config.naming.featureExtension
|
|
2470
|
+
};
|
|
2307
2471
|
const componentFilePath = path.join(componentDir, `${normalizedName}${config.naming.componentExtension}`);
|
|
2308
|
-
const indexFilePath =
|
|
2309
|
-
const contextFilePath =
|
|
2310
|
-
const hookFilePath =
|
|
2311
|
-
const testFilePath =
|
|
2312
|
-
const configFilePath =
|
|
2313
|
-
const constantsFilePath =
|
|
2314
|
-
const typesFilePath =
|
|
2315
|
-
const apiFilePath =
|
|
2316
|
-
const servicesFilePath =
|
|
2317
|
-
const schemasFilePath =
|
|
2318
|
-
const readmeFilePath =
|
|
2319
|
-
const storiesFilePath =
|
|
2472
|
+
const indexFilePath = resolvePatternedPath(componentDir, componentPatterns.index, patternData, 'index.ts', 'filePatterns.components.index');
|
|
2473
|
+
const contextFilePath = resolvePatternedPath(contextDirInside, componentPatterns.context, patternData, `${normalizedName}Context.tsx`, 'filePatterns.components.context');
|
|
2474
|
+
const hookFilePath = resolvePatternedPath(hooksDirInside, componentPatterns.hook, patternData, getHookFileName(normalizedName, config.naming.hookExtension), 'filePatterns.components.hook');
|
|
2475
|
+
const testFilePath = resolvePatternedPath(testsDir, componentPatterns.test, patternData, `${normalizedName}${config.naming.testExtension}`, 'filePatterns.components.test');
|
|
2476
|
+
const configFilePath = resolvePatternedPath(configDirInside, componentPatterns.config, patternData, 'index.ts', 'filePatterns.components.config');
|
|
2477
|
+
const constantsFilePath = resolvePatternedPath(constantsDirInside, componentPatterns.constants, patternData, 'index.ts', 'filePatterns.components.constants');
|
|
2478
|
+
const typesFilePath = resolvePatternedPath(typesDirInside, componentPatterns.types, patternData, 'index.ts', 'filePatterns.components.types');
|
|
2479
|
+
const apiFilePath = resolvePatternedPath(apiDirInside, componentPatterns.api, patternData, 'index.ts', 'filePatterns.components.api');
|
|
2480
|
+
const servicesFilePath = resolvePatternedPath(servicesDirInside, componentPatterns.services, patternData, 'index.ts', 'filePatterns.components.services');
|
|
2481
|
+
const schemasFilePath = resolvePatternedPath(schemasDirInside, componentPatterns.schemas, patternData, 'index.ts', 'filePatterns.components.schemas');
|
|
2482
|
+
const readmeFilePath = resolvePatternedPath(componentDir, componentPatterns.readme, patternData, 'README.md', 'filePatterns.components.readme');
|
|
2483
|
+
const storiesFilePath = resolvePatternedPath(componentDir, componentPatterns.stories, patternData, `${normalizedName}.stories.tsx`, 'filePatterns.components.stories');
|
|
2320
2484
|
if (options.dryRun) {
|
|
2321
2485
|
console.log('Dry run - would create:');
|
|
2322
2486
|
console.log(` Component: ${componentFilePath}`);
|
|
@@ -2593,7 +2757,7 @@ async function removeComponentCommand(identifier, options) {
|
|
|
2593
2757
|
const component = findComponent(state, identifier);
|
|
2594
2758
|
let componentDir;
|
|
2595
2759
|
if (component) {
|
|
2596
|
-
componentDir = component.path;
|
|
2760
|
+
componentDir = path.resolve(process.cwd(), component.path);
|
|
2597
2761
|
}
|
|
2598
2762
|
else {
|
|
2599
2763
|
// Fallback: try to guess path if not in state
|
|
@@ -3220,15 +3384,74 @@ async function scanDirectoryOrFile(fullPath, fileSet, state) {
|
|
|
3220
3384
|
}
|
|
3221
3385
|
}
|
|
3222
3386
|
|
|
3387
|
+
async function upgradeConfigCommand(options) {
|
|
3388
|
+
try {
|
|
3389
|
+
const configPath = getConfigPath();
|
|
3390
|
+
if (!fs.existsSync(configPath)) {
|
|
3391
|
+
throw new Error(`Textor configuration not found at ${configPath}\n` +
|
|
3392
|
+
`Run 'textor init' to create it.`);
|
|
3393
|
+
}
|
|
3394
|
+
const rawContent = await promises.readFile(configPath, 'utf-8');
|
|
3395
|
+
const rawConfig = JSON.parse(rawContent);
|
|
3396
|
+
const migrated = applyConfigMigrations(rawConfig);
|
|
3397
|
+
const merged = mergeConfig(DEFAULT_CONFIG, migrated);
|
|
3398
|
+
merged.configVersion = CURRENT_CONFIG_VERSION;
|
|
3399
|
+
validateConfig(merged);
|
|
3400
|
+
if (options.dryRun) {
|
|
3401
|
+
console.log('Dry run - upgraded configuration:');
|
|
3402
|
+
console.log(JSON.stringify(merged, null, 2));
|
|
3403
|
+
return;
|
|
3404
|
+
}
|
|
3405
|
+
await promises.writeFile(configPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
|
|
3406
|
+
console.log('Configuration upgraded successfully.');
|
|
3407
|
+
console.log(` Version: ${rawConfig.configVersion || 1} -> ${CURRENT_CONFIG_VERSION}`);
|
|
3408
|
+
console.log(` Path: ${configPath}`);
|
|
3409
|
+
}
|
|
3410
|
+
catch (error) {
|
|
3411
|
+
if (error instanceof SyntaxError) {
|
|
3412
|
+
console.error('Error: Failed to parse config: Invalid JSON');
|
|
3413
|
+
}
|
|
3414
|
+
else {
|
|
3415
|
+
console.error('Error:', error.message);
|
|
3416
|
+
}
|
|
3417
|
+
if (typeof process.exit === 'function' && process.env.NODE_ENV !== 'test') {
|
|
3418
|
+
process.exit(1);
|
|
3419
|
+
}
|
|
3420
|
+
throw error;
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
|
|
3424
|
+
async function normalizeStateCommand(options) {
|
|
3425
|
+
try {
|
|
3426
|
+
const state = await loadState();
|
|
3427
|
+
if (options.dryRun) {
|
|
3428
|
+
console.log('Dry run - normalized state:');
|
|
3429
|
+
console.log(JSON.stringify(state, null, 2));
|
|
3430
|
+
return;
|
|
3431
|
+
}
|
|
3432
|
+
await saveState(state);
|
|
3433
|
+
console.log('State normalized successfully.');
|
|
3434
|
+
}
|
|
3435
|
+
catch (error) {
|
|
3436
|
+
console.error('Error:', error.message);
|
|
3437
|
+
if (typeof process.exit === 'function' && process.env.NODE_ENV !== 'test') {
|
|
3438
|
+
process.exit(1);
|
|
3439
|
+
}
|
|
3440
|
+
throw error;
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
|
|
3223
3444
|
exports.addSectionCommand = addSectionCommand;
|
|
3224
3445
|
exports.adoptCommand = adoptCommand;
|
|
3225
3446
|
exports.createComponentCommand = createComponentCommand;
|
|
3226
3447
|
exports.initCommand = initCommand;
|
|
3227
3448
|
exports.listSectionsCommand = listSectionsCommand;
|
|
3228
3449
|
exports.moveSectionCommand = moveSectionCommand;
|
|
3450
|
+
exports.normalizeStateCommand = normalizeStateCommand;
|
|
3229
3451
|
exports.removeComponentCommand = removeComponentCommand;
|
|
3230
3452
|
exports.removeSectionCommand = removeSectionCommand;
|
|
3231
3453
|
exports.statusCommand = statusCommand;
|
|
3232
3454
|
exports.syncCommand = syncCommand;
|
|
3455
|
+
exports.upgradeConfigCommand = upgradeConfigCommand;
|
|
3233
3456
|
exports.validateStateCommand = validateStateCommand;
|
|
3234
3457
|
//# sourceMappingURL=index.cjs.map
|