@final-commerce/common 2.0.16 → 2.0.18-beta.1
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.
- package/bin/gate-save.mjs +7 -59
- package/bin/gate-setup.mjs +1 -10
- package/bin/gate-start.mjs +3 -31
- package/package.json +1 -1
- package/bin/prepare-commit-msg.sh +0 -16
package/bin/gate-save.mjs
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* gate-save — pre-commit hook.
|
|
4
4
|
*
|
|
5
5
|
* 1. Runs lint-staged over modified surfaces.
|
|
6
|
-
* 2.
|
|
7
|
-
* reference (e.g. "// TODO: do something FI-123"). Bare TODOs block commit.
|
|
6
|
+
* 2. Syncs i18n bundles when the repo opts in via the fc-i18n package.json key.
|
|
8
7
|
*
|
|
9
8
|
* Exits 0 on success, 1 on any gate failure.
|
|
10
9
|
*
|
|
@@ -48,7 +47,6 @@ if (stagedResult.status !== 0) {
|
|
|
48
47
|
fail(`Could not enumerate staged files:\n${stagedResult.stderr.trim()}`);
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
const CODE_EXTS = /\.(ts|tsx|mts|cts|js|mjs|cjs|jsx)$/;
|
|
52
50
|
const VERSION_BUMP_FILES = /^(package\.json|package-lock\.json|yarn\.lock)$/;
|
|
53
51
|
|
|
54
52
|
const allStaged = stagedResult.stdout
|
|
@@ -61,8 +59,6 @@ if (allStaged.length > 0 && allStaged.every((f) => VERSION_BUMP_FILES.test(f)))
|
|
|
61
59
|
process.exit(0);
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
const codeFiles = allStaged.filter((f) => CODE_EXTS.test(f) && !f.startsWith('bin/'));
|
|
65
|
-
|
|
66
62
|
// ── 1. lint-staged ────────────────────────────────────────────────────────────
|
|
67
63
|
|
|
68
64
|
info('Running lint-staged…');
|
|
@@ -74,64 +70,16 @@ if (lintResult.status !== 0) {
|
|
|
74
70
|
|
|
75
71
|
ok('lint-staged passed.');
|
|
76
72
|
|
|
77
|
-
// ── 2.
|
|
78
|
-
|
|
79
|
-
info('Scanning staged files for bare TODOs…');
|
|
80
|
-
|
|
81
|
-
// A Jira-style ticket reference: one or more uppercase letters, a dash, and digits.
|
|
82
|
-
const JIRA_REF_RE = /[A-Z]+-\d+/;
|
|
83
|
-
// Matches any TODO annotation (case-insensitive), with optional owner parens.
|
|
84
|
-
const TODO_LINE_RE = /TODO(?:\s*\([^)]*\))?\s*[::]?\s+(.+)/i;
|
|
85
|
-
|
|
86
|
-
const violations = [];
|
|
87
|
-
|
|
88
|
-
for (const file of codeFiles) {
|
|
89
|
-
// Read the staged blob, not the working-tree file, to avoid false positives
|
|
90
|
-
// from unstaged edits made after the last `git add`.
|
|
91
|
-
const blobResult = capture(`git show :${file}`);
|
|
92
|
-
if (blobResult.status !== 0) continue;
|
|
93
|
-
|
|
94
|
-
const lines = blobResult.stdout.split('\n');
|
|
95
|
-
|
|
96
|
-
for (let i = 0; i < lines.length; i++) {
|
|
97
|
-
const match = lines[i].match(TODO_LINE_RE);
|
|
98
|
-
if (match && !JIRA_REF_RE.test(lines[i])) {
|
|
99
|
-
violations.push({ file, line: i + 1, text: lines[i].trim() });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (violations.length > 0) {
|
|
105
|
-
process.stderr.write(
|
|
106
|
-
'\x1b[31m✖ Bare TODOs detected — each TODO must carry a Jira ticket reference:\x1b[0m\n',
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
const byFile = violations.reduce((acc, v) => {
|
|
110
|
-
(acc[v.file] ??= []).push(v);
|
|
111
|
-
return acc;
|
|
112
|
-
}, {});
|
|
113
|
-
|
|
114
|
-
for (const [file, items] of Object.entries(byFile)) {
|
|
115
|
-
process.stderr.write(`\x1b[33m\n ${file}\x1b[0m\n`);
|
|
116
|
-
items.forEach(({ line, text }) => {
|
|
117
|
-
process.stderr.write(`\x1b[90m ${String(line).padStart(4)} ${text}\x1b[0m\n`);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
process.stderr.write(
|
|
122
|
-
'\x1b[90m\n Example fix: // TODO: refactor this FI-999\x1b[0m\n',
|
|
123
|
-
);
|
|
124
|
-
fail(`${violations.length} bare TODO(s) must be resolved before committing.`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
ok('No bare TODOs detected.');
|
|
128
|
-
|
|
129
|
-
// ── 3. i18n sync ──────────────────────────────────────────────────────────────
|
|
73
|
+
// ── 2. i18n sync ──────────────────────────────────────────────────────────────
|
|
130
74
|
|
|
131
75
|
const pkgJsonPath = resolve(process.cwd(), 'package.json');
|
|
132
76
|
if (existsSync(pkgJsonPath)) {
|
|
133
77
|
let rootPkg;
|
|
134
|
-
try {
|
|
78
|
+
try {
|
|
79
|
+
rootPkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
|
|
80
|
+
} catch {
|
|
81
|
+
/* ignore */
|
|
82
|
+
}
|
|
135
83
|
|
|
136
84
|
if (rootPkg?.['fc-i18n']?.slug) {
|
|
137
85
|
info('Running i18n sync…');
|
package/bin/gate-setup.mjs
CHANGED
|
@@ -114,15 +114,6 @@ function writeHook(name, content) {
|
|
|
114
114
|
writeHook('pre-commit', 'npx gate-save');
|
|
115
115
|
writeHook('pre-push', 'npx gate-done');
|
|
116
116
|
|
|
117
|
-
const prepareCommitMsgHook = join(huskyDir, 'prepare-commit-msg');
|
|
118
|
-
if (existsSync(prepareCommitMsgHook)) {
|
|
119
|
-
skip('.husky/prepare-commit-msg');
|
|
120
|
-
} else {
|
|
121
|
-
writeFileSync(prepareCommitMsgHook, readFileSync(join(import.meta.dirname, 'prepare-commit-msg.sh'), 'utf8'), 'utf8');
|
|
122
|
-
chmodSync(prepareCommitMsgHook, 0o755);
|
|
123
|
-
ok('Created .husky/prepare-commit-msg');
|
|
124
|
-
}
|
|
125
|
-
|
|
126
117
|
writeHook('commit-msg', 'npx --no -- commitlint --edit "$1"');
|
|
127
118
|
|
|
128
119
|
// ── 6. package.json mutations ─────────────────────────────────────────────────
|
|
@@ -157,7 +148,7 @@ if (!pkg['lint-staged']) {
|
|
|
157
148
|
ok('Added lint-staged config to package.json');
|
|
158
149
|
}
|
|
159
150
|
|
|
160
|
-
// devDependencies — add husky, lint-staged,
|
|
151
|
+
// devDependencies — add husky, lint-staged, and commitlint if missing.
|
|
161
152
|
pkg.devDependencies ??= {};
|
|
162
153
|
const devDepsToAdd = [
|
|
163
154
|
['husky', '^9.0.0'],
|
package/bin/gate-start.mjs
CHANGED
|
@@ -3,22 +3,18 @@
|
|
|
3
3
|
* gate-start — Developer session initialiser.
|
|
4
4
|
*
|
|
5
5
|
* 1. Verifies @final-commerce/common in node_modules matches latest registry release.
|
|
6
|
-
* 2. Prompts for (or accepts via arg/env) a Jira ticket ID.
|
|
7
6
|
*
|
|
8
|
-
* Branch checkout
|
|
7
|
+
* Branch checkout and ticket tracking are handled by the tasks-manager workspace tooling.
|
|
9
8
|
*
|
|
10
9
|
* Usage:
|
|
11
|
-
* gate-start
|
|
12
|
-
* JIRA_TICKET_ID=FI-1234 gate-start
|
|
10
|
+
* gate-start
|
|
13
11
|
*/
|
|
14
12
|
|
|
15
13
|
import { spawnSync } from 'node:child_process';
|
|
16
|
-
import { createInterface } from 'node:readline';
|
|
17
14
|
import { existsSync, readFileSync } from 'node:fs';
|
|
18
15
|
import { resolve } from 'node:path';
|
|
19
16
|
|
|
20
17
|
const PKG_NAME = '@final-commerce/common';
|
|
21
|
-
const TICKET_RE = /^[A-Z]+-\d+$/;
|
|
22
18
|
|
|
23
19
|
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
24
20
|
|
|
@@ -39,16 +35,6 @@ function ok(msg) {
|
|
|
39
35
|
process.stdout.write(`\x1b[32m✔ ${msg}\x1b[0m\n`);
|
|
40
36
|
}
|
|
41
37
|
|
|
42
|
-
function ask(question) {
|
|
43
|
-
return new Promise((resolve) => {
|
|
44
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
45
|
-
rl.question(question, (answer) => {
|
|
46
|
-
rl.close();
|
|
47
|
-
resolve(answer.trim());
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
38
|
// ── 1. upstream version check ─────────────────────────────────────────────────
|
|
53
39
|
|
|
54
40
|
info(`Checking ${PKG_NAME} upstream version…`);
|
|
@@ -84,18 +70,4 @@ if (localVersion !== latestVersion) {
|
|
|
84
70
|
);
|
|
85
71
|
}
|
|
86
72
|
|
|
87
|
-
ok(`${PKG_NAME}@${localVersion} is current.`);
|
|
88
|
-
|
|
89
|
-
// ── 2. Jira ticket ID ─────────────────────────────────────────────────────────
|
|
90
|
-
|
|
91
|
-
let ticketId = (process.env.JIRA_TICKET_ID ?? process.argv[2] ?? '').toUpperCase();
|
|
92
|
-
|
|
93
|
-
if (!ticketId) {
|
|
94
|
-
ticketId = (await ask('\nJira ticket ID (e.g. FI-1234): ')).toUpperCase();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (!TICKET_RE.test(ticketId)) {
|
|
98
|
-
fail(`"${ticketId}" is not a valid Jira ticket ID. Expected format: PROJECT-123`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
ok(`Ticket ${ticketId} confirmed. Ready to work.`);
|
|
73
|
+
ok(`${PKG_NAME}@${localVersion} is current. Ready to work.`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@final-commerce/common",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.18-beta.1",
|
|
4
4
|
"description": "Shared utilities, types, constants, and engineering governance configs for the Final Commerce platform.",
|
|
5
5
|
"homepage": "https://github.com/Final-Commerce/common#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env sh
|
|
2
|
-
|
|
3
|
-
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
|
|
4
|
-
if [ -z "$BRANCH" ]; then exit 0; fi
|
|
5
|
-
|
|
6
|
-
# Skip protected branches
|
|
7
|
-
if echo "$BRANCH" | grep -qE '^(main|master|develop|pre-prod|release-staging.*)$'; then exit 0; fi
|
|
8
|
-
|
|
9
|
-
TICKET=$(echo "$BRANCH" | grep -oE '[A-Z]+-[0-9]+' | head -1)
|
|
10
|
-
if [ -z "$TICKET" ]; then exit 0; fi
|
|
11
|
-
|
|
12
|
-
MSG_FILE="$1"
|
|
13
|
-
CURRENT=$(cat "$MSG_FILE")
|
|
14
|
-
if echo "$CURRENT" | grep -q "$TICKET"; then exit 0; fi
|
|
15
|
-
|
|
16
|
-
awk -v ticket="[$TICKET]" 'NR==1{print $0 " " ticket; next}{print}' "$MSG_FILE" > "$MSG_FILE.tmp" && mv "$MSG_FILE.tmp" "$MSG_FILE"
|