@git.zone/cli 2.13.10 → 2.13.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.
Files changed (105) hide show
  1. package/assets/templates/{npmextra/npmextra.json → smartconfig/_smartconfig.json} +3 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/classes.gitzoneconfig.js +4 -4
  4. package/dist_ts/gitzone.cli.js +1 -15
  5. package/dist_ts/mod_commit/index.js +4 -4
  6. package/dist_ts/mod_config/classes.commitconfig.d.ts +3 -3
  7. package/dist_ts/mod_config/classes.commitconfig.js +20 -20
  8. package/dist_ts/mod_config/classes.releaseconfig.d.ts +3 -3
  9. package/dist_ts/mod_config/classes.releaseconfig.js +21 -21
  10. package/dist_ts/mod_config/index.js +12 -12
  11. package/dist_ts/mod_format/classes.baseformatter.d.ts +2 -9
  12. package/dist_ts/mod_format/classes.baseformatter.js +13 -26
  13. package/dist_ts/mod_format/classes.formatplanner.d.ts +1 -3
  14. package/dist_ts/mod_format/classes.formatplanner.js +11 -39
  15. package/dist_ts/mod_format/classes.formatstats.d.ts +0 -5
  16. package/dist_ts/mod_format/classes.formatstats.js +3 -35
  17. package/dist_ts/mod_format/formatters/cleanup.formatter.js +1 -2
  18. package/dist_ts/mod_format/formatters/copy.formatter.js +5 -8
  19. package/dist_ts/mod_format/formatters/gitignore.formatter.d.ts +5 -0
  20. package/dist_ts/mod_format/formatters/gitignore.formatter.js +23 -29
  21. package/dist_ts/mod_format/formatters/packagejson.formatter.js +5 -5
  22. package/dist_ts/mod_format/formatters/prettier.formatter.js +4 -12
  23. package/dist_ts/mod_format/formatters/{npmextra.formatter.d.ts → smartconfig.formatter.d.ts} +6 -1
  24. package/dist_ts/mod_format/formatters/smartconfig.formatter.js +180 -0
  25. package/dist_ts/mod_format/formatters/templates.formatter.d.ts +4 -0
  26. package/dist_ts/mod_format/formatters/templates.formatter.js +50 -43
  27. package/dist_ts/mod_format/formatters/tsconfig.formatter.js +6 -4
  28. package/dist_ts/mod_format/index.d.ts +0 -4
  29. package/dist_ts/mod_format/index.js +27 -78
  30. package/dist_ts/mod_format/interfaces.format.d.ts +1 -18
  31. package/dist_ts/mod_format/interfaces.format.js +16 -2
  32. package/dist_ts/mod_format/mod.plugins.d.ts +3 -7
  33. package/dist_ts/mod_format/mod.plugins.js +4 -8
  34. package/dist_ts/mod_services/classes.globalregistry.js +2 -2
  35. package/dist_ts/mod_services/classes.servicemanager.d.ts +2 -2
  36. package/dist_ts/mod_services/classes.servicemanager.js +21 -21
  37. package/dist_ts/plugins.d.ts +2 -2
  38. package/dist_ts/plugins.js +3 -3
  39. package/package.json +5 -11
  40. package/ts/00_commitinfo_data.ts +1 -1
  41. package/ts/classes.gitzoneconfig.ts +3 -3
  42. package/ts/gitzone.cli.ts +0 -17
  43. package/ts/mod_commit/index.ts +3 -3
  44. package/ts/mod_config/classes.commitconfig.ts +19 -19
  45. package/ts/mod_config/classes.releaseconfig.ts +20 -20
  46. package/ts/mod_config/index.ts +11 -11
  47. package/ts/mod_format/classes.baseformatter.ts +14 -25
  48. package/ts/mod_format/classes.formatplanner.ts +10 -39
  49. package/ts/mod_format/classes.formatstats.ts +2 -42
  50. package/ts/mod_format/formatters/cleanup.formatter.ts +0 -1
  51. package/ts/mod_format/formatters/copy.formatter.ts +4 -8
  52. package/ts/mod_format/formatters/gitignore.formatter.ts +25 -29
  53. package/ts/mod_format/formatters/packagejson.formatter.ts +4 -4
  54. package/ts/mod_format/formatters/prettier.formatter.ts +3 -13
  55. package/ts/mod_format/formatters/smartconfig.formatter.ts +213 -0
  56. package/ts/mod_format/formatters/templates.formatter.ts +55 -43
  57. package/ts/mod_format/formatters/tsconfig.formatter.ts +6 -3
  58. package/ts/mod_format/index.ts +35 -98
  59. package/ts/mod_format/interfaces.format.ts +16 -19
  60. package/ts/mod_format/mod.plugins.ts +4 -12
  61. package/ts/mod_services/classes.globalregistry.ts +2 -2
  62. package/ts/mod_services/classes.servicemanager.ts +20 -20
  63. package/ts/plugins.ts +2 -2
  64. package/dist_ts/mod_format/classes.changecache.d.ts +0 -26
  65. package/dist_ts/mod_format/classes.changecache.js +0 -181
  66. package/dist_ts/mod_format/classes.dependency-analyzer.d.ts +0 -13
  67. package/dist_ts/mod_format/classes.dependency-analyzer.js +0 -93
  68. package/dist_ts/mod_format/classes.rollbackmanager.d.ts +0 -22
  69. package/dist_ts/mod_format/classes.rollbackmanager.js +0 -259
  70. package/dist_ts/mod_format/format.cleanup.d.ts +0 -2
  71. package/dist_ts/mod_format/format.cleanup.js +0 -25
  72. package/dist_ts/mod_format/format.copy.d.ts +0 -24
  73. package/dist_ts/mod_format/format.copy.js +0 -77
  74. package/dist_ts/mod_format/format.gitignore.d.ts +0 -2
  75. package/dist_ts/mod_format/format.gitignore.js +0 -48
  76. package/dist_ts/mod_format/format.license.d.ts +0 -2
  77. package/dist_ts/mod_format/format.license.js +0 -26
  78. package/dist_ts/mod_format/format.npmextra.d.ts +0 -5
  79. package/dist_ts/mod_format/format.npmextra.js +0 -125
  80. package/dist_ts/mod_format/format.packagejson.d.ts +0 -2
  81. package/dist_ts/mod_format/format.packagejson.js +0 -145
  82. package/dist_ts/mod_format/format.prettier.d.ts +0 -2
  83. package/dist_ts/mod_format/format.prettier.js +0 -56
  84. package/dist_ts/mod_format/format.readme.d.ts +0 -1
  85. package/dist_ts/mod_format/format.readme.js +0 -29
  86. package/dist_ts/mod_format/format.templates.d.ts +0 -5
  87. package/dist_ts/mod_format/format.templates.js +0 -68
  88. package/dist_ts/mod_format/format.tsconfig.d.ts +0 -2
  89. package/dist_ts/mod_format/format.tsconfig.js +0 -28
  90. package/dist_ts/mod_format/formatters/npmextra.formatter.js +0 -142
  91. package/ts/mod_format/classes.changecache.ts +0 -235
  92. package/ts/mod_format/classes.dependency-analyzer.ts +0 -117
  93. package/ts/mod_format/classes.rollbackmanager.ts +0 -340
  94. package/ts/mod_format/format.cleanup.ts +0 -26
  95. package/ts/mod_format/format.copy.ts +0 -93
  96. package/ts/mod_format/format.gitignore.ts +0 -54
  97. package/ts/mod_format/format.license.ts +0 -32
  98. package/ts/mod_format/format.npmextra.ts +0 -151
  99. package/ts/mod_format/format.packagejson.ts +0 -196
  100. package/ts/mod_format/format.prettier.ts +0 -66
  101. package/ts/mod_format/format.readme.ts +0 -29
  102. package/ts/mod_format/format.templates.ts +0 -79
  103. package/ts/mod_format/format.tsconfig.ts +0 -31
  104. package/ts/mod_format/formatters/npmextra.formatter.ts +0 -174
  105. /package/{npmextra.json → .smartconfig.json} +0 -0
