@git.zone/cli 1.15.4 → 1.16.0

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 (81) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/gitzone.cli.js +26 -2
  3. package/dist_ts/gitzone.logging.d.ts +3 -0
  4. package/dist_ts/gitzone.logging.js +20 -2
  5. package/dist_ts/mod_format/classes.baseformatter.d.ts +21 -0
  6. package/dist_ts/mod_format/classes.baseformatter.js +73 -0
  7. package/dist_ts/mod_format/classes.changecache.d.ts +25 -0
  8. package/dist_ts/mod_format/classes.changecache.js +105 -0
  9. package/dist_ts/mod_format/classes.dependency-analyzer.d.ts +13 -0
  10. package/dist_ts/mod_format/classes.dependency-analyzer.js +87 -0
  11. package/dist_ts/mod_format/classes.diffreporter.d.ts +13 -0
  12. package/dist_ts/mod_format/classes.diffreporter.js +94 -0
  13. package/dist_ts/mod_format/classes.formatcontext.d.ts +19 -0
  14. package/dist_ts/mod_format/classes.formatcontext.js +51 -0
  15. package/dist_ts/mod_format/classes.formatplanner.d.ts +13 -0
  16. package/dist_ts/mod_format/classes.formatplanner.js +164 -0
  17. package/dist_ts/mod_format/classes.formatstats.d.ts +40 -0
  18. package/dist_ts/mod_format/classes.formatstats.js +161 -0
  19. package/dist_ts/mod_format/classes.rollbackmanager.d.ts +21 -0
  20. package/dist_ts/mod_format/classes.rollbackmanager.js +170 -0
  21. package/dist_ts/mod_format/format.copy.d.ts +22 -0
  22. package/dist_ts/mod_format/format.copy.js +66 -2
  23. package/dist_ts/mod_format/format.packagejson.js +48 -2
  24. package/dist_ts/mod_format/formatters/cleanup.formatter.d.ts +7 -0
  25. package/dist_ts/mod_format/formatters/cleanup.formatter.js +33 -0
  26. package/dist_ts/mod_format/formatters/copy.formatter.d.ts +4 -0
  27. package/dist_ts/mod_format/formatters/copy.formatter.js +8 -0
  28. package/dist_ts/mod_format/formatters/gitignore.formatter.d.ts +4 -0
  29. package/dist_ts/mod_format/formatters/gitignore.formatter.js +8 -0
  30. package/dist_ts/mod_format/formatters/legacy.formatter.d.ts +11 -0
  31. package/dist_ts/mod_format/formatters/legacy.formatter.js +29 -0
  32. package/dist_ts/mod_format/formatters/license.formatter.d.ts +4 -0
  33. package/dist_ts/mod_format/formatters/license.formatter.js +8 -0
  34. package/dist_ts/mod_format/formatters/npmextra.formatter.d.ts +4 -0
  35. package/dist_ts/mod_format/formatters/npmextra.formatter.js +8 -0
  36. package/dist_ts/mod_format/formatters/packagejson.formatter.d.ts +4 -0
  37. package/dist_ts/mod_format/formatters/packagejson.formatter.js +8 -0
  38. package/dist_ts/mod_format/formatters/prettier.formatter.d.ts +9 -0
  39. package/dist_ts/mod_format/formatters/prettier.formatter.js +111 -0
  40. package/dist_ts/mod_format/formatters/readme.formatter.d.ts +7 -0
  41. package/dist_ts/mod_format/formatters/readme.formatter.js +19 -0
  42. package/dist_ts/mod_format/formatters/templates.formatter.d.ts +4 -0
  43. package/dist_ts/mod_format/formatters/templates.formatter.js +8 -0
  44. package/dist_ts/mod_format/formatters/tsconfig.formatter.d.ts +4 -0
  45. package/dist_ts/mod_format/formatters/tsconfig.formatter.js +8 -0
  46. package/dist_ts/mod_format/index.d.ts +14 -1
  47. package/dist_ts/mod_format/index.js +202 -28
  48. package/dist_ts/mod_format/interfaces.format.d.ts +43 -0
  49. package/dist_ts/mod_format/interfaces.format.js +2 -0
  50. package/dist_ts/mod_format/mod.plugins.d.ts +5 -1
  51. package/dist_ts/mod_format/mod.plugins.js +6 -2
  52. package/package.json +4 -3
  53. package/readme.hints.md +187 -0
  54. package/readme.plan.md +170 -0
  55. package/ts/00_commitinfo_data.ts +1 -1
  56. package/ts/gitzone.cli.ts +29 -1
  57. package/ts/gitzone.logging.ts +23 -1
  58. package/ts/mod_format/classes.baseformatter.ts +93 -0
  59. package/ts/mod_format/classes.changecache.ts +144 -0
  60. package/ts/mod_format/classes.dependency-analyzer.ts +107 -0
  61. package/ts/mod_format/classes.diffreporter.ts +108 -0
  62. package/ts/mod_format/classes.formatcontext.ts +65 -0
  63. package/ts/mod_format/classes.formatplanner.ts +184 -0
  64. package/ts/mod_format/classes.formatstats.ts +209 -0
  65. package/ts/mod_format/classes.rollbackmanager.ts +218 -0
  66. package/ts/mod_format/format.copy.ts +77 -1
  67. package/ts/mod_format/format.packagejson.ts +50 -1
  68. package/ts/mod_format/formatters/cleanup.formatter.ts +39 -0
  69. package/ts/mod_format/formatters/copy.formatter.ts +8 -0
  70. package/ts/mod_format/formatters/gitignore.formatter.ts +8 -0
  71. package/ts/mod_format/formatters/legacy.formatter.ts +36 -0
  72. package/ts/mod_format/formatters/license.formatter.ts +8 -0
  73. package/ts/mod_format/formatters/npmextra.formatter.ts +8 -0
  74. package/ts/mod_format/formatters/packagejson.formatter.ts +8 -0
  75. package/ts/mod_format/formatters/prettier.formatter.ts +125 -0
  76. package/ts/mod_format/formatters/readme.formatter.ts +22 -0
  77. package/ts/mod_format/formatters/templates.formatter.ts +8 -0
  78. package/ts/mod_format/formatters/tsconfig.formatter.ts +8 -0
  79. package/ts/mod_format/index.ts +241 -33
  80. package/ts/mod_format/interfaces.format.ts +45 -0
  81. package/ts/mod_format/mod.plugins.ts +8 -0
