@oamm/textor 1.0.13 → 1.0.14

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/dist/index.cjs CHANGED
@@ -637,14 +637,6 @@ async function safeDelete(filePath, options = {}) {
637
637
  await promises.unlink(filePath);
638
638
  return { deleted: true };
639
639
  }
640
- async function ensureNotExists(filePath, force = false) {
641
- if (fs.existsSync(filePath)) {
642
- if (!force) {
643
- throw new Error(`File already exists: ${filePath}\n` +
644
- `Use --force to overwrite.`);
645
- }
646
- }
647
- }
648
640
  async function ensureDir(dirPath) {
649
641
  await promises.mkdir(dirPath, { recursive: true });
650
642
  }
@@ -1796,29 +1788,35 @@ async function addSectionCommand(route, featurePath, options) {
1796
1788
  }
1797
1789
  }
1798
1790
  }
1799
- await ensureNotExists(featureFilePath, options.force);
1800
- if (shouldCreateIndex)
1801
- await ensureNotExists(indexFilePath, options.force);
1802
- if (shouldCreateContext)
1803
- await ensureNotExists(contextFilePath, options.force);
1804
- if (shouldCreateHooks)
1805
- await ensureNotExists(hookFilePath, options.force);
1806
- if (shouldCreateTests)
1807
- await ensureNotExists(testFilePath, options.force);
1808
- if (shouldCreateTypes)
1809
- await ensureNotExists(typesFilePath, options.force);
1810
- if (shouldCreateApi)
1811
- await ensureNotExists(apiFilePath, options.force);
1812
- if (shouldCreateServices)
1813
- await ensureNotExists(servicesFilePath, options.force);
1814
- if (shouldCreateSchemas)
1815
- await ensureNotExists(schemasFilePath, options.force);
1816
- if (shouldCreateReadme)
1817
- await ensureNotExists(readmeFilePath, options.force);
1818
- if (shouldCreateStories)
1819
- await ensureNotExists(storiesFilePath, options.force);
1820
- if (shouldCreateScriptsDir)
1821
- await ensureNotExists(scriptsIndexPath, options.force);
1791
+ const featureExists = fs.existsSync(featureFilePath);
1792
+ if (featureExists && !options.force) {
1793
+ console.log(`ℹ Feature already exists at ${featureFilePath}. Entering additive mode.`);
1794
+ }
1795
+ // Check sub-items only if not in force mode
1796
+ if (!options.force) {
1797
+ if (shouldCreateIndex && fs.existsSync(indexFilePath))
1798
+ console.log(` - Skipping existing index: ${indexFilePath}`);
1799
+ if (shouldCreateContext && fs.existsSync(contextFilePath))
1800
+ console.log(` - Skipping existing context: ${contextFilePath}`);
1801
+ if (shouldCreateHooks && fs.existsSync(hookFilePath))
1802
+ console.log(` - Skipping existing hook: ${hookFilePath}`);
1803
+ if (shouldCreateTests && fs.existsSync(testFilePath))
1804
+ console.log(` - Skipping existing test: ${testFilePath}`);
1805
+ if (shouldCreateTypes && fs.existsSync(typesFilePath))
1806
+ console.log(` - Skipping existing types: ${typesFilePath}`);
1807
+ if (shouldCreateApi && fs.existsSync(apiFilePath))
1808
+ console.log(` - Skipping existing api: ${apiFilePath}`);
1809
+ if (shouldCreateServices && fs.existsSync(servicesFilePath))
1810
+ console.log(` - Skipping existing services: ${servicesFilePath}`);
1811
+ if (shouldCreateSchemas && fs.existsSync(schemasFilePath))
1812
+ console.log(` - Skipping existing schemas: ${schemasFilePath}`);
1813
+ if (shouldCreateReadme && fs.existsSync(readmeFilePath))
1814
+ console.log(` - Skipping existing readme: ${readmeFilePath}`);
1815
+ if (shouldCreateStories && fs.existsSync(storiesFilePath))
1816
+ console.log(` - Skipping existing stories: ${storiesFilePath}`);
1817
+ if (shouldCreateScriptsDir && fs.existsSync(scriptsIndexPath))
1818
+ console.log(` - Skipping existing scripts: ${scriptsIndexPath}`);
1819
+ }
1822
1820
  let layoutImportPath = null;