@@ -0,0 +1,213 @@
1
+ import { BaseFormatter } from '../classes.baseformatter.js';
2
+ import type { IPlannedChange } from '../interfaces.format.js';
3
+ import * as plugins from '../mod.plugins.js';
4
+ import { logger, logVerbose } from '../../gitzone.logging.js';
5
+
6
+ /**
7
+ * Migrates .smartconfig.json from old namespace keys to new package-scoped keys
8
+ */
9
+ const migrateNamespaceKeys = (smartconfigJson: any): boolean => {
10
+ let migrated = false;
11
+ const migrations = [
12
+ { oldKey: 'gitzone', newKey: '@git.zone/cli' },
13
+ { oldKey: 'tsdoc', newKey: '@git.zone/tsdoc' },
14
+ { oldKey: 'npmdocker', newKey: '@git.zone/tsdocker' },
15
+ { oldKey: 'npmci', newKey: '@ship.zone/szci' },
16
+ { oldKey: 'szci', newKey: '@ship.zone/szci' },
17
+ ];
18
+ for (const { oldKey, newKey } of migrations) {
19
+ if (smartconfigJson[oldKey]) {
20
+ if (!smartconfigJson[newKey]) {
21
+ smartconfigJson[newKey] = smartconfigJson[oldKey];
22
+ } else {
23
+ smartconfigJson[newKey] = {
24
+ ...smartconfigJson[oldKey],
25
+ ...smartconfigJson[newKey],
26
+ };
27
+ }
28
+ delete smartconfigJson[oldKey];
29
+ migrated = true;
30
+ }
31
+ }
32
+ return migrated;
33
+ };
34
+
35
+ /**
36
+ * Migrates npmAccessLevel from @ship.zone/szci to @git.zone/cli.release.accessLevel
37
+ */
38
+ const migrateAccessLevel = (smartconfigJson: any): boolean => {
39
+ const szciConfig = smartconfigJson['@ship.zone/szci'];
40
+
41
+ if (!szciConfig?.npmAccessLevel) {
42
+ return false;
43
+ }
44
+
45
+ const gitzoneConfig = smartconfigJson['@git.zone/cli'] || {};
46
+ if (gitzoneConfig?.release?.accessLevel) {
47
+ delete szciConfig.npmAccessLevel;
48
+ return true;
49
+ }
50
+
51
+ if (!smartconfigJson['@git.zone/cli']) {
52
+ smartconfigJson['@git.zone/cli'] = {};
53
+ }
54
+ if (!smartconfigJson['@git.zone/cli'].release) {
55
+ smartconfigJson['@git.zone/cli'].release = {};
56
+ }
57
+
58
+ smartconfigJson['@git.zone/cli'].release.accessLevel = szciConfig.npmAccessLevel;
59
+ delete szciConfig.npmAccessLevel;
60
+
61
+ return true;
62
+ };
63
+
64
+ // Config file names in priority order (newest → oldest)
65
+ const CONFIG_FILE_NAMES = ['.smartconfig.json', 'smartconfig.json', 'npmextra.json'];
66
+ const TARGET_CONFIG_FILE = '.smartconfig.json';
67
+
68
+ export class SmartconfigFormatter extends BaseFormatter {
69
+ get name(): string {
70
+ return 'smartconfig';
71
+ }
72
+
73
+ /**
74
+ * Find the config file, checking in priority order.
75
+ * Returns the path and whether it needs renaming.
76
+ */
77
+ private async findConfigFile(): Promise<{ path: string; needsRename: boolean } | null> {
78
+ for (const filename of CONFIG_FILE_NAMES) {
79
+ const exists = await plugins.smartfs.file(filename).exists();
80
+ if (exists) {
81
+ return {
82
+ path: filename,
83
+ needsRename: filename !== TARGET_CONFIG_FILE,
84
+ };
85
+ }
86
+ }
87
+ return null;
88
+ }
89
+
90
+ async analyze(): Promise<IPlannedChange[]> {
91
+ const changes: IPlannedChange[] = [];
92
+
93
+ const configFile = await this.findConfigFile();
94
+ if (!configFile) {
95
+ logVerbose('No config file found (.smartconfig.json, smartconfig.json, or npmextra.json), skipping');
96
+ return changes;
97
+ }
98
+
99
+ // Read current content
100
+ const currentContent = (await plugins.smartfs
101
+ .file(configFile.path)
102
+ .encoding('utf8')
103
+ .read()) as string;
104
+
105
+ // Parse and apply migrations
106
+ const smartconfigJson = JSON.parse(currentContent);
107
+ migrateNamespaceKeys(smartconfigJson);
108
+ migrateAccessLevel(smartconfigJson);
109
+
110
+ // Ensure namespaces exist
111
+ if (!smartconfigJson['@git.zone/cli']) {
112
+ smartconfigJson['@git.zone/cli'] = {};
113
+ }
114
+ if (!smartconfigJson['@ship.zone/szci']) {
115
+ smartconfigJson['@ship.zone/szci'] = {};
116
+ }
117
+
118
+ const newContent = JSON.stringify(smartconfigJson, null, 2);
119
+
120
+ // If file needs renaming, plan a create + delete
121
+ if (configFile.needsRename) {
122
+ changes.push({
123
+ type: 'create',
124
+ path: TARGET_CONFIG_FILE,
125
+ module: this.name,
126
+ description: `Migrate ${configFile.path} to ${TARGET_CONFIG_FILE}`,
127
+ content: newContent,
128
+ });
129
+ changes.push({
130
+ type: 'delete',
131
+ path: configFile.path,
132
+ module: this.name,
133
+ description: `Remove old ${configFile.path}`,
134
+ });
135
+ } else if (newContent !== currentContent) {
136
+ // File is already .smartconfig.json, just needs content update
137
+ changes.push({
138
+ type: 'modify',
139
+ path: TARGET_CONFIG_FILE,
140
+ module: this.name,
141
+ description: 'Migrate and format .smartconfig.json',
142
+ content: newContent,
143
+ });
144
+ }
145
+
146
+ return changes;
147
+ }
148
+
149
+ async applyChange(change: IPlannedChange): Promise<void> {
150
+ if (change.type === 'delete') {
151
+ await this.deleteFile(change.path);
152
+ logger.log('info', `Removed old config file ${change.path}`);
153
+ return;
154
+ }
155
+
156
+ if (!change.content) return;
157
+
158
+ // Parse the content to check for missing required fields
159
+ const smartconfigJson = JSON.parse(change.content);
160
+
161
+ const expectedRepoInformation: string[] = [
162
+ 'projectType',
163
+ 'module.githost',
164
+ 'module.gitscope',
165
+ 'module.gitrepo',
166
+ 'module.description',
167
+ 'module.npmPackagename',
168
+ 'module.license',
169
+ ];
170
+
171
+ const interactInstance = new plugins.smartinteract.SmartInteract();
172
+ for (const expectedRepoInformationItem of expectedRepoInformation) {
173
+ if (
174
+ !plugins.smartobject.smartGet(
175
+ smartconfigJson['@git.zone/cli'],
176
+ expectedRepoInformationItem,
177
+ )
178
+ ) {
179
+ interactInstance.addQuestions([
180
+ {
181
+ message: `What is the value of ${expectedRepoInformationItem}`,
182
+ name: expectedRepoInformationItem,
183
+ type: 'input',
184
+ default: 'undefined variable',
185
+ },
186
+ ]);
187
+ }
188
+ }
189
+
190
+ const answerbucket = await interactInstance.runQueue();
191
+ for (const expectedRepoInformationItem of expectedRepoInformation) {
192
+ const cliProvidedValue = answerbucket.getAnswerFor(
193
+ expectedRepoInformationItem,
194
+ );
195
+ if (cliProvidedValue) {
196
+ plugins.smartobject.smartAdd(
197
+ smartconfigJson['@git.zone/cli'],
198
+ expectedRepoInformationItem,
199
+ cliProvidedValue,
200
+ );
201
+ }
202
+ }
203
+
204
+ const finalContent = JSON.stringify(smartconfigJson, null, 2);
205
+
206
+ if (change.type === 'create') {
207
+ await this.createFile(change.path, finalContent);
208
+ } else {
209
+ await this.modifyFile(change.path, finalContent);
210
+ }
211
+ logger.log('info', `Updated ${change.path}`);
212
+ }
213
+ }
@@ -9,6 +9,32 @@ export class TemplatesFormatter extends BaseFormatter {
9
9
  return 'templates';
10
10
  }