@@ -1,6 +1,82 @@
1
1
  import type { Project } from '../classes.project.js';
2
- import * as plugins from '../plugins.js';
2
+ import * as plugins from './mod.plugins.js';
3
+ import { logger } from '../gitzone.logging.js';
3
4
 
4
5
  export const run = async (projectArg: Project) => {
5
6
  const gitzoneConfig = await projectArg.gitzoneConfig;
7
+
8
+ // Get copy configuration from npmextra.json
9
+ const npmextraConfig = new plugins.npmextra.Npmextra();
10
+ const copyConfig = npmextraConfig.dataFor<any>('gitzone.format.copy', {
11
+ patterns: []
12
+ });
13
+
14
+ if (!copyConfig.patterns || copyConfig.patterns.length === 0) {
15
+ logger.log('info', 'No copy patterns configured in npmextra.json');
16
+ return;
17
+ }
18
+
19
+ for (const pattern of copyConfig.patterns) {
20
+ if (!pattern.from || !pattern.to) {
21
+ logger.log('warn', 'Invalid copy pattern - missing "from" or "to" field');
22
+ continue;
23
+ }
24
+
25
+ try {
26
+ // Handle glob patterns
27
+ const files = await plugins.smartfile.fs.listFileTree('.', pattern.from);
28
+
29
+ for (const file of files) {
30
+ const sourcePath = file;
31
+ let destPath = pattern.to;
32
+
33
+ // If destination is a directory, preserve filename
34
+ if (pattern.to.endsWith('/')) {
35
+ const filename = plugins.path.basename(file);
36
+ destPath = plugins.path.join(pattern.to, filename);
37
+ }
38
+
39
+ // Handle template variables in destination path
40
+ if (pattern.preservePath) {
41
+ const relativePath = plugins.path.relative(
42
+ plugins.path.dirname(pattern.from.replace(/\*/g, '')),
43
+ file
44
+ );
45
+ destPath = plugins.path.join(pattern.to, relativePath);
46
+ }
47
+
48
+ // Ensure destination directory exists
49
+ await plugins.smartfile.fs.ensureDir(plugins.path.dirname(destPath));
50
+
51
+ // Copy file
52
+ await plugins.smartfile.fs.copy(sourcePath, destPath);
53
+ logger.log('info', `Copied ${sourcePath} to ${destPath}`);
54
+ }
55
+ } catch (error) {
56
+ logger.log('error', `Failed to copy pattern ${pattern.from}: ${error.message}`);
57
+ }
58
+ }
6
59
  };
