@winccoa-tools-pack/npm-winccoa-ctrl-code-style 0.1.1 → 0.1.2

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.
@@ -1,190 +1,190 @@
1
- /* eslint-env node */
2
- /* global console, process */
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- import { execFileSync } from 'node:child_process';
6
-
7
- function runGit(args) {
8
- return execFileSync('git', args, { encoding: 'utf8' }).trim();
9
- }
10
-
11
- function todayIsoDate() {
12
- const d = new Date();
13
- const yyyy = String(d.getFullYear()).padStart(4, '0');
14
- const mm = String(d.getMonth() + 1).padStart(2, '0');
15
- const dd = String(d.getDate()).padStart(2, '0');
16
- return `${yyyy}-${mm}-${dd}`;
17
- }
18
-
19
- function parseArgs(argv) {
20
- const args = new Set(argv);
21
- const getValue = (flag) => {
22
- const idx = argv.indexOf(flag);
23
- if (idx === -1) return undefined;
24
- return argv[idx + 1];
25
- };
26
-
27
- return {
28
- write: args.has('--write'),
29
- fromTag: getValue('--from-tag'),
30
- date: getValue('--date'),
31
- };
32
- }
33
-
34
- function getStableTags() {
35
- // Only stable SemVer tags like v2.3.1 (no suffix)
36
- const out = runGit(['tag', '--list', 'v[0-9]*.[0-9]*.[0-9]*', '--sort=version:refname']);
37
- return out ? out.split(/\r?\n/).map((t) => t.trim()).filter(Boolean) : [];
38
- }
39
-
40
- function getCommitSubjects(range) {
41
- const args = ['log', '--no-decorate', '--pretty=%s'];
42
- if (range) args.push(range);
43
- const out = runGit(args);
44
- if (!out) return [];
45
- return out
46
- .split(/\r?\n/)
47
- .map((s) => s.trim())
48
- .filter(Boolean);
49
- }
50
-
51
- function isNoiseSubject(subject) {
52
- if (subject.startsWith('Merge ')) return true;
53
- if (subject.startsWith('chore(release):')) return true;
54
- return false;
55
- }
56
-
57
- function categorize(subject) {
58
- // Conventional commits: type(scope)!: subject
59
- const match = /^(?<type>[a-z]+)(\([^\r\n()]+\))?(!)?:\s+(?<msg>.+)$/.exec(subject);
60
- const type = match?.groups?.type;
61
- const msg = match?.groups?.msg ?? subject;
62
-
63
- switch (type) {
64
- case 'feat':
65
- return { section: 'Added', text: msg };
66
- case 'fix':
67
- return { section: 'Fixed', text: msg };
68
- case 'perf':
69
- case 'refactor':
70
- return { section: 'Changed', text: msg };
71
- case 'docs':
72
- case 'build':
73
- case 'ci':
74
- case 'test':
75
- case 'style':
76
- case 'chore':
77
- case 'revert':
78
- case 'deps':
79
- case 'deps-dev':
80
- return { section: 'Changed', text: msg };
81
- default:
82
- return { section: 'Changed', text: subject };
83
- }
84
- }
85
-
86
- function renderEntry({ version, date, itemsBySection }) {
87
- const sectionsOrder = ['Added', 'Fixed', 'Changed'];
88
- const lines = [];
89
-
90
- lines.push(`## [${version}] - ${date}`);
91
- lines.push('');
92
-
93
- let any = false;
94
- for (const section of sectionsOrder) {
95
- const items = itemsBySection.get(section) ?? [];
96
- if (items.length === 0) continue;
97
- any = true;
98
- lines.push(`### ${section}`);
99
- lines.push('');
100
- for (const item of items) {
101
- lines.push(`- ${item}`);
102
- }
103
- lines.push('');
104
- }
105
-
106
- if (!any) {
107
- lines.push('### Changed');
108
- lines.push('');
109
- lines.push('- Maintenance release');
110
- lines.push('');
111
- }
112
-
113
- return lines.join('\n').trimEnd();
114
- }
115
-
116
- function insertIntoChangelog(changelogContent, entryMarkdown) {
117
- const firstHeadingIdx = changelogContent.indexOf('\n## [');
118
- if (firstHeadingIdx === -1) {
119
- return `${changelogContent.trimEnd()}\n\n${entryMarkdown}\n`;
120
- }
121
-
122
- const before = changelogContent.slice(0, firstHeadingIdx + 1); // keep leading newline
123
- const after = changelogContent.slice(firstHeadingIdx + 1);
124
- return `${before}${entryMarkdown}\n\n${after}`;
125
- }
126
-
127
- const { write, fromTag, date: dateArg } = parseArgs(process.argv.slice(2));
128
-
129
- const repoRoot = path.resolve(process.cwd());
130
- const packageJsonPath = path.join(repoRoot, 'package.json');
131
- const changelogPath = path.join(repoRoot, 'CHANGELOG.md');
132
-
133
- if (!fs.existsSync(packageJsonPath)) {
134
- console.error(`::error::Missing package.json at ${packageJsonPath}`);
135
- process.exit(1);
136
- }
137
-
138
- if (!fs.existsSync(changelogPath)) {
139
- console.error(`::error::Missing CHANGELOG.md at ${changelogPath}`);
140
- process.exit(1);
141
- }
142
-
143
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
144
- const version = String(pkg.version || '').trim();
145
- if (!version) {
146
- console.error('::error::package.json does not contain a valid "version" field');
147
- process.exit(1);
148
- }
149
-
150
- const changelog = fs.readFileSync(changelogPath, 'utf8');
151
- const expectedHeadingPrefix = `## [${version}] - `;
152
- const alreadyExists = changelog.includes(expectedHeadingPrefix);
153
-
154
- let startTag = fromTag;
155
- if (!startTag) {
156
- const stableTags = getStableTags().filter((t) => t !== `v${version}`);
157
- startTag = stableTags.length > 0 ? stableTags[stableTags.length - 1] : undefined;
158
- }
159
-
160
- const range = startTag ? `${startTag}..HEAD` : undefined;
161
- const subjects = getCommitSubjects(range)
162
- .filter((s) => !isNoiseSubject(s));
163
-
164
- const itemsBySection = new Map([
165
- ['Added', []],
166
- ['Fixed', []],
167
- ['Changed', []],
168
- ]);
169
-
170
- for (const subject of subjects) {
171
- const { section, text } = categorize(subject);
172
- itemsBySection.get(section)?.push(text);
173
- }
174
-
175
- const entry = renderEntry({ version, date: dateArg ?? todayIsoDate(), itemsBySection });
176
-
177
- process.stdout.write(entry + '\n');
178
-
179
- if (!write) {
180
- process.exit(0);
181
- }
182
-
183
- if (alreadyExists) {
184
- console.error(`CHANGELOG already contains heading for v${version}; skipping write.`);
185
- process.exit(0);
186
- }
187
-
188
- const updated = insertIntoChangelog(changelog, entry);
189
- fs.writeFileSync(changelogPath, updated, 'utf8');
190
- console.error(`✅ Inserted changelog entry for v${version} into CHANGELOG.md`);
1
+ /* eslint-env node */
2
+ /* global console, process */
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { execFileSync } from 'node:child_process';
6
+
7
+ function runGit(args) {
8
+ return execFileSync('git', args, { encoding: 'utf8' }).trim();
9
+ }
10
+
11
+ function todayIsoDate() {
12
+ const d = new Date();
13
+ const yyyy = String(d.getFullYear()).padStart(4, '0');
14
+ const mm = String(d.getMonth() + 1).padStart(2, '0');
15
+ const dd = String(d.getDate()).padStart(2, '0');
16
+ return `${yyyy}-${mm}-${dd}`;
17
+ }
18
+
19
+ function parseArgs(argv) {
20
+ const args = new Set(argv);
21
+ const getValue = (flag) => {
22
+ const idx = argv.indexOf(flag);
23
+ if (idx === -1) return undefined;
24
+ return argv[idx + 1];
25
+ };
26
+
27
+ return {
28
+ write: args.has('--write'),
29
+ fromTag: getValue('--from-tag'),
30
+ date: getValue('--date'),
31
+ };
32
+ }
33
+
34
+ function getStableTags() {
35
+ // Only stable SemVer tags like v2.3.1 (no suffix)
36
+ const out = runGit(['tag', '--list', 'v[0-9]*.[0-9]*.[0-9]*', '--sort=version:refname']);
37
+ return out ? out.split(/\r?\n/).map((t) => t.trim()).filter(Boolean) : [];
38
+ }
39
+
40
+ function getCommitSubjects(range) {
41
+ const args = ['log', '--no-decorate', '--pretty=%s'];
42
+ if (range) args.push(range);
43
+ const out = runGit(args);
44
+ if (!out) return [];
45
+ return out
46
+ .split(/\r?\n/)
47
+ .map((s) => s.trim())
48
+ .filter(Boolean);
49
+ }
50
+
51
+ function isNoiseSubject(subject) {
52
+ if (subject.startsWith('Merge ')) return true;
53
+ if (subject.startsWith('chore(release):')) return true;
54
+ return false;
55
+ }
56
+
57
+ function categorize(subject) {
58
+ // Conventional commits: type(scope)!: subject
59
+ const match = /^(?<type>[a-z]+)(\([^\r\n()]+\))?(!)?:\s+(?<msg>.+)$/.exec(subject);
60
+ const type = match?.groups?.type;
61
+ const msg = match?.groups?.msg ?? subject;
62
+
63
+ switch (type) {
64
+ case 'feat':
65
+ return { section: 'Added', text: msg };
66
+ case 'fix':
67
+ return { section: 'Fixed', text: msg };
68
+ case 'perf':
69
+ case 'refactor':
70
+ return { section: 'Changed', text: msg };
71
+ case 'docs':
72
+ case 'build':
73
+ case 'ci':
74
+ case 'test':
75
+ case 'style':
76
+ case 'chore':
77
+ case 'revert':
78
+ case 'deps':
79
+ case 'deps-dev':
80
+ return { section: 'Changed', text: msg };
81
+ default:
82
+ return { section: 'Changed', text: subject };
83
+ }
84
+ }
85
+
86
+ function renderEntry({ version, date, itemsBySection }) {
87
+ const sectionsOrder = ['Added', 'Fixed', 'Changed'];
88
+ const lines = [];
89
+
90
+ lines.push(`## [${version}] - ${date}`);
91
+ lines.push('');
92
+
93
+ let any = false;
94
+ for (const section of sectionsOrder) {
95
+ const items = itemsBySection.get(section) ?? [];
96
+ if (items.length === 0) continue;
97
+ any = true;
98
+ lines.push(`### ${section}`);
99
+ lines.push('');
100
+ for (const item of items) {
101
+ lines.push(`- ${item}`);
102
+ }
103
+ lines.push('');
104
+ }
105
+
106
+ if (!any) {
107
+ lines.push('### Changed');
108
+ lines.push('');
109
+ lines.push('- Maintenance release');
110
+ lines.push('');
111
+ }
112
+
113
+ return lines.join('\n').trimEnd();
114
+ }
115
+
116
+ function insertIntoChangelog(changelogContent, entryMarkdown) {
117
+ const firstHeadingIdx = changelogContent.indexOf('\n## [');
118
+ if (firstHeadingIdx === -1) {
119
+ return `${changelogContent.trimEnd()}\n\n${entryMarkdown}\n`;
120
+ }
121
+
122
+ const before = changelogContent.slice(0, firstHeadingIdx + 1); // keep leading newline
123
+ const after = changelogContent.slice(firstHeadingIdx + 1);
124
+ return `${before}${entryMarkdown}\n\n${after}`;
125
+ }
126
+
127
+ const { write, fromTag, date: dateArg } = parseArgs(process.argv.slice(2));
128
+
129
+ const repoRoot = path.resolve(process.cwd());
130
+ const packageJsonPath = path.join(repoRoot, 'package.json');
131
+ const changelogPath = path.join(repoRoot, 'CHANGELOG.md');
132
+
133
+ if (!fs.existsSync(packageJsonPath)) {
134
+ console.error(`::error::Missing package.json at ${packageJsonPath}`);
135
+ process.exit(1);
136
+ }
137
+
138
+ if (!fs.existsSync(changelogPath)) {
139
+ console.error(`::error::Missing CHANGELOG.md at ${changelogPath}`);
140
+ process.exit(1);
141
+ }
142
+
143
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
144
+ const version = String(pkg.version || '').trim();
145
+ if (!version) {
146
+ console.error('::error::package.json does not contain a valid "version" field');
147
+ process.exit(1);
148
+ }
149
+
150
+ const changelog = fs.readFileSync(changelogPath, 'utf8');
151
+ const expectedHeadingPrefix = `## [${version}] - `;
152
+ const alreadyExists = changelog.includes(expectedHeadingPrefix);
153
+
154
+ let startTag = fromTag;
155
+ if (!startTag) {
156
+ const stableTags = getStableTags().filter((t) => t !== `v${version}`);
157
+ startTag = stableTags.length > 0 ? stableTags[stableTags.length - 1] : undefined;
158
+ }
159
+
160
+ const range = startTag ? `${startTag}..HEAD` : undefined;
161
+ const subjects = getCommitSubjects(range)
162
+ .filter((s) => !isNoiseSubject(s));
163
+
164
+ const itemsBySection = new Map([
165
+ ['Added', []],
166
+ ['Fixed', []],
167
+ ['Changed', []],
168
+ ]);
169
+
170
+ for (const subject of subjects) {
171
+ const { section, text } = categorize(subject);
172
+ itemsBySection.get(section)?.push(text);
173
+ }
174
+
175
+ const entry = renderEntry({ version, date: dateArg ?? todayIsoDate(), itemsBySection });
176
+
177
+ process.stdout.write(entry + '\n');
178
+
179
+ if (!write) {
180
+ process.exit(0);
181
+ }
182
+
183
+ if (alreadyExists) {
184
+ console.error(`CHANGELOG already contains heading for v${version}; skipping write.`);
185
+ process.exit(0);
186
+ }
187
+
188
+ const updated = insertIntoChangelog(changelog, entry);
189
+ fs.writeFileSync(changelogPath, updated, 'utf8');
190
+ console.error(`✅ Inserted changelog entry for v${version} into CHANGELOG.md`);