@oamm/textor 1.0.13 → 1.0.16

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
  }
@@ -1544,18 +1536,26 @@ function reconstructSections(state, config) {
1544
1536
  // Keep existing sections if their files still exist
1545
1537
  const validSections = (state.sections || []).filter(section => {
1546
1538
  // Check if route file exists in state.files
1547
- const routeFile = Object.keys(files).find(f => {
1539
+ const routeFile = section.route ? Object.keys(files).find(f => {
1548
1540
  const normalizedF = f.replace(/\\/g, '/');
1549
1541
  const routePath = section.route === '/' ? 'index' : section.route.slice(1);
1550
1542
  return normalizedF.startsWith(pagesRoot + '/' + routePath + '.') ||
1551
1543
  normalizedF === pagesRoot + '/' + routePath + '/index.astro'; // nested mode
1552
- });
1544
+ }) : true;
1553
1545
  // Check if feature directory has at least one file in state.files
1554
- const hasFeatureFiles = Object.keys(files).some(f => f.replace(/\\/g, '/').startsWith(section.featurePath.replace(/\\/g, '/') + '/'));
1546
+ const hasFeatureFiles = section.featurePath ? Object.keys(files).some(f => {
1547
+ const normalizedF = f.replace(/\\/g, '/');
1548
+ const featPath = section.featurePath.replace(/\\/g, '/');
1549
+ return normalizedF.startsWith(featPath + '/') ||
1550
+ normalizedF.startsWith(featuresRoot + '/' + featPath + '/');
1551
+ }) : false;
1555
1552
  return routeFile && hasFeatureFiles;
1556
1553
  });
1557
1554
  const sections = new Map();
1558
- validSections.forEach(s => sections.set(s.route, s));
1555
+ validSections.forEach(s => {
1556
+ const key = s.route || `feature:${s.featurePath}`;
1557
+ sections.set(key, s);
1558
+ });
1559
1559
  // Try to discover new sections
1560
1560
  for (const filePath in files) {
1561
1561
  const normalizedPath = filePath.replace(/\\/g, '/');
@@ -1796,29 +1796,35 @@ async function addSectionCommand(route, featurePath, options) {
1796
1796
  }
1797
1797
  }
1798
1798
  }
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);
1799
+ const featureExists = fs.existsSync(featureFilePath);
1800
+ if (featureExists && !options.force) {
1801
+ console.log(`ℹ Feature already exists at ${featureFilePath}. Entering additive mode.`);
1802
+ }
1803
+ // Check sub-items only if not in force mode
1804
+ if (!options.force) {
1805
+ if (shouldCreateIndex && fs.existsSync(indexFilePath))
1806
+ console.log(` - Skipping existing index: ${indexFilePath}`);
1807
+ if (shouldCreateContext && fs.existsSync(contextFilePath))
1808
+ console.log(` - Skipping existing context: ${contextFilePath}`);
1809
+ if (shouldCreateHooks && fs.existsSync(hookFilePath))
1810
+ console.log(` - Skipping existing hook: ${hookFilePath}`);
1811
+ if (shouldCreateTests && fs.existsSync(testFilePath))
1812
+ console.log(` - Skipping existing test: ${testFilePath}`);
1813
+ if (shouldCreateTypes && fs.existsSync(typesFilePath))
1814
+ console.log(` - Skipping existing types: ${typesFilePath}`);
1815
+ if (shouldCreateApi && fs.existsSync(apiFilePath))
1816
+ console.log(` - Skipping existing api: ${apiFilePath}`);
1817
+ if (shouldCreateServices && fs.existsSync(servicesFilePath))
1818
+ console.log(` - Skipping existing services: ${servicesFilePath}`);
1819
+ if (shouldCreateSchemas && fs.existsSync(schemasFilePath))
1820
+ console.log(` - Skipping existing schemas: ${schemasFilePath}`);
1821
+ if (shouldCreateReadme && fs.existsSync(readmeFilePath))
1822
+ console.log(` - Skipping existing readme: ${readmeFilePath}`);
1823
+ if (shouldCreateStories && fs.existsSync(storiesFilePath))
1824
+ console.log(` - Skipping existing stories: ${storiesFilePath}`);
1825
+ if (shouldCreateScriptsDir && fs.existsSync(scriptsIndexPath))
1826
+ console.log(` - Skipping existing scripts: ${scriptsIndexPath}`);
1827
+ }
1822
1828
  let layoutImportPath = null;