60
+
61
+ /**
62
+ * Example npmextra.json configuration:
63
+ * {
64
+ * "gitzone": {
65
+ * "format": {
66
+ * "copy": {
67
+ * "patterns": [
68
+ * {
69
+ * "from": "src/assets/*",
70
+ * "to": "dist/assets/",
71
+ * "preservePath": true
72
+ * },
73
+ * {
74
+ * "from": "config/*.json",
75
+ * "to": "dist/"
76
+ * }
77
+ * ]
78
+ * }
79
+ * }
80
+ * }
81
+ * }
82
+ */
@@ -13,7 +13,56 @@ const ensureDependency = async (
13
13
  position: 'dep' | 'devDep' | 'everywhere',
14
14
  constraint: 'exclude' | 'include' | 'latest',
15
15
  dependencyArg: string,
16
- ) => {};
16
+ ) => {
17
+ const [packageName, version] = dependencyArg.includes('@')
18
+ ? dependencyArg.split('@').filter(Boolean)
19
+ : [dependencyArg, 'latest'];
20
+
21
+ const targetSections: string[] = [];
22
+
23
+ switch (position) {
24
+ case 'dep':
25
+ targetSections.push('dependencies');
26
+ break;
27
+ case 'devDep':
28
+ targetSections.push('devDependencies');
29
+ break;
30
+ case 'everywhere':
31
+ targetSections.push('dependencies', 'devDependencies');
32
+ break;
33
+ }
34
+
35
+ for (const section of targetSections) {
36
+ if (!packageJsonObjectArg[section]) {
37
+ packageJsonObjectArg[section] = {};
38
+ }
39
+
40
+ switch (constraint) {
41
+ case 'exclude':
42
+ delete packageJsonObjectArg[section][packageName];
43
+ break;
44
+ case 'include':
45
+ if (!packageJsonObjectArg[section][packageName]) {
46
+ packageJsonObjectArg[section][packageName] = version === 'latest' ? '^1.0.0' : version;
47
+ }
48
+ break;
49
+ case 'latest':
50
+ // Fetch latest version from npm
51
+ try {
52
+ const registry = new plugins.smartnpm.NpmRegistry();
53
+ const packageInfo = await registry.getPackageInfo(packageName);
54
+ const latestVersion = packageInfo['dist-tags'].latest;
55
+ packageJsonObjectArg[section][packageName] = `^${latestVersion}`;
56
+ } catch (error) {
57
+ logger.log('warn', `Could not fetch latest version for ${packageName}, using existing or default`);
58
+ if (!packageJsonObjectArg[section][packageName]) {
59
+ packageJsonObjectArg[section][packageName] = version === 'latest' ? '^1.0.0' : version;
60
+ }
61
+ }
62
+ break;
63
+ }
64
+ }
65
+ };
17
66
 