1823
1821
  const cliProps = options.prop || {};
1824
1822
  const rawLayoutProps = { ...configLayoutProps, ...cliProps };
@@ -1940,15 +1938,17 @@ async function addSectionCommand(route, featurePath, options) {
1940
1938
  if (shouldCreateTypes)
1941
1939
  await ensureDir(typesDirInside);
1942
1940
  const featureSignature = getSignature(config, config.naming.featureExtension === '.astro' ? 'astro' : 'tsx');
1943
- const featureHash = await writeFileWithSignature(featureFilePath, featureContent, featureSignature, config.hashing?.normalization);
1944
- await registerFile(featureFilePath, {
1945
- kind: 'feature',
1946
- template: 'feature',
1947
- hash: featureHash,
1948
- owner: normalizedRoute
1949
- });
1950
- writtenFiles.push(featureFilePath);
1951
- if (shouldCreateScriptsDir) {
1941
+ if (!featureExists || options.force) {
1942
+ const featureHash = await writeFileWithSignature(featureFilePath, featureContent, featureSignature, config.hashing?.normalization);
1943
+ await registerFile(featureFilePath, {
1944
+ kind: 'feature',
1945
+ template: 'feature',
1946
+ hash: featureHash,
1947
+ owner: normalizedRoute
1948
+ });
1949
+ writtenFiles.push(featureFilePath);
1950
+ }
1951
+ if (shouldCreateScriptsDir && (!fs.existsSync(scriptsIndexPath) || options.force)) {
1952
1952
  const hash = await writeFileWithSignature(scriptsIndexPath, generateScriptsIndexTemplate(), getSignature(config, 'typescript'), config.hashing?.normalization);
1953
1953
  await registerFile(scriptsIndexPath, {
1954
1954
  kind: 'feature-file',
@@ -1958,7 +1958,7 @@ async function addSectionCommand(route, featurePath, options) {
1958
1958
  });
1959
1959
  writtenFiles.push(scriptsIndexPath);
1960
1960
  }
1961
- if (shouldCreateIndex) {
1961
+ if (shouldCreateIndex && (!fs.existsSync(indexFilePath) || options.force)) {
1962
1962
  const indexContent = generateIndexTemplate(featureComponentName, config.naming.featureExtension);
1963
1963
  const hash = await writeFileWithSignature(indexFilePath, indexContent, getSignature(config, 'typescript'), config.hashing?.normalization);
1964
1964
  await registerFile(indexFilePath, {
@@ -1969,7 +1969,7 @@ async function addSectionCommand(route, featurePath, options) {
1969
1969
  });
1970
1970
  writtenFiles.push(indexFilePath);
1971
1971
  }
1972
- if (shouldCreateApi) {
1972
+ if (shouldCreateApi && (!fs.existsSync(apiFilePath) || options.force)) {
1973
1973
  const apiContent = generateApiTemplate(featureComponentName);
1974
1974
  const hash = await writeFileWithSignature(apiFilePath, apiContent, getSignature(config, 'typescript'), config.hashing?.normalization);
1975
1975
  await registerFile(apiFilePath, {
@@ -1980,7 +1980,7 @@ async function addSectionCommand(route, featurePath, options) {
1980
1980
  });
1981
1981
  writtenFiles.push(apiFilePath);
1982
1982
  }
1983
- if (shouldCreateServices) {
1983
+ if (shouldCreateServices && (!fs.existsSync(servicesFilePath) || options.force)) {
1984
1984
  const servicesContent = generateServiceTemplate(featureComponentName);
1985
1985
  const hash = await writeFileWithSignature(servicesFilePath, servicesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
1986
1986
  await registerFile(servicesFilePath, {
@@ -1991,7 +1991,7 @@ async function addSectionCommand(route, featurePath, options) {
1991
1991
  });
1992
1992
  writtenFiles.push(servicesFilePath);
1993
1993
  }
1994
- if (shouldCreateSchemas) {
1994
+ if (shouldCreateSchemas && (!fs.existsSync(schemasFilePath) || options.force)) {
1995
1995
  const schemasContent = generateSchemaTemplate(featureComponentName);
1996
1996
  const hash = await writeFileWithSignature(schemasFilePath, schemasContent, getSignature(config, 'typescript'), config.hashing?.normalization);
1997
1997
  await registerFile(schemasFilePath, {
@@ -2002,7 +2002,7 @@ async function addSectionCommand(route, featurePath, options) {
2002
2002
  });
2003
2003
  writtenFiles.push(schemasFilePath);
2004
2004
  }
2005
- if (shouldCreateHooks) {
2005
+ if (shouldCreateHooks && (!fs.existsSync(hookFilePath) || options.force)) {
2006
2006
  const hookName = getHookFunctionName(featureComponentName);
2007
2007
  const hookContent = generateHookTemplate(featureComponentName, hookName);
2008
2008
  const hash = await writeFileWithSignature(hookFilePath, hookContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -2014,7 +2014,7 @@ async function addSectionCommand(route, featurePath, options) {
2014
2014
  });
2015
2015
  writtenFiles.push(hookFilePath);
2016
2016
  }
2017
- if (shouldCreateContext) {
2017
+ if (shouldCreateContext && (!fs.existsSync(contextFilePath) || options.force)) {
2018
2018
  const contextContent = generateContextTemplate(featureComponentName);
2019
2019
  const hash = await writeFileWithSignature(contextFilePath, contextContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2020
2020
  await registerFile(contextFilePath, {
@@ -2025,7 +2025,7 @@ async function addSectionCommand(route, featurePath, options) {
2025
2025
  });
2026
2026
  writtenFiles.push(contextFilePath);
2027
2027
  }
2028
- if (shouldCreateTests) {
2028
+ if (shouldCreateTests && (!fs.existsSync(testFilePath) || options.force)) {
2029
2029
  const relativeFeaturePath = `./${path.basename(featureFilePath)}`;
2030
2030
  const testContent = generateTestTemplate(featureComponentName, relativeFeaturePath);
2031
2031
  const hash = await writeFileWithSignature(testFilePath, testContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -2037,7 +2037,7 @@ async function addSectionCommand(route, featurePath, options) {
2037
2037
  });
2038
2038
  writtenFiles.push(testFilePath);
2039
2039
  }
2040
- if (shouldCreateTypes) {
2040
+ if (shouldCreateTypes && (!fs.existsSync(typesFilePath) || options.force)) {
2041
2041
  const typesContent = generateTypesTemplate(featureComponentName);
2042
2042
  const hash = await writeFileWithSignature(typesFilePath, typesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2043
2043
  await registerFile(typesFilePath, {
@@ -2048,7 +2048,7 @@ async function addSectionCommand(route, featurePath, options) {
2048
2048
  });
2049
2049
  writtenFiles.push(typesFilePath);
2050
2050
  }
2051
- if (shouldCreateReadme) {
2051
+ if (shouldCreateReadme && (!fs.existsSync(readmeFilePath) || options.force)) {
2052
2052
  const readmeContent = generateReadmeTemplate(featureComponentName);
2053
2053
  const hash = await writeFileWithSignature(readmeFilePath, readmeContent, getSignature(config, 'astro'), config.hashing?.normalization);
2054
2054
  await registerFile(readmeFilePath, {
@@ -2059,7 +2059,7 @@ async function addSectionCommand(route, featurePath, options) {
2059
2059
  });
2060
2060
  writtenFiles.push(readmeFilePath);
2061
2061
  }
2062
- if (shouldCreateStories) {
2062
+ if (shouldCreateStories && (!fs.existsSync(storiesFilePath) || options.force)) {
2063
2063
  const relativePath = `./${path.basename(featureFilePath)}`;
2064
2064
  const storiesContent = generateStoriesTemplate(featureComponentName, relativePath);
2065
2065
  const hash = await writeFileWithSignature(storiesFilePath, storiesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -2872,30 +2872,37 @@ async function createComponentCommand(componentName, options) {
2872
2872
  console.log(` Sub-components: ${subComponentsDir}/`);
2873
2873
  return;
2874
2874
  }
2875
- await ensureNotExists(componentFilePath, options.force);
2876
- await ensureNotExists(indexFilePath, options.force);
2877
- if (shouldCreateContext)
2878
- await ensureNotExists(contextFilePath, options.force);
2879
- if (shouldCreateHook)
2880
- await ensureNotExists(hookFilePath, options.force);
2881
- if (shouldCreateTests)
2882
- await ensureNotExists(testFilePath, options.force);
2883
- if (shouldCreateConfig)
2884
- await ensureNotExists(configFilePath, options.force);
2885
- if (shouldCreateConstants)
2886
- await ensureNotExists(constantsFilePath, options.force);
2887
- if (shouldCreateTypes)
2888
- await ensureNotExists(typesFilePath, options.force);
2889
- if (shouldCreateApi)
2890
- await ensureNotExists(apiFilePath, options.force);
2891
- if (shouldCreateServices)
2892
- await ensureNotExists(servicesFilePath, options.force);
2893
- if (shouldCreateSchemas)
2894
- await ensureNotExists(schemasFilePath, options.force);
2895
- if (shouldCreateReadme)
2896
- await ensureNotExists(readmeFilePath, options.force);
2897
- if (shouldCreateStories)
2898
- await ensureNotExists(storiesFilePath, options.force);
2875
+ const componentExists = fs.existsSync(componentFilePath);
2876
+ if (componentExists && !options.force) {
2877
+ console.log(`ℹ Component already exists at ${componentFilePath}. Entering additive mode.`);
2878
+ }
2879
+ // Check sub-items only if not in force mode
2880
+ if (!options.force) {
2881
+ if (fs.existsSync(indexFilePath))
2882
+ console.log(` - Skipping existing index: ${indexFilePath}`);
2883
+ if (shouldCreateContext && fs.existsSync(contextFilePath))
2884
+ console.log(` - Skipping existing context: ${contextFilePath}`);
2885
+ if (shouldCreateHook && fs.existsSync(hookFilePath))
2886
+ console.log(` - Skipping existing hook: ${hookFilePath}`);
2887
+ if (shouldCreateTests && fs.existsSync(testFilePath))
2888
+ console.log(` - Skipping existing test: ${testFilePath}`);
2889
+ if (shouldCreateConfig && fs.existsSync(configFilePath))
2890
+ console.log(` - Skipping existing config: ${configFilePath}`);
2891
+ if (shouldCreateConstants && fs.existsSync(constantsFilePath))
2892
+ console.log(` - Skipping existing constants: ${constantsFilePath}`);
2893
+ if (shouldCreateTypes && fs.existsSync(typesFilePath))
2894
+ console.log(` - Skipping existing types: ${typesFilePath}`);
2895
+ if (shouldCreateApi && fs.existsSync(apiFilePath))
2896
+ console.log(` - Skipping existing api: ${apiFilePath}`);
2897
+ if (shouldCreateServices && fs.existsSync(servicesFilePath))
2898
+ console.log(` - Skipping existing services: ${servicesFilePath}`);
2899
+ if (shouldCreateSchemas && fs.existsSync(schemasFilePath))
2900
+ console.log(` - Skipping existing schemas: ${schemasFilePath}`);
2901
+ if (shouldCreateReadme && fs.existsSync(readmeFilePath))
2902
+ console.log(` - Skipping existing readme: ${readmeFilePath}`);
2903
+ if (shouldCreateStories && fs.existsSync(storiesFilePath))
2904
+ console.log(` - Skipping existing stories: ${storiesFilePath}`);
2905
+ }
2899
2906
  await ensureDir(componentDir);
2900
2907
  if (shouldCreateSubComponentsDir)
2901
2908
  await ensureDir(subComponentsDir);
@@ -2919,24 +2926,29 @@ async function createComponentCommand(componentName, options) {
2919
2926
  await ensureDir(schemasDirInside);
2920
2927
  const componentContent = generateComponentTemplate(normalizedName, framework, config.naming.componentExtension);
2921
2928
  const signature = getSignature(config, config.naming.componentExtension === '.astro' ? 'astro' : 'tsx');
2922
- const componentHash = await writeFileWithSignature(componentFilePath, componentContent, signature, config.hashing?.normalization);
2923
- await registerFile(componentFilePath, {
2924
- kind: 'component',
2925
- template: 'component',
2926
- hash: componentHash,
2927
- owner: normalizedName
2928
- });
2929
- const writtenFiles = [componentFilePath];
2930
- const indexContent = generateIndexTemplate(normalizedName, config.naming.componentExtension);
2931
- const indexHash = await writeFileWithSignature(indexFilePath, indexContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2932
- await registerFile(indexFilePath, {
2933
- kind: 'component-file',
2934
- template: 'index',
2935
- hash: indexHash,
2936
- owner: normalizedName
2937
- });
2938
- writtenFiles.push(indexFilePath);
2939
- if (shouldCreateTypes) {
2929
+ const writtenFiles = [];
2930
+ if (!componentExists || options.force) {
2931
+ const componentHash = await writeFileWithSignature(componentFilePath, componentContent, signature, config.hashing?.normalization);
2932
+ await registerFile(componentFilePath, {
2933
+ kind: 'component',
2934
+ template: 'component',
2935
+ hash: componentHash,
2936
+ owner: normalizedName
2937
+ });
2938
+ writtenFiles.push(componentFilePath);
2939
+ }
2940
+ if (!fs.existsSync(indexFilePath) || options.force) {
2941
+ const indexContent = generateIndexTemplate(normalizedName, config.naming.componentExtension);
2942
+ const indexHash = await writeFileWithSignature(indexFilePath, indexContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2943
+ await registerFile(indexFilePath, {
2944
+ kind: 'component-file',
2945
+ template: 'index',
2946
+ hash: indexHash,
2947
+ owner: normalizedName
2948
+ });
2949
+ writtenFiles.push(indexFilePath);
2950
+ }
2951
+ if (shouldCreateTypes && (!fs.existsSync(typesFilePath) || options.force)) {
2940
2952
  const typesContent = generateTypesTemplate(normalizedName);
2941
2953
  const hash = await writeFileWithSignature(typesFilePath, typesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2942
2954
  await registerFile(typesFilePath, {
@@ -2947,7 +2959,7 @@ async function createComponentCommand(componentName, options) {
2947
2959
  });
2948
2960
  writtenFiles.push(typesFilePath);
2949
2961
  }
2950
- if (shouldCreateContext) {
2962
+ if (shouldCreateContext && (!fs.existsSync(contextFilePath) || options.force)) {
2951
2963
  const contextContent = generateContextTemplate(normalizedName);
2952
2964
  const hash = await writeFileWithSignature(contextFilePath, contextContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2953
2965
  await registerFile(contextFilePath, {
@@ -2958,7 +2970,7 @@ async function createComponentCommand(componentName, options) {
2958
2970
  });
2959
2971
  writtenFiles.push(contextFilePath);
2960
2972
  }
2961
- if (shouldCreateHook) {
2973
+ if (shouldCreateHook && (!fs.existsSync(hookFilePath) || options.force)) {
2962
2974
  const hookName = getHookFunctionName(normalizedName);
2963
2975
  const hookContent = generateHookTemplate(normalizedName, hookName);
2964
2976
  const hash = await writeFileWithSignature(hookFilePath, hookContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -2970,7 +2982,7 @@ async function createComponentCommand(componentName, options) {
2970
2982
  });
2971
2983
  writtenFiles.push(hookFilePath);
2972
2984
  }
2973
- if (shouldCreateTests) {
2985
+ if (shouldCreateTests && (!fs.existsSync(testFilePath) || options.force)) {
2974
2986
  const relativeComponentPath = `../${normalizedName}${config.naming.componentExtension}`;
2975
2987
  const testContent = generateTestTemplate(normalizedName, relativeComponentPath);
2976
2988
  const hash = await writeFileWithSignature(testFilePath, testContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -2982,7 +2994,7 @@ async function createComponentCommand(componentName, options) {
2982
2994
  });
2983
2995
  writtenFiles.push(testFilePath);
2984
2996
  }
2985
- if (shouldCreateConfig) {
2997
+ if (shouldCreateConfig && (!fs.existsSync(configFilePath) || options.force)) {
2986
2998
  const configContent = generateConfigTemplate(normalizedName);
2987
2999
  const hash = await writeFileWithSignature(configFilePath, configContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2988
3000
  await registerFile(configFilePath, {
@@ -2993,7 +3005,7 @@ async function createComponentCommand(componentName, options) {
2993
3005
  });
2994
3006
  writtenFiles.push(configFilePath);
2995
3007
  }
2996
- if (shouldCreateConstants) {
3008
+ if (shouldCreateConstants && (!fs.existsSync(constantsFilePath) || options.force)) {
2997
3009
  const constantsContent = generateConstantsTemplate(normalizedName);
2998
3010
  const hash = await writeFileWithSignature(constantsFilePath, constantsContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2999
3011
  await registerFile(constantsFilePath, {
@@ -3004,7 +3016,7 @@ async function createComponentCommand(componentName, options) {
3004
3016
  });
3005
3017
  writtenFiles.push(constantsFilePath);
3006
3018
  }
3007
- if (shouldCreateApi) {
3019
+ if (shouldCreateApi && (!fs.existsSync(apiFilePath) || options.force)) {
3008
3020
  const apiContent = generateApiTemplate(normalizedName);
3009
3021
  const hash = await writeFileWithSignature(apiFilePath, apiContent, getSignature(config, 'typescript'), config.hashing?.normalization);
3010
3022
  await registerFile(apiFilePath, {
@@ -3015,7 +3027,7 @@ async function createComponentCommand(componentName, options) {
3015
3027
  });
3016
3028
  writtenFiles.push(apiFilePath);
3017
3029
  }
3018
- if (shouldCreateServices) {
3030
+ if (shouldCreateServices && (!fs.existsSync(servicesFilePath) || options.force)) {
3019
3031
  const servicesContent = generateServiceTemplate(normalizedName);
3020
3032
  const hash = await writeFileWithSignature(servicesFilePath, servicesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
3021
3033
  await registerFile(servicesFilePath, {
@@ -3026,7 +3038,7 @@ async function createComponentCommand(componentName, options) {
3026
3038
  });
3027
3039
  writtenFiles.push(servicesFilePath);
3028
3040
  }
3029
- if (shouldCreateSchemas) {
3041
+ if (shouldCreateSchemas && (!fs.existsSync(schemasFilePath) || options.force)) {
3030
3042
  const schemasContent = generateSchemaTemplate(normalizedName);
3031
3043
  const hash = await writeFileWithSignature(schemasFilePath, schemasContent, getSignature(config, 'typescript'), config.hashing?.normalization);
3032
3044
  await registerFile(schemasFilePath, {
@@ -3037,7 +3049,7 @@ async function createComponentCommand(componentName, options) {
3037
3049
  });
3038
3050
  writtenFiles.push(schemasFilePath);
3039
3051
  }
3040
- if (shouldCreateReadme) {
3052
+ if (shouldCreateReadme && (!fs.existsSync(readmeFilePath) || options.force)) {
3041
3053
  const readmeContent = generateReadmeTemplate(normalizedName);
3042
3054
  const hash = await writeFileWithSignature(readmeFilePath, readmeContent, getSignature(config, 'astro'), config.hashing?.normalization);
3043
3055
  await registerFile(readmeFilePath, {
@@ -3048,7 +3060,7 @@ async function createComponentCommand(componentName, options) {
3048
3060
  });
3049
3061
  writtenFiles.push(readmeFilePath);
3050
3062
  }
3051
- if (shouldCreateStories) {
3063
+ if (shouldCreateStories && (!fs.existsSync(storiesFilePath) || options.force)) {
3052
3064
  const relativePath = `./${normalizedName}${config.naming.componentExtension}`;
3053
3065
  const storiesContent = generateStoriesTemplate(normalizedName, relativePath);
3054
3066
  const hash = await writeFileWithSignature(storiesFilePath, storiesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -3987,6 +3999,61 @@ async function renameComponent(oldName, newName, options) {
3987
3999
  console.log(`✓ Renamed component ${normalizedOldName} to ${normalizedNewName}`);
3988
4000
  }
3989
4001
 
4002
+ /**
4003
+ * Add a new item (hook, api, service, etc.) to an existing feature or component.
4004
+ *
4005
+ * @param {string} itemType The type of item to add (e.g., 'api', 'hook', 'service')
4006
+ * @param {string} targetName The name of the feature or component
4007
+ * @param {Object} options Additional options from Commander
4008
+ */
4009
+ async function addItemCommand(itemType, targetName, options) {
4010
+ try {
4011
+ const state = await loadState();
4012
+ // Normalize itemType
4013
+ let normalizedItem = itemType.toLowerCase();
4014
+ if (normalizedItem === 'test')
4015
+ normalizedItem = 'tests';
4016
+ if (normalizedItem === 'service')
4017
+ normalizedItem = 'services';
4018
+ if (normalizedItem === 'schema')
4019
+ normalizedItem = 'schemas';
4020
+ if (normalizedItem === 'hook')
4021
+ normalizedItem = 'hooks'; // for add-section
4022
+ // Try to find as section (feature) first
4023
+ let section = findSection(state, targetName);
4024
+ let component = findComponent(state, targetName);
4025
+ // If not found by exact name, try to find by featurePath or part of it
4026
+ if (!section && !component) {
4027
+ section = state.sections.find(s => s.featurePath === targetName || s.featurePath.endsWith('/' + targetName));
4028
+ }
4029
+ if (!section && !component) {
4030
+ throw new Error(`Target not found in state: "${targetName}". Please use "add-section" or "create-component" directly if it's not managed by Textor.`);
4031
+ }
4032
+ const flags = { [normalizedItem]: true };
4033
+ // Also set singular for create-component which uses 'hook'
4034
+ if (normalizedItem === 'hooks')
4035
+ flags.hook = true;
4036
+ if (section) {
4037
+ console.log(`ℹ Adding ${normalizedItem} to feature: ${section.featurePath}`);
4038
+ return await addSectionCommand(undefined, section.featurePath, { ...options, ...flags });
4039
+ }
4040
+ if (component) {
4041
+ console.log(`ℹ Adding ${normalizedItem} to component: ${component.name}`);
4042
+ // For create-component, we might need to be careful with flags that are on by default
4043
+ // but getEffectiveOptions should handle it if we pass them explicitly as true.
4044
+ return await createComponentCommand(component.name, { ...options, ...flags });
4045
+ }
4046
+ }
4047
+ catch (error) {
4048
+ console.error('Error:', error.message);
4049
+ if (typeof process.exit === 'function' && process.env.NODE_ENV !== 'test') {
4050
+ process.exit(1);
4051
+ }
4052
+ throw error;
4053
+ }
4054
+ }
4055
+
4056
+ exports.addItemCommand = addItemCommand;
3990
4057
  exports.addSectionCommand = addSectionCommand;
3991
4058
  exports.adoptCommand = adoptCommand;
3992
4059
  exports.createComponentCommand = createComponentCommand;