@git.zone/cli 1.16.7 → 1.16.9

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 (72) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/mod_format/classes.baseformatter.d.ts +0 -2
  3. package/dist_ts/mod_format/classes.baseformatter.js +14 -23
  4. package/dist_ts/mod_format/classes.changecache.js +14 -29
  5. package/dist_ts/mod_format/classes.dependency-analyzer.js +24 -18
  6. package/dist_ts/mod_format/classes.diffreporter.js +4 -4
  7. package/dist_ts/mod_format/classes.formatcontext.d.ts +0 -13
  8. package/dist_ts/mod_format/classes.formatcontext.js +1 -41
  9. package/dist_ts/mod_format/classes.formatplanner.js +25 -45
  10. package/dist_ts/mod_format/classes.formatstats.js +22 -20
  11. package/dist_ts/mod_format/classes.rollbackmanager.js +20 -32
  12. package/dist_ts/mod_format/format.cleanup.js +7 -2
  13. package/dist_ts/mod_format/format.copy.d.ts +1 -1
  14. package/dist_ts/mod_format/format.copy.js +3 -3
  15. package/dist_ts/mod_format/format.gitignore.js +27 -7
  16. package/dist_ts/mod_format/format.license.js +1 -1
  17. package/dist_ts/mod_format/format.npmextra.js +1 -1
  18. package/dist_ts/mod_format/format.packagejson.js +5 -3
  19. package/dist_ts/mod_format/format.prettier.js +7 -2
  20. package/dist_ts/mod_format/format.readme.js +1 -1
  21. package/dist_ts/mod_format/format.templates.js +1 -1
  22. package/dist_ts/mod_format/format.tsconfig.js +4 -2
  23. package/dist_ts/mod_format/formatters/cleanup.formatter.js +8 -3
  24. package/dist_ts/mod_format/formatters/legacy.formatter.js +6 -4
  25. package/dist_ts/mod_format/formatters/prettier.formatter.js +68 -51
  26. package/dist_ts/mod_format/formatters/readme.formatter.js +6 -4
  27. package/dist_ts/mod_format/index.js +13 -71
  28. package/dist_ts/mod_meta/meta.classes.meta.js +1 -1
  29. package/dist_ts/mod_standard/index.js +1 -1
  30. package/dist_ts/mod_start/index.js +1 -1
  31. package/dist_ts/mod_template/index.js +1 -1
  32. package/npmextra.json +1 -1
  33. package/package.json +1 -1
  34. package/readme.hints.md +8 -3
  35. package/readme.md +30 -3
  36. package/readme.plan.md +21 -1
  37. package/ts/00_commitinfo_data.ts +1 -1
  38. package/ts/mod_format/classes.baseformatter.ts +28 -39
  39. package/ts/mod_format/classes.changecache.ts +74 -80
  40. package/ts/mod_format/classes.dependency-analyzer.ts +46 -36
  41. package/ts/mod_format/classes.diffreporter.ts +44 -28
  42. package/ts/mod_format/classes.formatcontext.ts +3 -54
  43. package/ts/mod_format/classes.formatplanner.ts +56 -70
  44. package/ts/mod_format/classes.formatstats.ts +69 -49
  45. package/ts/mod_format/classes.rollbackmanager.ts +123 -103
  46. package/ts/mod_format/format.cleanup.ts +9 -2
  47. package/ts/mod_format/format.copy.ts +17 -14
  48. package/ts/mod_format/format.gitignore.ts +33 -7
  49. package/ts/mod_format/format.license.ts +3 -1
  50. package/ts/mod_format/format.npmextra.ts +9 -2
  51. package/ts/mod_format/format.packagejson.ts +47 -12
  52. package/ts/mod_format/format.prettier.ts +10 -2
  53. package/ts/mod_format/format.readme.ts +2 -1
  54. package/ts/mod_format/format.templates.ts +15 -7
  55. package/ts/mod_format/format.tsconfig.ts +6 -2
  56. package/ts/mod_format/formatters/cleanup.formatter.ts +13 -8
  57. package/ts/mod_format/formatters/copy.formatter.ts +1 -1
  58. package/ts/mod_format/formatters/gitignore.formatter.ts +1 -1
  59. package/ts/mod_format/formatters/legacy.formatter.ts +19 -12
  60. package/ts/mod_format/formatters/license.formatter.ts +1 -1
  61. package/ts/mod_format/formatters/npmextra.formatter.ts +1 -1
  62. package/ts/mod_format/formatters/packagejson.formatter.ts +1 -1
  63. package/ts/mod_format/formatters/prettier.formatter.ts +114 -78
  64. package/ts/mod_format/formatters/readme.formatter.ts +11 -9
  65. package/ts/mod_format/formatters/templates.formatter.ts +1 -1
  66. package/ts/mod_format/formatters/tsconfig.formatter.ts +1 -1
  67. package/ts/mod_format/index.ts +57 -113
  68. package/ts/mod_format/interfaces.format.ts +3 -3
  69. package/ts/mod_meta/meta.classes.meta.ts +57 -19
  70. package/ts/mod_standard/index.ts +3 -1
  71. package/ts/mod_start/index.ts +3 -1
  72. package/ts/mod_template/index.ts +5 -2