11
11
 
12
+ /**
13
+ * Render a template directory through smartscaf and return a map of path → content.
14
+ */
15
+ private async renderTemplate(templateName: string): Promise<Map<string, string>> {
16
+ const templateDir = plugins.path.join(paths.templatesDir, templateName);
17
+
18
+ const scafTemplate = new plugins.smartscaf.ScafTemplate(templateDir);
19
+ await scafTemplate.readTemplateFromDir();
20
+
21
+ const gitzoneData = this.project.gitzoneConfig?.data;
22
+ if (gitzoneData) {
23
+ await scafTemplate.supplyVariables({
24
+ module: gitzoneData.module,
25
+ projectType: gitzoneData.projectType,
26
+ });
27
+ }
28
+
29
+ const renderedFiles = await scafTemplate.renderToMemory();
30
+
31
+ const fileMap = new Map<string, string>();
32
+ for (const file of renderedFiles) {
33
+ fileMap.set(file.path, file.contents.toString());
34
+ }
35
+ return fileMap;
36
+ }
37
+
12
38
  async analyze(): Promise<IPlannedChange[]> {
13
39
  const changes: IPlannedChange[] = [];
14
40
  const project = this.project;
@@ -25,7 +51,8 @@ export class TemplatesFormatter extends BaseFormatter {
25
51
  switch (projectType) {
26
52
  case 'npm':
27
53
  case 'wcc':
28
- const accessLevel = project.gitzoneConfig?.data?.npmciOptions?.npmAccessLevel;
54
+ const accessLevel = (project.gitzoneConfig?.data as any)?.release?.accessLevel
55
+ || project.gitzoneConfig?.data?.npmciOptions?.npmAccessLevel;
29
56
  const ciTemplate = accessLevel === 'public' ? 'ci_default' : 'ci_default_private';
30
57
  const ciChanges = await this.analyzeTemplate(ciTemplate, [
31
58
  { templatePath: '.gitea/workflows/default_nottags.yaml', destPath: '.gitea/workflows/default_nottags.yaml' },
@@ -62,9 +89,6 @@ export class TemplatesFormatter extends BaseFormatter {
62
89
  { templatePath: 'html/index.html', destPath: 'html/index.html' },
63
90
  ]);
64
91
  changes.push(...websiteChanges);
65
- } else if (projectType === 'service') {
66
- const serviceChanges = await this.analyzeTemplate('service_update', []);
67
- changes.push(...serviceChanges);
68
92
  } else if (projectType === 'wcc') {
69
93
  const wccChanges = await this.analyzeTemplate('wcc_update', [
70
94
  { templatePath: 'html/index.html', destPath: 'html/index.html' },
@@ -83,53 +107,47 @@ export class TemplatesFormatter extends BaseFormatter {
83
107
  const changes: IPlannedChange[] = [];
84
108
  const templateDir = plugins.path.join(paths.templatesDir, templateName);
85
109
 
86
- // Check if template exists
87
110
  const templateExists = await plugins.smartfs.directory(templateDir).exists();
88
111
  if (!templateExists) {
89
112
  logVerbose(`Template ${templateName} not found`);
90
113
  return changes;
91
114
  }
92
115
 
116
+ let renderedFiles: Map<string, string>;
117
+ try {
118
+ renderedFiles = await this.renderTemplate(templateName);
119
+ } catch (error) {
120
+ logVerbose(`Failed to render template ${templateName}: ${error.message}`);
121
+ return changes;
122
+ }
123
+
93
124
  for (const file of files) {
94
- const templateFilePath = plugins.path.join(templateDir, file.templatePath);
95
- const destFilePath = file.destPath;
125
+ // Look up by templatePath first, then destPath (frontmatter may rename files)
126
+ const processedContent = renderedFiles.get(file.templatePath)
127
+ || renderedFiles.get(file.destPath);
96
128
 
97
- // Check if template file exists
98
- const fileExists = await plugins.smartfs.file(templateFilePath).exists();
99
- if (!fileExists) {
100
- logVerbose(`Template file ${templateFilePath} not found`);
129
+ if (!processedContent) {
130
+ logVerbose(`Template file ${file.templatePath} not found in rendered output`);
101
131
  continue;
102
132
  }
103
133
 
104
- try {
105
- // Read template content
106
- const templateContent = (await plugins.smartfs
107
- .file(templateFilePath)
134
+ const destExists = await plugins.smartfs.file(file.destPath).exists();
135
+ let currentContent = '';
136
+ if (destExists) {
137
+ currentContent = (await plugins.smartfs
138
+ .file(file.destPath)
108
139
  .encoding('utf8')
109
140
  .read()) as string;
141
+ }
110
142
 
111
- // Check if destination file exists
112
- const destExists = await plugins.smartfs.file(destFilePath).exists();
113
- let currentContent = '';
114
- if (destExists) {
115
- currentContent = (await plugins.smartfs
116
- .file(destFilePath)
117
- .encoding('utf8')
118
- .read()) as string;
119
- }
120
-
121
- // Only add change if content differs
122
- if (templateContent !== currentContent) {
123
- changes.push({
124
- type: destExists ? 'modify' : 'create',
125
- path: destFilePath,
126
- module: this.name,
127
- description: `Apply template ${templateName}/${file.templatePath}`,
128
- content: templateContent,
129
- });
130
- }
131
- } catch (error) {
132
- logVerbose(`Failed to read template ${templateFilePath}: ${error.message}`);
143
+ if (processedContent !== currentContent) {
144
+ changes.push({
145
+ type: destExists ? 'modify' : 'create',
146
+ path: file.destPath,
147
+ module: this.name,
148
+ description: `Apply template ${templateName}/${file.templatePath}`,
149
+ content: processedContent,
150
+ });
133
151
  }
134
152
  }
135
153
 
@@ -139,12 +157,6 @@ export class TemplatesFormatter extends BaseFormatter {
139
157
  async applyChange(change: IPlannedChange): Promise<void> {
140
158
  if (!change.content) return;
141
159
 
142
- // Ensure destination directory exists
143
- const destDir = plugins.path.dirname(change.path);
144
- if (destDir && destDir !== '.') {
145
- await plugins.smartfs.directory(destDir).recursive().create();
146
- }
147
-
148
160
  if (change.type === 'create') {
149
161
  await this.createFile(change.path, change.content);
150
162
  } else {
@@ -30,9 +30,10 @@ export class TsconfigFormatter extends BaseFormatter {
30
30
  const tsconfigObject = JSON.parse(currentContent);
31
31
  tsconfigObject.compilerOptions = tsconfigObject.compilerOptions || {};
32
32
  tsconfigObject.compilerOptions.baseUrl = '.';
33
- tsconfigObject.compilerOptions.paths = {};
33
+ const existingPaths = tsconfigObject.compilerOptions.paths || {};
34
34
 
35
- // Get module paths from tspublish
35
+ // Get module paths from tspublish, merging with existing custom paths
36
+ const tspublishPaths: Record<string, string[]> = {};
36
37
  try {
37
38
  const tsPublishMod = await import('@git.zone/tspublish');
38
39
  const tsPublishInstance = new tsPublishMod.TsPublish();
@@ -40,7 +41,7 @@ export class TsconfigFormatter extends BaseFormatter {
40
41
 
41
42
  for (const publishModule of Object.keys(publishModules)) {
42
43
  const publishConfig = publishModules[publishModule];
43
- tsconfigObject.compilerOptions.paths[`${publishConfig.name}`] = [
44
+ tspublishPaths[`${publishConfig.name}`] = [
44
45
  `./${publishModule}/index.js`,
45
46
  ];
46
47
  }
@@ -48,6 +49,8 @@ export class TsconfigFormatter extends BaseFormatter {
48
49
  logVerbose(`Could not get tspublish modules: ${error.message}`);
49
50
  }
50
51
 
52
+ tsconfigObject.compilerOptions.paths = { ...existingPaths, ...tspublishPaths };
53
+
51
54
  const newContent = JSON.stringify(tsconfigObject, null, 2);
52
55
 
53
56
  // Only add change if content differs
@@ -5,9 +5,8 @@ import { FormatPlanner } from './classes.formatplanner.js';
5
5
  import { BaseFormatter } from './classes.baseformatter.js';
6
6
  import { logger, setVerboseMode } from '../gitzone.logging.js';
7
7
 
8
- // Import wrapper classes for formatters
9
8
  import { CleanupFormatter } from './formatters/cleanup.formatter.js';
10
- import { NpmextraFormatter } from './formatters/npmextra.formatter.js';
9
+ import { SmartconfigFormatter } from './formatters/smartconfig.formatter.js';
11
10
  import { LicenseFormatter } from './formatters/license.formatter.js';
12
11
  import { PackageJsonFormatter } from './formatters/packagejson.formatter.js';
13
12
  import { TemplatesFormatter } from './formatters/templates.formatter.js';
@@ -17,82 +16,66 @@ import { PrettierFormatter } from './formatters/prettier.formatter.js';
17
16
  import { ReadmeFormatter } from './formatters/readme.formatter.js';
18
17
  import { CopyFormatter } from './formatters/copy.formatter.js';
19
18
 
19
+ // Shared formatter class map used by both run() and runFormatter()
20
+ const formatterMap: Record<string, new (ctx: FormatContext, proj: Project) => BaseFormatter> = {
21
+ cleanup: CleanupFormatter,
22
+ smartconfig: SmartconfigFormatter,
23
+ license: LicenseFormatter,
24
+ packagejson: PackageJsonFormatter,
25
+ templates: TemplatesFormatter,
26
+ gitignore: GitignoreFormatter,
27
+ tsconfig: TsconfigFormatter,
28
+ prettier: PrettierFormatter,
29
+ readme: ReadmeFormatter,
30
+ copy: CopyFormatter,
31
+ };
32
+
33
+ // Formatters that don't require projectType to be set
34
+ const formattersNotRequiringProjectType = ['smartconfig', 'prettier', 'cleanup', 'packagejson'];
35
+
20
36
  export let run = async (
21
37
  options: {
22
- write?: boolean; // Explicitly write changes (default: false, dry-mode)
23
- dryRun?: boolean; // Deprecated, kept for compatibility
38
+ write?: boolean;
39
+ dryRun?: boolean; // Deprecated, kept for compatibility
24
40
  yes?: boolean;
25
41
  planOnly?: boolean;
26
42
  savePlan?: string;
27
43
  fromPlan?: string;
28
44
  detailed?: boolean;
29
45
  interactive?: boolean;
30
- parallel?: boolean;
31
46
  verbose?: boolean;
32
- diff?: boolean; // Show file diffs
47
+ diff?: boolean;
33
48
  } = {},
34
49
  ): Promise<any> => {
35
- // Set verbose mode if requested
36
50
  if (options.verbose) {
37
51
  setVerboseMode(true);
38
52
  }
39
53
 
40
- // Determine if we should write changes
41
- // Default is dry-mode (no writing) unless --write/-w is specified
42
54
  const shouldWrite = options.write ?? (options.dryRun === false);
43
55
 
44
56
  const project = await Project.fromCwd({ requireProjectType: false });
45
57
  const context = new FormatContext();
46
- // Cache system removed - no longer needed
47
58
  const planner = new FormatPlanner();
48
59
 
49
- // Get configuration from npmextra
50
- const npmextraConfig = new plugins.npmextra.Smartconfig();
51
- const formatConfig = npmextraConfig.dataFor<any>('@git.zone/cli.format', {
60
+ const smartconfigInstance = new plugins.smartconfig.Smartconfig();
61
+ const formatConfig = smartconfigInstance.dataFor<any>('@git.zone/cli.format', {
52
62
  interactive: true,
53
63
  showDiffs: false,
54
64
  autoApprove: false,
55
- planTimeout: 30000,
56
- rollback: {
57
- enabled: true,
58
- autoRollbackOnError: true,
59
- backupRetentionDays: 7,
60
- maxBackupSize: '100MB',
61
- excludePatterns: ['node_modules/**', '.git/**'],
62
- },
63
65
  modules: {
64
66
  skip: [],
65
67
  only: [],
66
- order: [],
67
- },
68
- parallel: true,
69
- cache: {
70
- enabled: true,
71
- clean: true, // Clean invalid entries from cache
72
68
  },
73
69
  });
74
70
 
75
- // Cache cleaning removed - no longer using cache system
76
-
77
- // Override config with command options
78
71
  const interactive = options.interactive ?? formatConfig.interactive;
79
72
  const autoApprove = options.yes ?? formatConfig.autoApprove;
80
- const parallel = options.parallel ?? formatConfig.parallel;
81
73
 
82
74
  try {
83
- // Initialize formatters
84
- const formatters = [
85
- new CleanupFormatter(context, project),
86
- new NpmextraFormatter(context, project),
87
- new LicenseFormatter(context, project),
88
- new PackageJsonFormatter(context, project),
89
- new TemplatesFormatter(context, project),
90
- new GitignoreFormatter(context, project),
91
- new TsconfigFormatter(context, project),
92
- new PrettierFormatter(context, project),
93
- new ReadmeFormatter(context, project),
94
- new CopyFormatter(context, project),
95
- ];
75
+ // Initialize formatters in execution order
76
+ const formatters = Object.entries(formatterMap).map(
77
+ ([, FormatterClass]) => new FormatterClass(context, project),
78
+ );
96
79
 
97
80
  // Filter formatters based on configuration
98
81
  const activeFormatters = formatters.filter((formatter) => {
@@ -128,13 +111,13 @@ export let run = async (
128
111
  logger.log('info', `Plan saved to ${options.savePlan}`);
129
112
  }
130
113
 
131
- // Exit if plan-only mode
132
114
  if (options.planOnly) {
133
115
  return;
134
116
  }
135
117
 
136
- // Show diffs if requested (works in both dry-run and write modes)
137
- if (options.diff) {
118
+ // Show diffs if explicitly requested or before interactive write confirmation
119
+ const showDiffs = options.diff || (shouldWrite && interactive && !autoApprove);
120
+ if (showDiffs) {
138
121
  logger.log('info', 'Showing file diffs:');
139
122
  console.log('');
140
123
 
@@ -171,22 +154,16 @@ export let run = async (
171
154
  }
172
155
 
173
156
  // Execute phase
174
- logger.log(
175
- 'info',
176
- `Executing format operations${parallel ? ' in parallel' : ' sequentially'}...`,
177
- );
178
- await planner.executePlan(plan, activeFormatters, context, parallel);
157
+ logger.log('info', 'Executing format operations...');
158
+ await planner.executePlan(plan, activeFormatters, context);
179
159
 
180
- // Finish statistics tracking
181
160
  context.getFormatStats().finish();
182
161
 
183
- // Display statistics
184
- const showStats = npmextraConfig.dataFor('gitzone.format.showStats', true);
162
+ const showStats = smartconfigInstance.dataFor('gitzone.format.showStats', true);
185
163
  if (showStats) {
186
164
  context.getFormatStats().displayStats();
187
165
  }
188
166
 
189
- // Save stats if requested
190
167
  if (options.detailed) {
191
168
  const statsPath = `.nogit/format-stats-${Date.now()}.json`;
192
169
  await context.getFormatStats().saveReport(statsPath);
@@ -195,36 +172,13 @@ export let run = async (
195
172
  logger.log('success', 'Format operations completed successfully!');
196
173
  } catch (error) {
197
174
  logger.log('error', `Format operation failed: ${error.message}`);
198
-
199
- // Rollback system has been removed for stability
200
-
201
175
  throw error;
202
176
  }
203
177
  };
204
178
 
205
- // Export CLI command handlers
206
- export const handleRollback = async (operationId?: string): Promise<void> => {
207
- logger.log('info', 'Rollback system has been disabled for stability');
208
- };
209
-
210
- export const handleListBackups = async (): Promise<void> => {
211
- logger.log('info', 'Backup system has been disabled for stability');
212
- };
213
-
214
- export const handleCleanBackups = async (): Promise<void> => {
215
- logger.log(
216
- 'info',
217
- 'Backup cleaning has been disabled - backup system removed',
218
- );
219
- };
220
-
221
- // Import the ICheckResult type for external use
222
179
  import type { ICheckResult } from './interfaces.format.js';
223
180
  export type { ICheckResult };
224
181
 
225
- // Formatters that don't require projectType to be set
226
- const formattersNotRequiringProjectType = ['npmextra', 'prettier', 'cleanup', 'packagejson'];
227
-
228
182
  /**
229
183
  * Run a single formatter by name (for use by other modules)
230
184
  */
@@ -232,29 +186,14 @@ export const runFormatter = async (
232
186
  formatterName: string,
233
187
  options: {
234
188
  silent?: boolean;
235
- checkOnly?: boolean; // Only check for diffs, don't apply
236
- showDiff?: boolean; // Show the diff output
189
+ checkOnly?: boolean;
190
+ showDiff?: boolean;
237
191
  } = {}
238
192
  ): Promise<ICheckResult | void> => {
239
- // Determine if this formatter requires projectType
240
193
  const requireProjectType = !formattersNotRequiringProjectType.includes(formatterName);
241
194
  const project = await Project.fromCwd({ requireProjectType });
242
195
  const context = new FormatContext();
243
196
 
244
- // Map formatter names to classes
245
- const formatterMap: Record<string, new (ctx: FormatContext, proj: Project) => BaseFormatter> = {
246
- cleanup: CleanupFormatter,
247
- npmextra: NpmextraFormatter,
248
- license: LicenseFormatter,
249
- packagejson: PackageJsonFormatter,
250
- templates: TemplatesFormatter,
251
- gitignore: GitignoreFormatter,
252
- tsconfig: TsconfigFormatter,
253
- prettier: PrettierFormatter,
254
- readme: ReadmeFormatter,
255
- copy: CopyFormatter,
256
- };
257
-
258
197
  const FormatterClass = formatterMap[formatterName];
259
198
  if (!FormatterClass) {
260
199
  throw new Error(`Unknown formatter: ${formatterName}`);
@@ -262,7 +201,6 @@ export const runFormatter = async (
262
201
 
263
202
  const formatter = new FormatterClass(context, project);
264
203
 
265
- // Check-only mode: just check for diffs and optionally display them
266
204
  if (options.checkOnly) {
267
205
  const result = await formatter.check();
268
206
  if (result.hasDiff && options.showDiff) {
@@ -271,7 +209,6 @@ export const runFormatter = async (
271
209
  return result;
272
210
  }
273
211
 
274
- // Normal mode: analyze and apply changes
275
212
  const changes = await formatter.analyze();
276
213
 
277
214
  for (const change of changes) {