1823
1829
  const cliProps = options.prop || {};
1824
1830
  const rawLayoutProps = { ...configLayoutProps, ...cliProps };
@@ -1940,15 +1946,17 @@ async function addSectionCommand(route, featurePath, options) {
1940
1946
  if (shouldCreateTypes)
1941
1947
  await ensureDir(typesDirInside);
1942
1948
  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) {
1949
+ if (!featureExists || options.force) {
1950
+ const featureHash = await writeFileWithSignature(featureFilePath, featureContent, featureSignature, config.hashing?.normalization);
1951
+ await registerFile(featureFilePath, {
1952
+ kind: 'feature',
1953
+ template: 'feature',
1954
+ hash: featureHash,
1955
+ owner: normalizedRoute
1956
+ });
1957
+ writtenFiles.push(featureFilePath);
1958
+ }
1959
+ if (shouldCreateScriptsDir && (!fs.existsSync(scriptsIndexPath) || options.force)) {
1952
1960
  const hash = await writeFileWithSignature(scriptsIndexPath, generateScriptsIndexTemplate(), getSignature(config, 'typescript'), config.hashing?.normalization);
1953
1961
  await registerFile(scriptsIndexPath, {
1954
1962
  kind: 'feature-file',
@@ -1958,7 +1966,7 @@ async function addSectionCommand(route, featurePath, options) {
1958
1966
  });
1959
1967
  writtenFiles.push(scriptsIndexPath);
1960
1968
  }
1961
- if (shouldCreateIndex) {
1969
+ if (shouldCreateIndex && (!fs.existsSync(indexFilePath) || options.force)) {
1962
1970
  const indexContent = generateIndexTemplate(featureComponentName, config.naming.featureExtension);
1963
1971
  const hash = await writeFileWithSignature(indexFilePath, indexContent, getSignature(config, 'typescript'), config.hashing?.normalization);
1964
1972
  await registerFile(indexFilePath, {
@@ -1969,7 +1977,7 @@ async function addSectionCommand(route, featurePath, options) {
1969
1977
  });
1970
1978
  writtenFiles.push(indexFilePath);
1971
1979
  }
1972
- if (shouldCreateApi) {
1980
+ if (shouldCreateApi && (!fs.existsSync(apiFilePath) || options.force)) {
1973
1981
  const apiContent = generateApiTemplate(featureComponentName);
1974
1982
  const hash = await writeFileWithSignature(apiFilePath, apiContent, getSignature(config, 'typescript'), config.hashing?.normalization);
1975
1983
  await registerFile(apiFilePath, {
@@ -1980,7 +1988,7 @@ async function addSectionCommand(route, featurePath, options) {
1980
1988
  });
1981
1989
  writtenFiles.push(apiFilePath);
1982
1990
  }
1983
- if (shouldCreateServices) {
1991
+ if (shouldCreateServices && (!fs.existsSync(servicesFilePath) || options.force)) {
1984
1992
  const servicesContent = generateServiceTemplate(featureComponentName);
1985
1993
  const hash = await writeFileWithSignature(servicesFilePath, servicesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
1986
1994
  await registerFile(servicesFilePath, {
@@ -1991,7 +1999,7 @@ async function addSectionCommand(route, featurePath, options) {
1991
1999
  });
1992
2000
  writtenFiles.push(servicesFilePath);
1993
2001
  }
1994
- if (shouldCreateSchemas) {
2002
+ if (shouldCreateSchemas && (!fs.existsSync(schemasFilePath) || options.force)) {
1995
2003
  const schemasContent = generateSchemaTemplate(featureComponentName);
1996
2004
  const hash = await writeFileWithSignature(schemasFilePath, schemasContent, getSignature(config, 'typescript'), config.hashing?.normalization);
1997
2005
  await registerFile(schemasFilePath, {
@@ -2002,7 +2010,7 @@ async function addSectionCommand(route, featurePath, options) {
2002
2010
  });
2003
2011
  writtenFiles.push(schemasFilePath);
2004
2012
  }
2005
- if (shouldCreateHooks) {
2013
+ if (shouldCreateHooks && (!fs.existsSync(hookFilePath) || options.force)) {
2006
2014
  const hookName = getHookFunctionName(featureComponentName);
2007
2015
  const hookContent = generateHookTemplate(featureComponentName, hookName);
2008
2016
  const hash = await writeFileWithSignature(hookFilePath, hookContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -2014,7 +2022,7 @@ async function addSectionCommand(route, featurePath, options) {
2014
2022
  });
2015
2023
  writtenFiles.push(hookFilePath);
2016
2024
  }
2017
- if (shouldCreateContext) {
2025
+ if (shouldCreateContext && (!fs.existsSync(contextFilePath) || options.force)) {
2018
2026
  const contextContent = generateContextTemplate(featureComponentName);
2019
2027
  const hash = await writeFileWithSignature(contextFilePath, contextContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2020
2028
  await registerFile(contextFilePath, {
@@ -2025,7 +2033,7 @@ async function addSectionCommand(route, featurePath, options) {
2025
2033
  });
2026
2034
  writtenFiles.push(contextFilePath);
2027
2035
  }
2028
- if (shouldCreateTests) {
2036
+ if (shouldCreateTests && (!fs.existsSync(testFilePath) || options.force)) {
2029
2037
  const relativeFeaturePath = `./${path.basename(featureFilePath)}`;
2030
2038
  const testContent = generateTestTemplate(featureComponentName, relativeFeaturePath);
2031
2039
  const hash = await writeFileWithSignature(testFilePath, testContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -2037,7 +2045,7 @@ async function addSectionCommand(route, featurePath, options) {
2037
2045
  });
2038
2046
  writtenFiles.push(testFilePath);
2039
2047
  }
2040
- if (shouldCreateTypes) {
2048
+ if (shouldCreateTypes && (!fs.existsSync(typesFilePath) || options.force)) {
2041
2049
  const typesContent = generateTypesTemplate(featureComponentName);
2042
2050
  const hash = await writeFileWithSignature(typesFilePath, typesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2043
2051
  await registerFile(typesFilePath, {
@@ -2048,7 +2056,7 @@ async function addSectionCommand(route, featurePath, options) {
2048
2056
  });
2049
2057
  writtenFiles.push(typesFilePath);
2050
2058
  }
2051
- if (shouldCreateReadme) {
2059
+ if (shouldCreateReadme && (!fs.existsSync(readmeFilePath) || options.force)) {
2052
2060
  const readmeContent = generateReadmeTemplate(featureComponentName);
2053
2061
  const hash = await writeFileWithSignature(readmeFilePath, readmeContent, getSignature(config, 'astro'), config.hashing?.normalization);
2054
2062
  await registerFile(readmeFilePath, {
@@ -2059,7 +2067,7 @@ async function addSectionCommand(route, featurePath, options) {
2059
2067
  });
2060
2068
  writtenFiles.push(readmeFilePath);
2061
2069
  }
2062
- if (shouldCreateStories) {
2070
+ if (shouldCreateStories && (!fs.existsSync(storiesFilePath) || options.force)) {
2063
2071
  const relativePath = `./${path.basename(featureFilePath)}`;
2064
2072
  const storiesContent = generateStoriesTemplate(featureComponentName, relativePath);
2065
2073
  const hash = await writeFileWithSignature(storiesFilePath, storiesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -2872,30 +2880,37 @@ async function createComponentCommand(componentName, options) {
2872
2880
  console.log(` Sub-components: ${subComponentsDir}/`);
2873
2881
  return;
2874
2882
  }
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);
2883
+ const componentExists = fs.existsSync(componentFilePath);
2884
+ if (componentExists && !options.force) {
2885
+ console.log(`ℹ Component already exists at ${componentFilePath}. Entering additive mode.`);
2886
+ }
2887
+ // Check sub-items only if not in force mode
2888
+ if (!options.force) {
2889
+ if (fs.existsSync(indexFilePath))
2890
+ console.log(` - Skipping existing index: ${indexFilePath}`);
2891
+ if (shouldCreateContext && fs.existsSync(contextFilePath))
2892
+ console.log(` - Skipping existing context: ${contextFilePath}`);
2893
+ if (shouldCreateHook && fs.existsSync(hookFilePath))
2894
+ console.log(` - Skipping existing hook: ${hookFilePath}`);
2895
+ if (shouldCreateTests && fs.existsSync(testFilePath))
2896
+ console.log(` - Skipping existing test: ${testFilePath}`);
2897
+ if (shouldCreateConfig && fs.existsSync(configFilePath))
2898
+ console.log(` - Skipping existing config: ${configFilePath}`);
2899
+ if (shouldCreateConstants && fs.existsSync(constantsFilePath))
2900
+ console.log(` - Skipping existing constants: ${constantsFilePath}`);
2901
+ if (shouldCreateTypes && fs.existsSync(typesFilePath))
2902
+ console.log(` - Skipping existing types: ${typesFilePath}`);
2903
+ if (shouldCreateApi && fs.existsSync(apiFilePath))
2904
+ console.log(` - Skipping existing api: ${apiFilePath}`);
2905
+ if (shouldCreateServices && fs.existsSync(servicesFilePath))
2906
+ console.log(` - Skipping existing services: ${servicesFilePath}`);
2907
+ if (shouldCreateSchemas && fs.existsSync(schemasFilePath))
2908
+ console.log(` - Skipping existing schemas: ${schemasFilePath}`);
2909
+ if (shouldCreateReadme && fs.existsSync(readmeFilePath))
2910
+ console.log(` - Skipping existing readme: ${readmeFilePath}`);
2911
+ if (shouldCreateStories && fs.existsSync(storiesFilePath))
2912
+ console.log(` - Skipping existing stories: ${storiesFilePath}`);
2913
+ }
2899
2914
  await ensureDir(componentDir);
2900
2915
  if (shouldCreateSubComponentsDir)
2901
2916
  await ensureDir(subComponentsDir);
@@ -2919,24 +2934,29 @@ async function createComponentCommand(componentName, options) {
2919
2934
  await ensureDir(schemasDirInside);
2920
2935
  const componentContent = generateComponentTemplate(normalizedName, framework, config.naming.componentExtension);
2921
2936
  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) {
2937
+ const writtenFiles = [];
2938
+ if (!componentExists || options.force) {
2939
+ const componentHash = await writeFileWithSignature(componentFilePath, componentContent, signature, config.hashing?.normalization);
2940
+ await registerFile(componentFilePath, {
2941
+ kind: 'component',
2942
+ template: 'component',
2943
+ hash: componentHash,
2944
+ owner: normalizedName
2945
+ });
2946
+ writtenFiles.push(componentFilePath);
2947
+ }
2948
+ if (!fs.existsSync(indexFilePath) || options.force) {
2949
+ const indexContent = generateIndexTemplate(normalizedName, config.naming.componentExtension);
2950
+ const indexHash = await writeFileWithSignature(indexFilePath, indexContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2951
+ await registerFile(indexFilePath, {
2952
+ kind: 'component-file',
2953
+ template: 'index',
2954
+ hash: indexHash,
2955
+ owner: normalizedName
2956
+ });
2957
+ writtenFiles.push(indexFilePath);
2958
+ }
2959
+ if (shouldCreateTypes && (!fs.existsSync(typesFilePath) || options.force)) {
2940
2960
  const typesContent = generateTypesTemplate(normalizedName);
2941
2961
  const hash = await writeFileWithSignature(typesFilePath, typesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2942
2962
  await registerFile(typesFilePath, {
@@ -2947,7 +2967,7 @@ async function createComponentCommand(componentName, options) {
2947
2967
  });
2948
2968
  writtenFiles.push(typesFilePath);
2949
2969
  }
2950
- if (shouldCreateContext) {
2970
+ if (shouldCreateContext && (!fs.existsSync(contextFilePath) || options.force)) {
2951
2971
  const contextContent = generateContextTemplate(normalizedName);
2952
2972
  const hash = await writeFileWithSignature(contextFilePath, contextContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2953
2973
  await registerFile(contextFilePath, {
@@ -2958,7 +2978,7 @@ async function createComponentCommand(componentName, options) {
2958
2978
  });
2959
2979
  writtenFiles.push(contextFilePath);
2960
2980
  }
2961
- if (shouldCreateHook) {
2981
+ if (shouldCreateHook && (!fs.existsSync(hookFilePath) || options.force)) {
2962
2982
  const hookName = getHookFunctionName(normalizedName);
2963
2983
  const hookContent = generateHookTemplate(normalizedName, hookName);
2964
2984
  const hash = await writeFileWithSignature(hookFilePath, hookContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -2970,7 +2990,7 @@ async function createComponentCommand(componentName, options) {
2970
2990
  });
2971
2991
  writtenFiles.push(hookFilePath);
2972
2992
  }
2973
- if (shouldCreateTests) {
2993
+ if (shouldCreateTests && (!fs.existsSync(testFilePath) || options.force)) {
2974
2994
  const relativeComponentPath = `../${normalizedName}${config.naming.componentExtension}`;
2975
2995
  const testContent = generateTestTemplate(normalizedName, relativeComponentPath);
2976
2996
  const hash = await writeFileWithSignature(testFilePath, testContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -2982,7 +3002,7 @@ async function createComponentCommand(componentName, options) {
2982
3002
  });
2983
3003
  writtenFiles.push(testFilePath);
2984
3004
  }
2985
- if (shouldCreateConfig) {
3005
+ if (shouldCreateConfig && (!fs.existsSync(configFilePath) || options.force)) {
2986
3006
  const configContent = generateConfigTemplate(normalizedName);
2987
3007
  const hash = await writeFileWithSignature(configFilePath, configContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2988
3008
  await registerFile(configFilePath, {
@@ -2993,7 +3013,7 @@ async function createComponentCommand(componentName, options) {
2993
3013
  });
2994
3014
  writtenFiles.push(configFilePath);
2995
3015
  }
2996
- if (shouldCreateConstants) {
3016
+ if (shouldCreateConstants && (!fs.existsSync(constantsFilePath) || options.force)) {
2997
3017
  const constantsContent = generateConstantsTemplate(normalizedName);
2998
3018
  const hash = await writeFileWithSignature(constantsFilePath, constantsContent, getSignature(config, 'typescript'), config.hashing?.normalization);
2999
3019
  await registerFile(constantsFilePath, {
@@ -3004,7 +3024,7 @@ async function createComponentCommand(componentName, options) {
3004
3024
  });
3005
3025
  writtenFiles.push(constantsFilePath);
3006
3026
  }
3007
- if (shouldCreateApi) {
3027
+ if (shouldCreateApi && (!fs.existsSync(apiFilePath) || options.force)) {
3008
3028
  const apiContent = generateApiTemplate(normalizedName);
3009
3029
  const hash = await writeFileWithSignature(apiFilePath, apiContent, getSignature(config, 'typescript'), config.hashing?.normalization);
3010
3030
  await registerFile(apiFilePath, {
@@ -3015,7 +3035,7 @@ async function createComponentCommand(componentName, options) {
3015
3035
  });
3016
3036
  writtenFiles.push(apiFilePath);
3017
3037
  }
3018
- if (shouldCreateServices) {
3038
+ if (shouldCreateServices && (!fs.existsSync(servicesFilePath) || options.force)) {
3019
3039
  const servicesContent = generateServiceTemplate(normalizedName);
3020
3040
  const hash = await writeFileWithSignature(servicesFilePath, servicesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
3021
3041
  await registerFile(servicesFilePath, {
@@ -3026,7 +3046,7 @@ async function createComponentCommand(componentName, options) {
3026
3046
  });
3027
3047
  writtenFiles.push(servicesFilePath);
3028
3048
  }
3029
- if (shouldCreateSchemas) {
3049
+ if (shouldCreateSchemas && (!fs.existsSync(schemasFilePath) || options.force)) {
3030
3050
  const schemasContent = generateSchemaTemplate(normalizedName);
3031
3051
  const hash = await writeFileWithSignature(schemasFilePath, schemasContent, getSignature(config, 'typescript'), config.hashing?.normalization);
3032
3052
  await registerFile(schemasFilePath, {
@@ -3037,7 +3057,7 @@ async function createComponentCommand(componentName, options) {
3037
3057
  });
3038
3058
  writtenFiles.push(schemasFilePath);
3039
3059
  }
3040
- if (shouldCreateReadme) {
3060
+ if (shouldCreateReadme && (!fs.existsSync(readmeFilePath) || options.force)) {
3041
3061
  const readmeContent = generateReadmeTemplate(normalizedName);
3042
3062
  const hash = await writeFileWithSignature(readmeFilePath, readmeContent, getSignature(config, 'astro'), config.hashing?.normalization);
3043
3063
  await registerFile(readmeFilePath, {
@@ -3048,7 +3068,7 @@ async function createComponentCommand(componentName, options) {
3048
3068
  });
3049
3069
  writtenFiles.push(readmeFilePath);
3050
3070
  }
3051
- if (shouldCreateStories) {
3071
+ if (shouldCreateStories && (!fs.existsSync(storiesFilePath) || options.force)) {
3052
3072
  const relativePath = `./${normalizedName}${config.naming.componentExtension}`;
3053
3073
  const storiesContent = generateStoriesTemplate(normalizedName, relativePath);
3054
3074
  const hash = await writeFileWithSignature(storiesFilePath, storiesContent, getSignature(config, 'typescript'), config.hashing?.normalization);
@@ -3626,7 +3646,24 @@ async function syncCommand(options) {
3626
3646
  }
3627
3647
  }
3628
3648
 
3629
- async function adoptCommand(identifier, options) {
3649
+ async function adoptCommand(kind, arg2, arg3, options) {
3650
+ // Handle cases where options is in a different position due to variable arguments
3651
+ if (typeof kind === 'object' && kind !== null && !Array.isArray(kind)) {
3652
+ options = kind;
3653
+ kind = undefined;
3654
+ arg2 = undefined;
3655
+ arg3 = undefined;
3656
+ }
3657
+ else if (typeof arg2 === 'object' && arg2 !== null && !Array.isArray(arg2)) {
3658
+ options = arg2;
3659
+ arg2 = undefined;
3660
+ arg3 = undefined;
3661
+ }
3662
+ else if (typeof arg3 === 'object' && arg3 !== null && !Array.isArray(arg3)) {
3663
+ options = arg3;
3664
+ arg3 = undefined;
3665
+ }
3666
+ options = options || {};
3630
3667
  try {
3631
3668
  const config = await loadConfig();
3632
3669
  const state = await loadState();
@@ -3636,6 +3673,9 @@ async function adoptCommand(identifier, options) {
3636
3673
  components: resolvePath(config, 'components')
3637
3674
  };
3638
3675
  let filesToAdopt = [];
3676
+ let identifier = kind;
3677
+ let customName = null;
3678
+ let sourcePath = null;
3639
3679
  if (!identifier && options.all) {
3640
3680
  // Adopt all untracked files in all roots
3641
3681
  const managedFiles = new Set();
@@ -3646,6 +3686,46 @@ async function adoptCommand(identifier, options) {
3646
3686
  }
3647
3687
  filesToAdopt = Array.from(managedFiles).filter(f => !state.files[f]);
3648
3688
  }
3689
+ else if (kind === 'component' && arg2) {
3690
+ const componentName = arg2;
3691
+ const untrackedFiles = new Set();
3692
+ const compPath = path.join(roots.components, componentName);
3693
+ if (fs.existsSync(compPath)) {
3694
+ await scanDirectoryOrFile(compPath, untrackedFiles, state);
3695
+ }
3696
+ else {
3697
+ throw new Error(`Component directory not found: ${compPath}`);
3698
+ }
3699
+ filesToAdopt = Array.from(untrackedFiles);
3700
+ }
3701
+ else if (kind === 'feature' && arg2 && arg3) {
3702
+ sourcePath = arg2;
3703
+ customName = arg3;
3704
+ const targetPath = path.join(roots.features, customName);
3705
+ const absoluteSource = path.resolve(process.cwd(), sourcePath);
3706
+ const absoluteTarget = path.resolve(targetPath);
3707
+ if (fs.existsSync(absoluteSource)) {
3708
+ if (!options.dryRun && absoluteSource !== absoluteTarget) {
3709
+ if (!fs.existsSync(path.dirname(absoluteTarget))) {
3710
+ await promises.mkdir(path.dirname(absoluteTarget), { recursive: true });
3711
+ }
3712
+ console.log(`Moving files from ${sourcePath} to ${path.relative(process.cwd(), targetPath)}...`);
3713
+ await promises.rename(absoluteSource, absoluteTarget);
3714
+ sourcePath = path.relative(process.cwd(), targetPath);
3715
+ }
3716
+ else if (options.dryRun && absoluteSource !== absoluteTarget) {
3717
+ console.log(`Dry run: would move files from ${sourcePath} to ${path.relative(process.cwd(), targetPath)}`);
3718
+ sourcePath = path.relative(process.cwd(), targetPath);
3719
+ }
3720
+ const untrackedFiles = new Set();
3721
+ const fullPath = absoluteSource !== absoluteTarget && !options.dryRun ? absoluteTarget : absoluteSource;
3722
+ await scanDirectoryOrFile(fullPath, untrackedFiles, state);
3723
+ filesToAdopt = Array.from(untrackedFiles);
3724
+ }
3725
+ else {
3726
+ throw new Error(`Source path not found: ${absoluteSource}`);
3727
+ }
3728
+ }
3649
3729
  else if (identifier) {
3650
3730
  const untrackedFiles = new Set();
3651
3731
  // 1. Try as direct path
@@ -3987,6 +4067,61 @@ async function renameComponent(oldName, newName, options) {
3987
4067
  console.log(`✓ Renamed component ${normalizedOldName} to ${normalizedNewName}`);
3988
4068
  }
3989
4069
 
4070
+ /**
4071
+ * Add a new item (hook, api, service, etc.) to an existing feature or component.
4072
+ *
4073
+ * @param {string} itemType The type of item to add (e.g., 'api', 'hook', 'service')
4074
+ * @param {string} targetName The name of the feature or component
4075
+ * @param {Object} options Additional options from Commander
4076
+ */
4077
+ async function addItemCommand(itemType, targetName, options) {
4078
+ try {
4079
+ const state = await loadState();
4080
+ // Normalize itemType
4081
+ let normalizedItem = itemType.toLowerCase();
4082
+ if (normalizedItem === 'test')
4083
+ normalizedItem = 'tests';
4084
+ if (normalizedItem === 'service')
4085
+ normalizedItem = 'services';
4086
+ if (normalizedItem === 'schema')
4087
+ normalizedItem = 'schemas';
4088
+ if (normalizedItem === 'hook')
4089
+ normalizedItem = 'hooks'; // for add-section
4090
+ // Try to find as section (feature) first
4091
+ let section = findSection(state, targetName);
4092
+ let component = findComponent(state, targetName);
4093
+ // If not found by exact name, try to find by featurePath or part of it
4094
+ if (!section && !component) {
4095
+ section = state.sections.find(s => s.featurePath === targetName || s.featurePath.endsWith('/' + targetName));
4096
+ }
4097
+ if (!section && !component) {
4098
+ throw new Error(`Target not found in state: "${targetName}". Please use "add-section" or "create-component" directly if it's not managed by Textor.`);
4099
+ }
4100
+ const flags = { [normalizedItem]: true };
4101
+ // Also set singular for create-component which uses 'hook'
4102
+ if (normalizedItem === 'hooks')
4103
+ flags.hook = true;
4104
+ if (section) {
4105
+ console.log(`ℹ Adding ${normalizedItem} to feature: ${section.featurePath}`);
4106
+ return await addSectionCommand(undefined, section.featurePath, { ...options, ...flags });
4107
+ }
4108
+ if (component) {
4109
+ console.log(`ℹ Adding ${normalizedItem} to component: ${component.name}`);
4110
+ // For create-component, we might need to be careful with flags that are on by default
4111
+ // but getEffectiveOptions should handle it if we pass them explicitly as true.
4112
+ return await createComponentCommand(component.name, { ...options, ...flags });
4113
+ }
4114
+ }
4115
+ catch (error) {
4116
+ console.error('Error:', error.message);
4117
+ if (typeof process.exit === 'function' && process.env.NODE_ENV !== 'test') {
4118
+ process.exit(1);
4119
+ }
4120
+ throw error;
4121
+ }
4122
+ }
4123
+
4124
+ exports.addItemCommand = addItemCommand;
3990
4125
  exports.addSectionCommand = addSectionCommand;
3991
4126
  exports.adoptCommand = adoptCommand;
3992
4127
  exports.createComponentCommand = createComponentCommand;