@@ -19,7 +19,7 @@ const ensureDependency = async (
19
19
  : [dependencyArg, 'latest'];
20
20
 
21
21
  const targetSections: string[] = [];
22
-
22
+
23
23
  switch (position) {
24
24
  case 'dep':
25
25
  targetSections.push('dependencies');
@@ -43,7 +43,8 @@ const ensureDependency = async (
43
43
  break;
44
44
  case 'include':
45
45
  if (!packageJsonObjectArg[section][packageName]) {
46
- packageJsonObjectArg[section][packageName] = version === 'latest' ? '^1.0.0' : version;
46
+ packageJsonObjectArg[section][packageName] =
47
+ version === 'latest' ? '^1.0.0' : version;
47
48
  }
48
49
  break;
49
50
  case 'latest':
@@ -54,9 +55,13 @@ const ensureDependency = async (
54
55
  const latestVersion = packageInfo['dist-tags'].latest;
55
56
  packageJsonObjectArg[section][packageName] = `^${latestVersion}`;
56
57
  } catch (error) {
57
- logger.log('warn', `Could not fetch latest version for ${packageName}, using existing or default`);
58
+ logger.log(
59
+ 'warn',
60
+ `Could not fetch latest version for ${packageName}, using existing or default`,
61
+ );
58
62
  if (!packageJsonObjectArg[section][packageName]) {
59
- packageJsonObjectArg[section][packageName] = version === 'latest' ? '^1.0.0' : version;
63
+ packageJsonObjectArg[section][packageName] =
64
+ version === 'latest' ? '^1.0.0' : version;
60
65
  }
61
66
  }
62
67
  break;
@@ -91,9 +96,15 @@ export const run = async (projectArg: Project) => {
91
96
 
92
97
  // Check for private or public
93
98
  if (packageJson.private !== undefined) {
94
- logger.log('info', 'Success -> found private/public info in package.json!');
99
+ logger.log(
100
+ 'info',
101
+ 'Success -> found private/public info in package.json!',
102
+ );
95
103
  } else {
96
- logger.log('error', 'found no private boolean! Setting it to private for now!');
104
+ logger.log(
105
+ 'error',
106
+ 'found no private boolean! Setting it to private for now!',
107
+ );
97
108
  packageJson.private = true;
98
109
  }
99
110
 
@@ -101,7 +112,10 @@ export const run = async (projectArg: Project) => {
101
112
  if (packageJson.license) {
102
113
  logger.log('info', 'Success -> found license in package.json!');
103
114
  } else {
104
- logger.log('error', 'found no license! Setting it to UNLICENSED for now!');
115
+ logger.log(
116
+ 'error',
117
+ 'found no license! Setting it to UNLICENSED for now!',
118
+ );
105
119
  packageJson.license = 'UNLICENSED';
106
120
  }
107
121
 
@@ -109,13 +123,19 @@ export const run = async (projectArg: Project) => {
109
123
  if (packageJson.scripts.build) {
110
124
  logger.log('info', 'Success -> found build script in package.json!');
111
125
  } else {
112
- logger.log('error', 'found no build script! Putting a placeholder there for now!');
126
+ logger.log(
127
+ 'error',
128
+ 'found no build script! Putting a placeholder there for now!',
129
+ );
113
130
  packageJson.scripts.build = `echo "Not needed for now"`;
114
131
  }
115
132
 
116
133
  // Check for buildDocs script
117
134
  if (!packageJson.scripts.buildDocs) {
118
- logger.log('info', 'found no buildDocs script! Putting tsdoc script there now.');
135
+ logger.log(
136
+ 'info',
137
+ 'found no buildDocs script! Putting tsdoc script there now.',
138
+ );
119
139
  packageJson.scripts.buildDocs = `tsdoc`;
120
140
  }
121
141
 
@@ -134,9 +154,24 @@ export const run = async (projectArg: Project) => {
134
154
  ];
135
155
 
136
156
  // check for dependencies
137
- await ensureDependency(packageJson, 'devDep', 'latest', '@push.rocks/tapbundle');
138
- await ensureDependency(packageJson, 'devDep', 'latest', '@git.zone/tstest');
139
- await ensureDependency(packageJson, 'devDep', 'latest', '@git.zone/tsbuild');
157
+ await ensureDependency(
158
+ packageJson,
159
+ 'devDep',
160
+ 'latest',
161
+ '@push.rocks/tapbundle',
162
+ );
163
+ await ensureDependency(
164
+ packageJson,
165
+ 'devDep',
166
+ 'latest',
167
+ '@git.zone/tstest',
168
+ );
169
+ await ensureDependency(
170
+ packageJson,
171
+ 'devDep',
172
+ 'latest',
173
+ '@git.zone/tsbuild',
174
+ );
140
175
 
141
176
  // set overrides
142
177
  const overrides = plugins.smartfile.fs.toObjectSync(
@@ -16,7 +16,12 @@ const prettierDefaultMarkdownConfig: prettier.Options = {
16
16
  parser: 'markdown',
17
17
  };
18
18
 
19
- const filesToFormat = [`ts/**/*.ts`, `test/**/*.ts`, `readme.md`, `docs/**/*.md`];
19
+ const filesToFormat = [
20
+ `ts/**/*.ts`,
21
+ `test/**/*.ts`,
22
+ `readme.md`,
23
+ `docs/**/*.md`,
24
+ ];
20
25
 
21
26
  const choosePrettierConfig = (fileArg: plugins.smartfile.SmartFile) => {
22
27
  switch (fileArg.parsedPath.ext) {
@@ -39,7 +44,10 @@ const prettierTypeScriptPipestop = plugins.through2.obj(
39
44
  cb(null);
40
45
  } else {
41
46
  logger.log('info', `${fileArg.path} is being reformated!`);
42
- const formatedFileString = await prettier.format(fileString, chosenConfig);
47
+ const formatedFileString = await prettier.format(
48
+ fileString,
49
+ chosenConfig,
50
+ );
43
51
  fileArg.setContentsFromString(formatedFileString);
44
52
  cb(null, fileArg);
45
53
  }
@@ -18,7 +18,8 @@ export const run = async () => {
18
18
  }
19
19
 
20
20
  // Check and initialize readme.hints.md if it doesn't exist
21
- const readmeHintsExists = await plugins.smartfile.fs.fileExists(readmeHintsPath);
21
+ const readmeHintsExists =
22
+ await plugins.smartfile.fs.fileExists(readmeHintsPath);
22
23
  if (!readmeHintsExists) {
23
24
  await plugins.smartfile.fs.toFs(
24
25
  '# Project Readme Hints\n\nThis is the initial readme hints file.',
@@ -26,10 +26,12 @@ export const run = async (project: Project) => {
26
26
  case 'npm':
27
27
  case 'wcc':
28
28
  if (project.gitzoneConfig.data.npmciOptions.npmAccessLevel === 'public') {
29
- const ciTemplateDefault = await templateModule.getTemplate('ci_default');
29
+ const ciTemplateDefault =
30
+ await templateModule.getTemplate('ci_default');
30
31
  ciTemplateDefault.writeToDisk(paths.cwd);
31
32
  } else {
32
- const ciTemplateDefault = await templateModule.getTemplate('ci_default_private');
33
+ const ciTemplateDefault =
34
+ await templateModule.getTemplate('ci_default_private');
33
35
  ciTemplateDefault.writeToDisk(paths.cwd);
34
36
  }
35
37
  logger.log('info', 'Updated .gitlabci.yml!');
@@ -41,7 +43,8 @@ export const run = async (project: Project) => {
41
43
  logger.log('info', 'Updated CI/CD config files!');
42
44
 
43
45
  // lets care about docker
44
- const dockerTemplate = await templateModule.getTemplate('dockerfile_service');
46
+ const dockerTemplate =
47
+ await templateModule.getTemplate('dockerfile_service');
45
48
  dockerTemplate.writeToDisk(paths.cwd);
46
49
  logger.log('info', 'Updated Dockerfile!');
47
50
 
@@ -56,17 +59,22 @@ export const run = async (project: Project) => {
56
59
 
57
60
  // update html
58
61
  if (project.gitzoneConfig.data.projectType === 'website') {
59
- const websiteUpdateTemplate = await templateModule.getTemplate('website_update');
60
- const variables ={
62
+ const websiteUpdateTemplate =
63
+ await templateModule.getTemplate('website_update');
64
+ const variables = {
61
65
  assetbrokerUrl: project.gitzoneConfig.data.module.assetbrokerUrl,
62
66
  legalUrl: project.gitzoneConfig.data.module.legalUrl,
63
67
  };
64
- console.log('updating website template with variables\n', JSON.stringify(variables, null, 2));
68
+ console.log(
69
+ 'updating website template with variables\n',
70
+ JSON.stringify(variables, null, 2),
71
+ );
65
72
  websiteUpdateTemplate.supplyVariables(variables);
66
73
  await websiteUpdateTemplate.writeToDisk(paths.cwd);
67
74
  logger.log('info', `Updated html for website!`);
68
75
  } else if (project.gitzoneConfig.data.projectType === 'service') {
69
- const websiteUpdateTemplate = await templateModule.getTemplate('service_update');
76
+ const websiteUpdateTemplate =
77
+ await templateModule.getTemplate('service_update');
70
78
  await websiteUpdateTemplate.writeToDisk(paths.cwd);
71
79
  logger.log('info', `Updated html for element template!`);
72
80
  } else if (project.gitzoneConfig.data.projectType === 'wcc') {
@@ -19,8 +19,12 @@ export const run = async (projectArg: Project) => {
19
19
  const publishModules = await tsPublishInstance.getModuleSubDirs(paths.cwd);
20
20
  for (const publishModule of Object.keys(publishModules)) {
21
21
  const publishConfig = publishModules[publishModule];
22
- tsconfigObject.compilerOptions.paths[`${publishConfig.name}`] = [`./${publishModule}/index.js`];
22
+ tsconfigObject.compilerOptions.paths[`${publishConfig.name}`] = [
23
+ `./${publishModule}/index.js`,
24
+ ];
23
25
  }
24
- tsconfigSmartfile.setContentsFromString(JSON.stringify(tsconfigObject, null, 2));
26
+ tsconfigSmartfile.setContentsFromString(
27
+ JSON.stringify(tsconfigObject, null, 2),
28
+ );
25
29
  await tsconfigSmartfile.write();
26
30
  };
@@ -7,13 +7,18 @@ export class CleanupFormatter extends BaseFormatter {
7
7
  get name(): string {
8
8
  return 'cleanup';
9
9
  }
10
-
10
+
11
11
  async analyze(): Promise<IPlannedChange[]> {
12
12
  const changes: IPlannedChange[] = [];
13
-
13
+
14
14
  // List of files to remove
15
- const filesToRemove = ['yarn.lock', 'package-lock.json', 'tslint.json', 'defaults.yml'];
16
-
15
+ const filesToRemove = [
16
+ 'yarn.lock',
17
+ 'package-lock.json',
18
+ 'tslint.json',
19
+ 'defaults.yml',
20
+ ];
21
+
17
22
  for (const file of filesToRemove) {
18
23
  const exists = await plugins.smartfile.fs.fileExists(file);
19
24
  if (exists) {
@@ -21,14 +26,14 @@ export class CleanupFormatter extends BaseFormatter {
21
26
  type: 'delete',
22
27
  path: file,
23
28
  module: this.name,
24
- description: `Remove obsolete file`
29
+ description: `Remove obsolete file`,
25
30
  });
26
31
  }
27
32
  }
28
-
33
+
29
34
  return changes;
30
35
  }
31
-
36
+
32
37
  async applyChange(change: IPlannedChange): Promise<void> {
33
38
  switch (change.type) {
34
39
  case 'delete':
@@ -36,4 +41,4 @@ export class CleanupFormatter extends BaseFormatter {
36
41
  break;
37
42
  }
38
43
  }
39
- }
44
+ }
@@ -5,4 +5,4 @@ export class CopyFormatter extends LegacyFormatter {
5
5
  constructor(context: any, project: any) {
6
6
  super(context, project, 'copy', formatCopy);
7
7
  }
8
- }
8
+ }
@@ -5,4 +5,4 @@ export class GitignoreFormatter extends LegacyFormatter {
5
5
  constructor(context: any, project: any) {
6
6
  super(context, project, 'gitignore', formatGitignore);
7
7
  }
8
- }
8
+ }
@@ -7,30 +7,37 @@ import * as plugins from '../mod.plugins.js';
7
7
  export class LegacyFormatter extends BaseFormatter {
8
8
  private moduleName: string;
9
9
  private formatModule: any;
10
-
11
- constructor(context: any, project: Project, moduleName: string, formatModule: any) {
10
+
11
+ constructor(
12
+ context: any,
13
+ project: Project,
14
+ moduleName: string,
15
+ formatModule: any,
16
+ ) {
12
17
  super(context, project);
13
18
  this.moduleName = moduleName;
14
19
  this.formatModule = formatModule;
15
20
  }
16
-
21
+
17
22
  get name(): string {
18
23
  return this.moduleName;
19
24
  }
20
-
25
+
21
26
  async analyze(): Promise<IPlannedChange[]> {
22
27
  // For legacy modules, we can't easily predict changes
23
28
  // So we'll return a generic change that indicates the module will run
24
- return [{
25
- type: 'modify',
26
- path: '<various files>',
27
- module: this.name,
28
- description: `Run ${this.name} formatter`
29
- }];
29
+ return [
30
+ {
31
+ type: 'modify',
32
+ path: '<various files>',
33
+ module: this.name,
34
+ description: `Run ${this.name} formatter`,
35
+ },
36
+ ];
30
37
  }
31
-
38
+
32
39
  async applyChange(change: IPlannedChange): Promise<void> {
33
40
  // Run the legacy format module
34
41
  await this.formatModule.run(this.project);
35
42
  }
36
- }
43
+ }
@@ -5,4 +5,4 @@ export class LicenseFormatter extends LegacyFormatter {
5
5
  constructor(context: any, project: any) {
6
6
  super(context, project, 'license', formatLicense);
7
7
  }
8
- }
8
+ }
@@ -5,4 +5,4 @@ export class NpmextraFormatter extends LegacyFormatter {
5
5
  constructor(context: any, project: any) {
6
6
  super(context, project, 'npmextra', formatNpmextra);
7
7
  }
8
- }
8
+ }
@@ -5,4 +5,4 @@ export class PackageJsonFormatter extends LegacyFormatter {
5
5
  constructor(context: any, project: any) {
6
6
  super(context, project, 'packagejson', formatPackageJson);
7
7
  }
8
- }
8
+ }
@@ -7,21 +7,16 @@ export class PrettierFormatter extends BaseFormatter {
7
7
  get name(): string {
8
8
  return 'prettier';
9
9
  }
10
-
10
+
11
11
  async analyze(): Promise<IPlannedChange[]> {
12
12
  const changes: IPlannedChange[] = [];
13
-
13
+
14
14
  // Define directories to format (TypeScript directories by default)
15
- const includeDirs = [
16
- 'ts',
17
- 'ts_*',
18
- 'test',
19
- 'tests'
20
- ];
21
-
15
+ const includeDirs = ['ts', 'ts_*', 'test', 'tests'];
16
+
22
17
  // File extensions to format
23
18
  const extensions = '{ts,tsx,js,jsx,json,md,css,scss,html,xml,yaml,yml}';
24
-
19
+
25
20
  // Also format root-level config files
26
21
  const rootConfigFiles = [
27
22
  'package.json',
@@ -34,35 +29,39 @@ export class PrettierFormatter extends BaseFormatter {
34
29
  'README.md',
35
30
  'changelog.md',
36
31
  'CHANGELOG.md',
37
- 'license',
38
- 'LICENSE',
39
- '*.md'
32
+ // Skip files without extensions as prettier can't infer parser
33
+ // 'license',
34
+ // 'LICENSE',
35
+ '*.md',
40
36
  ];
41
-
37
+
42
38
  // Collect all files to format
43
39
  const allFiles: string[] = [];
44
-
40
+
45
41
  // Add files from TypeScript directories
46
42
  for (const dir of includeDirs) {
47
43
  const globPattern = `${dir}/**/*.${extensions}`;
48
- const dirFiles = await plugins.smartfile.fs.listFileTree('.', globPattern);
44
+ const dirFiles = await plugins.smartfile.fs.listFileTree(
45
+ '.',
46
+ globPattern,
47
+ );
49
48
  allFiles.push(...dirFiles);
50
49
  }
51
-
50
+
52
51
  // Add root config files
53
52
  for (const pattern of rootConfigFiles) {
54
53
  const rootFiles = await plugins.smartfile.fs.listFileTree('.', pattern);
55
54
  // Only include files at root level (no slashes in path)
56
- const rootLevelFiles = rootFiles.filter(f => !f.includes('/'));
55
+ const rootLevelFiles = rootFiles.filter((f) => !f.includes('/'));
57
56
  allFiles.push(...rootLevelFiles);
58
57
  }
59
-
58
+
60
59
  // Remove duplicates
61
60
  const uniqueFiles = [...new Set(allFiles)];
62
-
61
+
63
62
  // Get all files that match the pattern
64
63
  const files = uniqueFiles;
65
-
64
+
66
65
  // Ensure we only process actual files (not directories)
67
66
  const validFiles: string[] = [];
68
67
  for (const file of files) {
@@ -76,101 +75,138 @@ export class PrettierFormatter extends BaseFormatter {
76
75
  logVerbose(`Skipping ${file} - cannot access: ${error.message}`);
77
76
  }
78
77
  }
79
-
78
+
80
79
  // Check which files need formatting
81
80
  for (const file of validFiles) {
82
81
  // Skip files that haven't changed
83
- if (!await this.shouldProcessFile(file)) {
82
+ if (!(await this.shouldProcessFile(file))) {
84
83
  logVerbose(`Skipping ${file} - no changes detected`);
85
84
  continue;
86
85
  }
87
-
86
+
88
87
  changes.push({
89
88
  type: 'modify',
90
89
  path: file,
91
90
  module: this.name,
92
- description: 'Format with Prettier'
91
+ description: 'Format with Prettier',
93
92
  });
94
93
  }
95
-
94
+
96
95
  logger.log('info', `Found ${changes.length} files to format with Prettier`);
97
96
  return changes;
98
97
  }
99
-
98
+
100
99
  async execute(changes: IPlannedChange[]): Promise<void> {
101
100
  const startTime = this.stats.moduleStartTime(this.name);
102
101
  this.stats.startModule(this.name);
103
-
102
+
104
103
  try {
105
104
  await this.preExecute();
106
-
107
- // Batch process files
108
- const batchSize = 10; // Process 10 files at a time
109
- const batches: IPlannedChange[][] = [];
110
-
111
- for (let i = 0; i < changes.length; i += batchSize) {
112
- batches.push(changes.slice(i, i + batchSize));
113
- }
114
-
115
- logVerbose(`Processing ${changes.length} files in ${batches.length} batches`);
116
-
117
- for (let i = 0; i < batches.length; i++) {
118
- const batch = batches[i];
119
- logVerbose(`Processing batch ${i + 1}/${batches.length} (${batch.length} files)`);
120
-
121
- // Process batch in parallel
122
- const promises = batch.map(async (change) => {
123
- try {
124
- await this.applyChange(change);
125
- this.stats.recordFileOperation(this.name, change.type, true);
126
- } catch (error) {
127
- this.stats.recordFileOperation(this.name, change.type, false);
128
- logger.log('error', `Failed to format ${change.path}: ${error.message}`);
129
- // Don't throw - continue with other files
130
- }
131
- });
132
-
133
- await Promise.all(promises);
105
+
106
+ logVerbose(`Processing ${changes.length} files sequentially`);
107
+
108
+ // Process files sequentially to avoid prettier cache/state issues
109
+ for (let i = 0; i < changes.length; i++) {
110
+ const change = changes[i];
111
+ logVerbose(
112
+ `Processing file ${i + 1}/${changes.length}: ${change.path}`,
113
+ );
114
+
115
+ try {
116
+ await this.applyChange(change);
117
+ this.stats.recordFileOperation(this.name, change.type, true);
118
+ } catch (error) {
119
+ this.stats.recordFileOperation(this.name, change.type, false);
120
+ logger.log(
121
+ 'error',
122
+ `Failed to format ${change.path}: ${error.message}`,
123
+ );
124
+ // Don't throw - continue with other files
125
+ }
134
126
  }
135
-
127
+
136
128
  await this.postExecute();
137
129
  } catch (error) {
138
- await this.context.rollbackOperation();
130
+ // Rollback removed - no longer tracking operations
139
131
  throw error;
140
132
  } finally {
141
133
  this.stats.endModule(this.name, startTime);
142
134
  }
143
135
  }
144
-
136
+
145
137
  async applyChange(change: IPlannedChange): Promise<void> {
146
138
  if (change.type !== 'modify') return;
147
-
139
+
148
140
  try {
141
+ // Validate the path before processing
142
+ if (!change.path || change.path.trim() === '') {
143
+ logger.log(
144
+ 'error',
145
+ `Invalid empty path in change: ${JSON.stringify(change)}`,
146
+ );
147
+ throw new Error('Invalid empty path');
148
+ }
149
+
149
150
  // Read current content
150
151
  const content = plugins.smartfile.fs.toStringSync(change.path);
151
-
152
+
152
153
  // Format with prettier
153
154
  const prettier = await import('prettier');
154
- const formatted = await prettier.format(content, {
155
- filepath: change.path,
156
- ...(await this.getPrettierConfig())
157
- });
158
-
159
- // Only write if content actually changed
160
- if (formatted !== content) {
161
- await this.modifyFile(change.path, formatted);
162
- logVerbose(`Formatted ${change.path}`);
163
- } else {
164
- // Still update cache even if content didn't change
165
- await this.cache.updateFileCache(change.path);
166
- logVerbose(`No formatting changes for ${change.path}`);
155
+
156
+ // Skip files that prettier can't parse without explicit parser
157
+ const fileExt = plugins.path.extname(change.path).toLowerCase();
158
+ if (!fileExt || fileExt === '') {
159
+ // Files without extensions need explicit parser
160
+ logVerbose(
161
+ `Skipping ${change.path} - no file extension for parser inference`,
162
+ );
163
+ return;
164
+ }
165
+
166
+ try {
167
+ const formatted = await prettier.format(content, {
168
+ filepath: change.path,
169
+ ...(await this.getPrettierConfig()),
170
+ });
171
+
172
+ // Only write if content actually changed
173
+ if (formatted !== content) {
174
+ // Debug: log the path being written
175
+ logVerbose(`Writing formatted content to: ${change.path}`);
176
+ await this.modifyFile(change.path, formatted);
177
+ logVerbose(`Formatted ${change.path}`);
178
+ } else {
179
+ logVerbose(`No formatting changes for ${change.path}`);
180
+ }
181
+ } catch (prettierError) {
182
+ // Check if it's a parser error
183
+ if (
184
+ prettierError.message &&
185
+ prettierError.message.includes('No parser could be inferred')
186
+ ) {
187
+ logVerbose(`Skipping ${change.path} - ${prettierError.message}`);
188
+ return; // Skip this file silently
189
+ }
190
+ throw prettierError;
167
191
  }
168
192
  } catch (error) {
169
- logger.log('error', `Failed to format ${change.path}: ${error.message}`);
193
+ // Log the full error stack for debugging mkdir issues
194
+ if (error.message && error.message.includes('mkdir')) {
195
+ logger.log(
196
+ 'error',
197
+ `Failed to format ${change.path}: ${error.message}`,
198
+ );
199
+ logger.log('error', `Error stack: ${error.stack}`);
200
+ } else {
201
+ logger.log(
202
+ 'error',
203
+ `Failed to format ${change.path}: ${error.message}`,
204
+ );
205
+ }
170
206
  throw error;
171
207
  }
172
208
  }
173
-
209
+
174
210
  private async getPrettierConfig(): Promise<any> {
175
211
  // Try to load prettier config from the project
176
212
  const prettierConfig = new plugins.npmextra.Npmextra();
@@ -181,7 +217,7 @@ export class PrettierFormatter extends BaseFormatter {
181
217
  printWidth: 80,
182
218
  tabWidth: 2,
183
219
  semi: true,
184
- arrowParens: 'always'
220
+ arrowParens: 'always',
185
221
  });
186
222
  }
187
- }
223
+ }
@@ -6,17 +6,19 @@ export class ReadmeFormatter extends BaseFormatter {
6
6
  get name(): string {
7
7
  return 'readme';
8
8
  }
9
-
9
+
10
10
  async analyze(): Promise<IPlannedChange[]> {
11
- return [{
12
- type: 'modify',
13
- path: 'readme.md',
14
- module: this.name,
15
- description: 'Ensure readme files exist'
16
- }];
11
+ return [
12
+ {
13
+ type: 'modify',
14
+ path: 'readme.md',
15
+ module: this.name,
16
+ description: 'Ensure readme files exist',
17
+ },
18
+ ];
17
19
  }
18
-
20
+
19
21
  async applyChange(change: IPlannedChange): Promise<void> {
20
22
  await formatReadme.run();
21
23
  }
22
- }
24
+ }
@@ -5,4 +5,4 @@ export class TemplatesFormatter extends LegacyFormatter {
5
5
  constructor(context: any, project: any) {
6
6
  super(context, project, 'templates', formatTemplates);
7
7
  }
8
- }
8
+ }