18
67
  export const run = async (projectArg: Project) => {
19
68
  const formatStreamWrapper = new plugins.smartstream.StreamWrapper([
@@ -0,0 +1,39 @@
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 * as cleanupFormatter from '../format.cleanup.js';
5
+
6
+ export class CleanupFormatter extends BaseFormatter {
7
+ get name(): string {
8
+ return 'cleanup';
9
+ }
10
+
11
+ async analyze(): Promise<IPlannedChange[]> {
12
+ const changes: IPlannedChange[] = [];
13
+
14
+ // List of files to remove
15
+ const filesToRemove = ['yarn.lock', 'package-lock.json', 'tslint.json', 'defaults.yml'];
16
+
17
+ for (const file of filesToRemove) {
18
+ const exists = await plugins.smartfile.fs.fileExists(file);
19
+ if (exists) {
20
+ changes.push({
21
+ type: 'delete',
22
+ path: file,
23
+ module: this.name,
24
+ description: `Remove obsolete file`
25
+ });
26
+ }
27
+ }
28
+
29
+ return changes;
30
+ }
31
+
32
+ async applyChange(change: IPlannedChange): Promise<void> {
33
+ switch (change.type) {
34
+ case 'delete':
35
+ await this.deleteFile(change.path);
36
+ break;
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,8 @@
1
+ import { LegacyFormatter } from './legacy.formatter.js';
2
+ import * as formatCopy from '../format.copy.js';
3
+
4
+ export class CopyFormatter extends LegacyFormatter {
5
+ constructor(context: any, project: any) {
6
+ super(context, project, 'copy', formatCopy);
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ import { LegacyFormatter } from './legacy.formatter.js';
2
+ import * as formatGitignore from '../format.gitignore.js';
3
+
4
+ export class GitignoreFormatter extends LegacyFormatter {
5
+ constructor(context: any, project: any) {
6
+ super(context, project, 'gitignore', formatGitignore);
7
+ }
8
+ }
@@ -0,0 +1,36 @@
1
+ import { BaseFormatter } from '../classes.baseformatter.js';
2
+ import type { IPlannedChange } from '../interfaces.format.js';
3
+ import { Project } from '../../classes.project.js';
4
+ import * as plugins from '../mod.plugins.js';
5
+
6
+ // This is a wrapper for existing format modules
7
+ export class LegacyFormatter extends BaseFormatter {
8
+ private moduleName: string;
9
+ private formatModule: any;
10
+
11
+ constructor(context: any, project: Project, moduleName: string, formatModule: any) {
12
+ super(context, project);
13
+ this.moduleName = moduleName;
14
+ this.formatModule = formatModule;
15
+ }
16
+
17
+ get name(): string {
18
+ return this.moduleName;
19
+ }
20
+
21
+ async analyze(): Promise<IPlannedChange[]> {
22
+ // For legacy modules, we can't easily predict changes
23
+ // 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
+ }];
30
+ }
31
+
32
+ async applyChange(change: IPlannedChange): Promise<void> {
33
+ // Run the legacy format module
34
+ await this.formatModule.run(this.project);
35
+ }
36
+ }
@@ -0,0 +1,8 @@
1
+ import { LegacyFormatter } from './legacy.formatter.js';
2
+ import * as formatLicense from '../format.license.js';
3
+
4
+ export class LicenseFormatter extends LegacyFormatter {
5
+ constructor(context: any, project: any) {
6
+ super(context, project, 'license', formatLicense);
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ import { LegacyFormatter } from './legacy.formatter.js';
2
+ import * as formatNpmextra from '../format.npmextra.js';
3
+
4
+ export class NpmextraFormatter extends LegacyFormatter {
5
+ constructor(context: any, project: any) {
6
+ super(context, project, 'npmextra', formatNpmextra);
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ import { LegacyFormatter } from './legacy.formatter.js';
2
+ import * as formatPackageJson from '../format.packagejson.js';
3
+
4
+ export class PackageJsonFormatter extends LegacyFormatter {
5
+ constructor(context: any, project: any) {
6
+ super(context, project, 'packagejson', formatPackageJson);
7
+ }
8
+ }
@@ -0,0 +1,125 @@
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
+ export class PrettierFormatter extends BaseFormatter {
7
+ get name(): string {
8
+ return 'prettier';
9
+ }
10
+
11
+ async analyze(): Promise<IPlannedChange[]> {
12
+ const changes: IPlannedChange[] = [];
13
+ const globPattern = '**/*.{ts,tsx,js,jsx,json,md,css,scss,html,xml,yaml,yml}';
14
+
15
+ // Get all files that match the pattern
16
+ const files = await plugins.smartfile.fs.listFileTree('.', globPattern);
17
+
18
+ // Check which files need formatting
19
+ for (const file of files) {
20
+ // Skip files that haven't changed
21
+ if (!await this.shouldProcessFile(file)) {
22
+ logVerbose(`Skipping ${file} - no changes detected`);
23
+ continue;
24
+ }
25
+
26
+ changes.push({
27
+ type: 'modify',
28
+ path: file,
29
+ module: this.name,
30
+ description: 'Format with Prettier'
31
+ });
32
+ }
33
+
34
+ logger.log('info', `Found ${changes.length} files to format with Prettier`);
35
+ return changes;
36
+ }
37
+
38
+ async execute(changes: IPlannedChange[]): Promise<void> {
39
+ const startTime = this.stats.moduleStartTime(this.name);
40
+ this.stats.startModule(this.name);
41
+
42
+ try {
43
+ await this.preExecute();
44
+
45
+ // Batch process files
46
+ const batchSize = 10; // Process 10 files at a time
47
+ const batches: IPlannedChange[][] = [];
48
+
49
+ for (let i = 0; i < changes.length; i += batchSize) {
50
+ batches.push(changes.slice(i, i + batchSize));
51
+ }
52
+
53
+ logVerbose(`Processing ${changes.length} files in ${batches.length} batches`);
54
+
55
+ for (let i = 0; i < batches.length; i++) {
56
+ const batch = batches[i];
57
+ logVerbose(`Processing batch ${i + 1}/${batches.length} (${batch.length} files)`);
58
+
59
+ // Process batch in parallel
60
+ const promises = batch.map(async (change) => {
61
+ try {
62
+ await this.applyChange(change);
63
+ this.stats.recordFileOperation(this.name, change.type, true);
64
+ } catch (error) {
65
+ this.stats.recordFileOperation(this.name, change.type, false);
66
+ logger.log('error', `Failed to format ${change.path}: ${error.message}`);
67
+ // Don't throw - continue with other files
68
+ }
69
+ });
70
+
71
+ await Promise.all(promises);
72
+ }
73
+
74
+ await this.postExecute();
75
+ } catch (error) {
76
+ await this.context.rollbackOperation();
77
+ throw error;
78
+ } finally {
79
+ this.stats.endModule(this.name, startTime);
80
+ }
81
+ }
82
+
83
+ async applyChange(change: IPlannedChange): Promise<void> {
84
+ if (change.type !== 'modify') return;
85
+
86
+ try {
87
+ // Read current content
88
+ const content = await plugins.smartfile.fs.toStringSync(change.path);
89
+
90
+ // Format with prettier
91
+ const prettier = await import('prettier');
92
+ const formatted = await prettier.format(content, {
93
+ filepath: change.path,
94
+ ...(await this.getPrettierConfig())
95
+ });
96
+
97
+ // Only write if content actually changed
98
+ if (formatted !== content) {
99
+ await this.modifyFile(change.path, formatted);
100
+ logVerbose(`Formatted ${change.path}`);
101
+ } else {
102
+ // Still update cache even if content didn't change
103
+ await this.cache.updateFileCache(change.path);
104
+ logVerbose(`No formatting changes for ${change.path}`);
105
+ }
106
+ } catch (error) {
107
+ logger.log('error', `Failed to format ${change.path}: ${error.message}`);
108
+ throw error;
109
+ }
110
+ }
111
+
112
+ private async getPrettierConfig(): Promise<any> {
113
+ // Try to load prettier config from the project
114
+ const prettierConfig = new plugins.npmextra.Npmextra();
115
+ return prettierConfig.dataFor('prettier', {
116
+ // Default prettier config
117
+ singleQuote: true,
118
+ trailingComma: 'all',
119
+ printWidth: 80,
120
+ tabWidth: 2,
121
+ semi: true,
122
+ arrowParens: 'always'
123
+ });
124
+ }
125
+ }
@@ -0,0 +1,22 @@
1
+ import { BaseFormatter } from '../classes.baseformatter.js';
2
+ import type { IPlannedChange } from '../interfaces.format.js';
3
+ import * as formatReadme from '../format.readme.js';
4
+
5
+ export class ReadmeFormatter extends BaseFormatter {
6
+ get name(): string {
7
+ return 'readme';
8
+ }
9
+
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
+ }];
17
+ }
18
+
19
+ async applyChange(change: IPlannedChange): Promise<void> {
20
+ await formatReadme.run();
21
+ }
22
+ }
@@ -0,0 +1,8 @@
1
+ import { LegacyFormatter } from './legacy.formatter.js';
2
+ import * as formatTemplates from '../format.templates.js';
3
+
4
+ export class TemplatesFormatter extends LegacyFormatter {
5
+ constructor(context: any, project: any) {
6
+ super(context, project, 'templates', formatTemplates);
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ import { LegacyFormatter } from './legacy.formatter.js';
2
+ import * as formatTsconfig from '../format.tsconfig.js';
3
+
4
+ export class TsconfigFormatter extends LegacyFormatter {
5
+ constructor(context: any, project: any) {
6
+ super(context, project, 'tsconfig', formatTsconfig);
7
+ }
8
+ }