@oamm/textor 1.0.10 → 1.0.12

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.
@@ -647,7 +647,8 @@ async function verifyFileIntegrity(filePath, expectedHash, options = {}) {
647
647
  acceptChanges = false,
648
648
  normalization = 'normalizeEOL',
649
649
  owner = null,
650
- actualOwner = null
650
+ actualOwner = null,
651
+ signatures = []
651
652
  } = options;
652
653
 
653
654
  if (force) return { valid: true };
@@ -660,7 +661,7 @@ async function verifyFileIntegrity(filePath, expectedHash, options = {}) {
660
661
  };
661
662
  }
662
663
 
663
- const isGenerated = await isTextorGenerated(filePath);
664
+ const isGenerated = await isTextorGenerated(filePath, signatures);
664
665
  if (!isGenerated) {
665
666
  return {
666
667
  valid: false,
@@ -693,7 +694,7 @@ async function verifyFileIntegrity(filePath, expectedHash, options = {}) {
693
694
  }
694
695
 
695
696
  async function safeDelete(filePath, options = {}) {
696
- const { force = false, expectedHash = null, acceptChanges = false, owner = null, actualOwner = null } = options;
697
+ const { force = false, expectedHash = null, acceptChanges = false, owner = null, actualOwner = null, signatures = [] } = options;
697
698
 
698
699
  if (!existsSync(filePath)) {
699
700
  return { deleted: false, reason: 'not-found' };
@@ -703,7 +704,8 @@ async function safeDelete(filePath, options = {}) {
703
704
  force,
704
705
  acceptChanges,
705
706
  owner,
706
- actualOwner
707
+ actualOwner,
708
+ signatures
707
709
  });
708
710
  if (!integrity.valid) {
709
711
  return { deleted: false, reason: integrity.reason, message: integrity.message };
@@ -745,7 +747,8 @@ async function isSafeToDeleteDir(dirPath, stateFiles = {}, options = {}) {
745
747
  const fileState = stateFiles[normalizedPath];
746
748
  const integrity = await verifyFileIntegrity(filePath, fileState?.hash, {
747
749
  ...options,
748
- actualOwner: fileState?.owner
750
+ actualOwner: fileState?.owner,
751
+ signatures: options.signatures || []
749
752
  });
750
753
  return integrity.valid;
751
754
  })
@@ -841,7 +844,8 @@ async function safeMove(fromPath, toPath, options = {}) {
841
844
  acceptChanges,
842
845
  normalization,
843
846
  owner,
844
- actualOwner
847
+ actualOwner,
848
+ signatures: options.signatures || []
845
849
  });
846
850
  if (!integrity.valid) {
847
851
  throw new Error(integrity.message);
@@ -2060,9 +2064,14 @@ async function addSectionCommand(route, featurePath, options) {
2060
2064
 
2061
2065
  if (routeFilePath) {
2062
2066
  if (existsSync(routeFilePath)) {
2063
- const isGenerated = await isTextorGenerated(routeFilePath);
2067
+ const configSignatures = Object.values(config.signatures || {});
2068
+ const isGenerated = await isTextorGenerated(routeFilePath, configSignatures);
2064
2069
  if (!isGenerated && !options.force) {
2065
- throw new Error(`File already exists: ${routeFilePath}\nUse --force to overwrite.`);
2070
+ if (routeFilePath.endsWith('.astro')) {
2071
+ console.log(`⚠ File already exists and is not managed by Textor. Adopting and merging: ${routeFilePath}`);
2072
+ } else {
2073
+ throw new Error(`File already exists: ${routeFilePath}\nUse --force to overwrite.`);
2074
+ }
2066
2075
  }
2067
2076
  }
2068
2077
  }
@@ -2526,6 +2535,7 @@ async function removeSectionCommand(route, featurePath, options) {
2526
2535
 
2527
2536
  const pagesRoot = resolvePath(config, 'pages');
2528
2537
  const featuresRoot = resolvePath(config, 'features');
2538
+ const configSignatures = Object.values(config.signatures || {});
2529
2539
 
2530
2540
  // Find route file in state if possible
2531
2541
  let routeFilePath = null;
@@ -2575,7 +2585,8 @@ async function removeSectionCommand(route, featurePath, options) {
2575
2585
  acceptChanges: options.acceptChanges,
2576
2586
  normalization: config.hashing?.normalization,
2577
2587
  owner: normalizedRoute,
2578
- actualOwner: fileState?.owner
2588
+ actualOwner: fileState?.owner,
2589
+ signatures: configSignatures
2579
2590
  });
2580
2591
 
2581
2592
  if (result.deleted) {
@@ -2592,7 +2603,8 @@ async function removeSectionCommand(route, featurePath, options) {
2592
2603
  stateFiles: state.files,
2593
2604
  acceptChanges: options.acceptChanges,
2594
2605
  normalization: config.hashing?.normalization,
2595
- owner: normalizedRoute
2606
+ owner: normalizedRoute,
2607
+ signatures: configSignatures
2596
2608
  });
2597
2609
 
2598
2610
  if (result.deleted) {
@@ -2819,6 +2831,7 @@ async function moveSectionCommand(fromRoute, fromFeature, toRoute, toFeature, op
2819
2831
 
2820
2832
  const pagesRoot = resolvePath(config, 'pages');
2821
2833
  const featuresRoot = resolvePath(config, 'features');
2834
+ const configSignatures = Object.values(config.signatures || {});
2822
2835
 
2823
2836
  const fromSection = findSection(state, actualFromRoute);
2824
2837
  const routeExtension = (fromSection && fromSection.extension) || config.naming.routeExtension;
@@ -2860,7 +2873,8 @@ async function moveSectionCommand(fromRoute, fromFeature, toRoute, toFeature, op
2860
2873
  expectedHash: routeFileState?.hash,
2861
2874
  acceptChanges: options.acceptChanges,
2862
2875
  owner: normalizedFromRoute,
2863
- actualOwner: routeFileState?.owner
2876
+ actualOwner: routeFileState?.owner,
2877
+ signatures: configSignatures
2864
2878
  });
2865
2879
  movedFiles.push({ from: fromRoutePath, to: toRoutePath });
2866
2880
 
@@ -2957,7 +2971,8 @@ async function moveSectionCommand(fromRoute, fromFeature, toRoute, toFeature, op
2957
2971
  ...options,
2958
2972
  fromName: fromFeatureComponentName,
2959
2973
  toName: toFeatureComponentName,
2960
- owner: normalizedFromRoute
2974
+ owner: normalizedFromRoute,
2975
+ signatures: configSignatures
2961
2976
  });
2962
2977
  movedFiles.push({ from: fromFeaturePath, to: toFeaturePath });
2963
2978
 
@@ -3666,6 +3681,7 @@ async function createComponentCommand(componentName, options) {
3666
3681
  async function removeComponentCommand(identifier, options) {
3667
3682
  try {
3668
3683
  const config = await loadConfig();
3684
+ const configSignatures = Object.values(config.signatures || {});
3669
3685
 
3670
3686
  if (config.git?.requireCleanRepo && !await isRepoClean()) {
3671
3687
  throw new Error('Git repository is not clean. Please commit or stash your changes before proceeding.');
@@ -3695,7 +3711,8 @@ async function removeComponentCommand(identifier, options) {
3695
3711
  stateFiles: state.files,
3696
3712
  acceptChanges: options.acceptChanges,
3697
3713
  normalization: config.hashing?.normalization,
3698
- owner: identifier
3714
+ owner: identifier,
3715
+ signatures: configSignatures
3699
3716
  });
3700
3717
 
3701
3718
  if (result.deleted || (result.reason === 'not-found' && component)) {
@@ -3744,7 +3761,8 @@ async function listSectionsCommand() {
3744
3761
  console.log(' No pages directory found.');
3745
3762
  } else {
3746
3763
  const extensions = [config.naming.routeExtension, '.ts', '.js'];
3747
- const sections = await findGeneratedFiles(pagesRoot, extensions);
3764
+ const signatures = Object.values(config.signatures || {});
3765
+ const sections = await findGeneratedFiles(pagesRoot, extensions, signatures);
3748
3766
 
3749
3767
  if (sections.length === 0) {
3750
3768
  console.log(' No Textor-managed sections found.');
@@ -3848,7 +3866,7 @@ async function listSectionsCommand() {
3848
3866
  }
3849
3867
  }
3850
3868
 
3851
- async function findGeneratedFiles(dir, extensions) {
3869
+ async function findGeneratedFiles(dir, extensions, signatures) {
3852
3870
  const results = [];
3853
3871
  const entries = await readdir(dir);
3854
3872
  const exts = Array.isArray(extensions) ? extensions : [extensions];
@@ -3858,9 +3876,9 @@ async function findGeneratedFiles(dir, extensions) {
3858
3876
  const stats = await stat(fullPath);
3859
3877
 
3860
3878
  if (stats.isDirectory()) {
3861
- results.push(...await findGeneratedFiles(fullPath, exts));
3879
+ results.push(...await findGeneratedFiles(fullPath, exts, signatures));
3862
3880
  } else if (exts.some(ext => entry.endsWith(ext))) {
3863
- if (await isTextorGenerated(fullPath)) {
3881
+ if (await isTextorGenerated(fullPath, signatures)) {
3864
3882
  results.push(fullPath);
3865
3883
  }
3866
3884
  }
@@ -3918,11 +3936,12 @@ async function validateStateCommand(options) {
3918
3936
 
3919
3937
  if (options.fix) {
3920
3938
  let fixedCount = 0;
3939
+ const signatures = Object.values(config.signatures || {});
3921
3940
 
3922
3941
  // Fix modified files if they still have the Textor signature
3923
3942
  for (const mod of results.modified) {
3924
3943
  const fullPath = path.join(process.cwd(), mod.path);
3925
- if (await isTextorGenerated(fullPath)) {
3944
+ if (await isTextorGenerated(fullPath, signatures)) {
3926
3945
  state.files[mod.path].hash = mod.newHash;
3927
3946
  fixedCount++;
3928
3947
  }
@@ -4348,8 +4367,9 @@ async function adoptFile(relPath, config, state, options) {
4348
4367
  else if (ext === '.js' || ext === '.jsx') signature = config.signatures.javascript;
4349
4368
 
4350
4369
  let finalContent = content;
4370
+ const shouldAddSignature = signature && !content.includes(signature) && options.signature !== false;
4351
4371
 
4352
- if (signature && !content.includes(signature)) {
4372
+ if (shouldAddSignature) {
4353
4373
  if (options.dryRun) {
4354
4374
  console.log(` ~ Would add signature to ${relPath}`);
4355
4375
  } else {
@@ -4359,9 +4379,17 @@ async function adoptFile(relPath, config, state, options) {
4359
4379
  }
4360
4380
  } else {
4361
4381
  if (options.dryRun) {
4362
- console.log(` + Would adopt (already has signature or no signature for ext): ${relPath}`);
4382
+ if (signature && !content.includes(signature) && options.signature === false) {
4383
+ console.log(` + Would adopt without signature (explicitly requested): ${relPath}`);
4384
+ } else {
4385
+ console.log(` + Would adopt (already has signature or no signature for ext): ${relPath}`);
4386
+ }
4363
4387
  } else {
4364
- console.log(` + Adopting: ${relPath}`);
4388
+ if (signature && !content.includes(signature) && options.signature === false) {
4389
+ console.log(` + Adopting without signature (explicitly requested): ${relPath}`);
4390
+ } else {
4391
+ console.log(` + Adopting: ${relPath}`);
4392
+ }
4365
4393
  }
4366
4394
  }
4367
4395
 
@@ -4371,7 +4399,8 @@ async function adoptFile(relPath, config, state, options) {
4371
4399
  kind: inferKind(relPath, config),
4372
4400
  hash: hash,
4373
4401
  timestamp: new Date().toISOString(),
4374
- synced: true
4402
+ synced: true,
4403
+ hasSignature: options.signature !== false
4375
4404
  };
4376
4405
  }
4377
4406
 
@@ -1 +1 @@
1
- {"version":3,"file":"textor.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"textor.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.cjs CHANGED
@@ -578,7 +578,7 @@ async function isTextorGenerated(filePath, customSignatures = []) {
578
578
  }
579
579
  }
580
580
  async function verifyFileIntegrity(filePath, expectedHash, options = {}) {
581
- const { force = false, acceptChanges = false, normalization = 'normalizeEOL', owner = null, actualOwner = null } = options;
581
+ const { force = false, acceptChanges = false, normalization = 'normalizeEOL', owner = null, actualOwner = null, signatures = [] } = options;
582
582
  if (force)
583
583
  return { valid: true };
584
584
  if (owner && actualOwner && owner !== actualOwner) {
@@ -588,7 +588,7 @@ async function verifyFileIntegrity(filePath, expectedHash, options = {}) {
588
588
  message: `Refusing to operate on ${filePath} - owned by ${actualOwner}, but requested by ${owner}. Use --force to override.`
589
589
  };
590
590
  }
591
- const isGenerated = await isTextorGenerated(filePath);
591
+ const isGenerated = await isTextorGenerated(filePath, signatures);
592
592
  if (!isGenerated) {
593
593
  return {
594
594
  valid: false,
@@ -617,7 +617,7 @@ async function verifyFileIntegrity(filePath, expectedHash, options = {}) {
617
617
  return { valid: true };
618
618
  }
619
619
  async function safeDelete(filePath, options = {}) {
620
- const { force = false, expectedHash = null, acceptChanges = false, owner = null, actualOwner = null } = options;
620
+ const { force = false, expectedHash = null, acceptChanges = false, owner = null, actualOwner = null, signatures = [] } = options;
621
621
  if (!fs.existsSync(filePath)) {
622
622
  return { deleted: false, reason: 'not-found' };
623
623
  }
@@ -625,7 +625,8 @@ async function safeDelete(filePath, options = {}) {
625
625
  force,
626
626
  acceptChanges,
627
627
  owner,
628
- actualOwner
628
+ actualOwner,
629
+ signatures
629
630
  });
630
631
  if (!integrity.valid) {
631
632
  return { deleted: false, reason: integrity.reason, message: integrity.message };
@@ -657,7 +658,8 @@ async function isSafeToDeleteDir(dirPath, stateFiles = {}, options = {}) {
657
658
  const fileState = stateFiles[normalizedPath];
658
659
  const integrity = await verifyFileIntegrity(filePath, fileState?.hash, {
659
660
  ...options,
660
- actualOwner: fileState?.owner
661
+ actualOwner: fileState?.owner,
662
+ signatures: options.signatures || []
661
663
  });
662
664
  return integrity.valid;
663
665
  }));
@@ -722,7 +724,8 @@ async function safeMove(fromPath, toPath, options = {}) {
722
724
  acceptChanges,
723
725
  normalization,
724
726
  owner,
725
- actualOwner
727
+ actualOwner,
728
+ signatures: options.signatures || []
726
729
  });
727
730
  if (!integrity.valid) {
728
731
  throw new Error(integrity.message);
@@ -1774,9 +1777,15 @@ async function addSectionCommand(route, featurePath, options) {
1774
1777
  }
1775
1778
  if (routeFilePath) {
1776
1779
  if (fs.existsSync(routeFilePath)) {
1777
- const isGenerated = await isTextorGenerated(routeFilePath);
1780
+ const configSignatures = Object.values(config.signatures || {});
1781
+ const isGenerated = await isTextorGenerated(routeFilePath, configSignatures);
1778
1782
  if (!isGenerated && !options.force) {
1779
- throw new Error(`File already exists: ${routeFilePath}\nUse --force to overwrite.`);
1783
+ if (routeFilePath.endsWith('.astro')) {
1784
+ console.log(`⚠ File already exists and is not managed by Textor. Adopting and merging: ${routeFilePath}`);
1785
+ }
1786
+ else {
1787
+ throw new Error(`File already exists: ${routeFilePath}\nUse --force to overwrite.`);
1788
+ }
1780
1789
  }
1781
1790
  }
1782
1791
  }
@@ -2154,6 +2163,7 @@ async function removeSectionCommand(route, featurePath, options) {
2154
2163
  const normalizedFeaturePath = featureToDirectoryPath(targetFeaturePath);
2155
2164
  const pagesRoot = resolvePath(config, 'pages');
2156
2165
  const featuresRoot = resolvePath(config, 'features');
2166
+ const configSignatures = Object.values(config.signatures || {});
2157
2167
  // Find route file in state if possible
2158
2168
  let routeFilePath = null;
2159
2169
  const routeRelPath = Object.keys(state.files).find(f => {
@@ -2195,7 +2205,8 @@ async function removeSectionCommand(route, featurePath, options) {
2195
2205
  acceptChanges: options.acceptChanges,
2196
2206
  normalization: config.hashing?.normalization,
2197
2207
  owner: normalizedRoute,
2198
- actualOwner: fileState?.owner
2208
+ actualOwner: fileState?.owner,
2209
+ signatures: configSignatures
2199
2210
  });
2200
2211
  if (result.deleted) {
2201
2212
  deletedFiles.push(routeFilePath);
@@ -2211,7 +2222,8 @@ async function removeSectionCommand(route, featurePath, options) {
2211
2222
  stateFiles: state.files,
2212
2223
  acceptChanges: options.acceptChanges,
2213
2224
  normalization: config.hashing?.normalization,
2214
- owner: normalizedRoute
2225
+ owner: normalizedRoute,
2226
+ signatures: configSignatures
2215
2227
  });
2216
2228
  if (result.deleted) {
2217
2229
  deletedDirs.push(featureDirPath);
@@ -2407,6 +2419,7 @@ async function moveSectionCommand(fromRoute, fromFeature, toRoute, toFeature, op
2407
2419
  const normalizedToFeature = actualToFeature ? featureToDirectoryPath(actualToFeature) : null;
2408
2420
  const pagesRoot = resolvePath(config, 'pages');
2409
2421
  const featuresRoot = resolvePath(config, 'features');
2422
+ const configSignatures = Object.values(config.signatures || {});
2410
2423
  const fromSection = findSection(state, actualFromRoute);
2411
2424
  const routeExtension = (fromSection && fromSection.extension) || config.naming.routeExtension;
2412
2425
  const fromRouteFile = routeToFilePath(normalizedFromRoute, {
@@ -2439,7 +2452,8 @@ async function moveSectionCommand(fromRoute, fromFeature, toRoute, toFeature, op
2439
2452
  expectedHash: routeFileState?.hash,
2440
2453
  acceptChanges: options.acceptChanges,
2441
2454
  owner: normalizedFromRoute,
2442
- actualOwner: routeFileState?.owner
2455
+ actualOwner: routeFileState?.owner,
2456
+ signatures: configSignatures
2443
2457
  });
2444
2458
  movedFiles.push({ from: fromRoutePath, to: toRoutePath });
2445
2459
  // Update state for moved route file
@@ -2518,7 +2532,8 @@ async function moveSectionCommand(fromRoute, fromFeature, toRoute, toFeature, op
2518
2532
  ...options,
2519
2533
  fromName: fromFeatureComponentName,
2520
2534
  toName: toFeatureComponentName,
2521
- owner: normalizedFromRoute
2535
+ owner: normalizedFromRoute,
2536
+ signatures: configSignatures
2522
2537
  });
2523
2538
  movedFiles.push({ from: fromFeaturePath, to: toFeaturePath });
2524
2539
  await cleanupEmptyDirs(path.dirname(fromFeaturePath), featuresRoot);
@@ -3047,6 +3062,7 @@ async function createComponentCommand(componentName, options) {
3047
3062
  async function removeComponentCommand(identifier, options) {
3048
3063
  try {
3049
3064
  const config = await loadConfig();
3065
+ const configSignatures = Object.values(config.signatures || {});
3050
3066
  if (config.git?.requireCleanRepo && !await isRepoClean()) {
3051
3067
  throw new Error('Git repository is not clean. Please commit or stash your changes before proceeding.');
3052
3068
  }
@@ -3071,7 +3087,8 @@ async function removeComponentCommand(identifier, options) {
3071
3087
  stateFiles: state.files,
3072
3088
  acceptChanges: options.acceptChanges,
3073
3089
  normalization: config.hashing?.normalization,
3074
- owner: identifier
3090
+ owner: identifier,
3091
+ signatures: configSignatures
3075
3092
  });
3076
3093
  if (result.deleted || (result.reason === 'not-found' && component)) {
3077
3094
  if (result.deleted) {
@@ -3120,7 +3137,8 @@ async function listSectionsCommand() {
3120
3137
  }
3121
3138
  else {
3122
3139
  const extensions = [config.naming.routeExtension, '.ts', '.js'];
3123
- const sections = await findGeneratedFiles(pagesRoot, extensions);
3140
+ const signatures = Object.values(config.signatures || {});
3141
+ const sections = await findGeneratedFiles(pagesRoot, extensions, signatures);
3124
3142
  if (sections.length === 0) {
3125
3143
  console.log(' No Textor-managed sections found.');
3126
3144
  }
@@ -3215,7 +3233,7 @@ async function listSectionsCommand() {
3215
3233
  process.exit(1);
3216
3234
  }
3217
3235
  }
3218
- async function findGeneratedFiles(dir, extensions) {
3236
+ async function findGeneratedFiles(dir, extensions, signatures) {
3219
3237
  const results = [];
3220
3238
  const entries = await promises.readdir(dir);
3221
3239
  const exts = Array.isArray(extensions) ? extensions : [extensions];
@@ -3223,10 +3241,10 @@ async function findGeneratedFiles(dir, extensions) {
3223
3241
  const fullPath = path.join(dir, entry);
3224
3242
  const stats = await promises.stat(fullPath);
3225
3243
  if (stats.isDirectory()) {
3226
- results.push(...await findGeneratedFiles(fullPath, exts));
3244
+ results.push(...await findGeneratedFiles(fullPath, exts, signatures));
3227
3245
  }
3228
3246
  else if (exts.some(ext => entry.endsWith(ext))) {
3229
- if (await isTextorGenerated(fullPath)) {
3247
+ if (await isTextorGenerated(fullPath, signatures)) {
3230
3248
  results.push(fullPath);
3231
3249
  }
3232
3250
  }
@@ -3275,10 +3293,11 @@ async function validateStateCommand(options) {
3275
3293
  }
3276
3294
  if (options.fix) {
3277
3295
  let fixedCount = 0;
3296
+ const signatures = Object.values(config.signatures || {});
3278
3297
  // Fix modified files if they still have the Textor signature
3279
3298
  for (const mod of results.modified) {
3280
3299
  const fullPath = path.join(process.cwd(), mod.path);
3281
- if (await isTextorGenerated(fullPath)) {
3300
+ if (await isTextorGenerated(fullPath, signatures)) {
3282
3301
  state.files[mod.path].hash = mod.newHash;
3283
3302
  fixedCount++;
3284
3303
  }
@@ -3660,7 +3679,8 @@ async function adoptFile(relPath, config, state, options) {
3660
3679
  else if (ext === '.js' || ext === '.jsx')
3661
3680
  signature = config.signatures.javascript;
3662
3681
  let finalContent = content;
3663
- if (signature && !content.includes(signature)) {
3682
+ const shouldAddSignature = signature && !content.includes(signature) && options.signature !== false;
3683
+ if (shouldAddSignature) {
3664
3684
  if (options.dryRun) {
3665
3685
  console.log(` ~ Would add signature to ${relPath}`);
3666
3686
  }
@@ -3672,10 +3692,20 @@ async function adoptFile(relPath, config, state, options) {
3672
3692
  }
3673
3693
  else {
3674
3694
  if (options.dryRun) {
3675
- console.log(` + Would adopt (already has signature or no signature for ext): ${relPath}`);
3695
+ if (signature && !content.includes(signature) && options.signature === false) {
3696
+ console.log(` + Would adopt without signature (explicitly requested): ${relPath}`);
3697
+ }
3698
+ else {
3699
+ console.log(` + Would adopt (already has signature or no signature for ext): ${relPath}`);
3700
+ }
3676
3701
  }
3677
3702
  else {
3678
- console.log(` + Adopting: ${relPath}`);
3703
+ if (signature && !content.includes(signature) && options.signature === false) {
3704
+ console.log(` + Adopting without signature (explicitly requested): ${relPath}`);
3705
+ }
3706
+ else {
3707
+ console.log(` + Adopting: ${relPath}`);
3708
+ }
3679
3709
  }
3680
3710
  }
3681
3711
  if (!options.dryRun) {
@@ -3684,7 +3714,8 @@ async function adoptFile(relPath, config, state, options) {
3684
3714
  kind: inferKind(relPath, config),
3685
3715
  hash: hash,
3686
3716
  timestamp: new Date().toISOString(),
3687
- synced: true
3717
+ synced: true,
3718
+ hasSignature: options.signature !== false
3688
3719
  };
3689
3720
  }
3690
3721
  return true;