licenseguard-cli 2.2.0 â 2.3.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/.claude/settings.local.json +7 -0
- package/CHANGELOG.md +14 -0
- package/lib/postinstall.js +56 -2
- package/lib/utils/git-helpers.js +102 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.3.0] - 2026-02-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **ESM (ECMAScript Module) Support** - Git hooks now work in ESM projects
|
|
13
|
+
- Auto-detects ESM projects via `package.json` `"type": "module"`
|
|
14
|
+
- Generates ESM-compatible git hooks using `import` syntax for ESM projects
|
|
15
|
+
- Generates CommonJS hooks using `require` syntax for CJS projects
|
|
16
|
+
- `isEsmProject()` utility function for ESM detection
|
|
17
|
+
- ESM hook generators: `generatePostCheckoutScriptEsm()`, `generatePreCommitScriptEsm()`
|
|
18
|
+
- ESM global hooks in `postinstall.js` for `npm install -g` compatibility
|
|
19
|
+
- Full test coverage: 23 unit tests + 5 integration tests
|
|
20
|
+
- Backward compatible - existing CJS projects unaffected
|
|
21
|
+
|
|
8
22
|
## [2.2.0] - 2025-11-25
|
|
9
23
|
|
|
10
24
|
### Added
|
package/lib/postinstall.js
CHANGED
|
@@ -15,7 +15,10 @@ const templateDir = path.join(homeDir, '.git-templates')
|
|
|
15
15
|
const hooksDir = path.join(templateDir, 'hooks')
|
|
16
16
|
|
|
17
17
|
// Self-contained hook scripts (only needs Node.js, not licenseguard)
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
// CommonJS hook generators
|
|
20
|
+
function generatePostCheckoutHookCjs() {
|
|
21
|
+
return `#!/usr/bin/env node
|
|
19
22
|
const fs = require('fs')
|
|
20
23
|
const path = require('path')
|
|
21
24
|
|
|
@@ -33,8 +36,10 @@ try {
|
|
|
33
36
|
}
|
|
34
37
|
process.exit(0)
|
|
35
38
|
`
|
|
39
|
+
}
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
function generatePreCommitHookCjs() {
|
|
42
|
+
return `#!/usr/bin/env node
|
|
38
43
|
const fs = require('fs')
|
|
39
44
|
const path = require('path')
|
|
40
45
|
|
|
@@ -52,6 +57,50 @@ try {
|
|
|
52
57
|
}
|
|
53
58
|
process.exit(0)
|
|
54
59
|
`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ESM hook generators
|
|
63
|
+
function generatePostCheckoutHookEsm() {
|
|
64
|
+
return `#!/usr/bin/env node
|
|
65
|
+
import fs from 'fs'
|
|
66
|
+
import path from 'path'
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const configPath = path.join(process.cwd(), '.licenseguardrc')
|
|
70
|
+
if (!fs.existsSync(configPath)) process.exit(0)
|
|
71
|
+
|
|
72
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
|
|
73
|
+
const blue = '\\x1b[34m'
|
|
74
|
+
const reset = '\\x1b[0m'
|
|
75
|
+
|
|
76
|
+
console.log(\`\${blue}đ This project uses \${config.license.toUpperCase()} License by \${config.owner}\${reset}\`)
|
|
77
|
+
} catch (e) {
|
|
78
|
+
// Silent fail - never block git
|
|
79
|
+
}
|
|
80
|
+
process.exit(0)
|
|
81
|
+
`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function generatePreCommitHookEsm() {
|
|
85
|
+
return `#!/usr/bin/env node
|
|
86
|
+
import fs from 'fs'
|
|
87
|
+
import path from 'path'
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const configPath = path.join(process.cwd(), '.licenseguardrc')
|
|
91
|
+
if (!fs.existsSync(configPath)) process.exit(0)
|
|
92
|
+
|
|
93
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
|
|
94
|
+
const blue = '\\x1b[34m'
|
|
95
|
+
const reset = '\\x1b[0m'
|
|
96
|
+
|
|
97
|
+
console.log(\`\${blue}âšī¸ Reminder: This project is licensed under \${config.license.toUpperCase()}\${reset}\`)
|
|
98
|
+
} catch (e) {
|
|
99
|
+
// Silent fail - never block git
|
|
100
|
+
}
|
|
101
|
+
process.exit(0)
|
|
102
|
+
`
|
|
103
|
+
}
|
|
55
104
|
|
|
56
105
|
function setupGlobalHooks() {
|
|
57
106
|
try {
|
|
@@ -68,6 +117,11 @@ function setupGlobalHooks() {
|
|
|
68
117
|
return
|
|
69
118
|
}
|
|
70
119
|
|
|
120
|
+
// Always use ESM for global hooks - ESM works in both ESM and CJS projects
|
|
121
|
+
// CJS hooks fail in ESM projects, but ESM hooks work everywhere
|
|
122
|
+
const postCheckoutHook = generatePostCheckoutHookEsm()
|
|
123
|
+
const preCommitHook = generatePreCommitHookEsm()
|
|
124
|
+
|
|
71
125
|
// Create template directories
|
|
72
126
|
if (!fs.existsSync(hooksDir)) {
|
|
73
127
|
fs.mkdirSync(hooksDir, { recursive: true })
|
package/lib/utils/git-helpers.js
CHANGED
|
@@ -15,6 +15,22 @@ function isGitRepo() {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Check if current project is an ESM (ECMAScript Module) project
|
|
20
|
+
* @returns {boolean} true if package.json has "type": "module"
|
|
21
|
+
*/
|
|
22
|
+
function isEsmProject() {
|
|
23
|
+
try {
|
|
24
|
+
if (!fs.existsSync('package.json')) {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
|
|
28
|
+
return pkg.type === 'module'
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
18
34
|
/**
|
|
19
35
|
* Initialize a git repository in the current directory
|
|
20
36
|
* @returns {boolean} true if git init succeeded
|
|
@@ -36,6 +52,7 @@ function initGitRepo() {
|
|
|
36
52
|
function installHooks() {
|
|
37
53
|
try {
|
|
38
54
|
const hooksDir = path.join('.git', 'hooks')
|
|
55
|
+
const isEsm = isEsmProject()
|
|
39
56
|
|
|
40
57
|
// Ensure hooks directory exists
|
|
41
58
|
if (!fs.existsSync(hooksDir)) {
|
|
@@ -43,8 +60,8 @@ function installHooks() {
|
|
|
43
60
|
}
|
|
44
61
|
|
|
45
62
|
const hooks = [
|
|
46
|
-
{ name: 'post-checkout', script: generatePostCheckoutScript() },
|
|
47
|
-
{ name: 'pre-commit', script: generatePreCommitScript() },
|
|
63
|
+
{ name: 'post-checkout', script: isEsm ? generatePostCheckoutScriptEsm() : generatePostCheckoutScript() },
|
|
64
|
+
{ name: 'pre-commit', script: isEsm ? generatePreCommitScriptEsm() : generatePreCommitScript() },
|
|
48
65
|
]
|
|
49
66
|
|
|
50
67
|
let hasConflicts = false
|
|
@@ -167,8 +184,91 @@ try {
|
|
|
167
184
|
`
|
|
168
185
|
}
|
|
169
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Generate post-checkout hook script (ESM version)
|
|
189
|
+
* @returns {string} Hook script content
|
|
190
|
+
*/
|
|
191
|
+
function generatePostCheckoutScriptEsm() {
|
|
192
|
+
return `#!/usr/bin/env node
|
|
193
|
+
import fs from 'fs'
|
|
194
|
+
import path from 'path'
|
|
195
|
+
import { fileURLToPath } from 'url'
|
|
196
|
+
|
|
197
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
198
|
+
const __dirname = path.dirname(__filename)
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
// Read .licenseguardrc from project root (relative to .git/hooks/)
|
|
202
|
+
const configPath = path.resolve(__dirname, '..', '..', '.licenseguardrc')
|
|
203
|
+
|
|
204
|
+
if (!fs.existsSync(configPath)) {
|
|
205
|
+
// No config file, silently exit
|
|
206
|
+
process.exit(0)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const configContent = fs.readFileSync(configPath, 'utf8')
|
|
210
|
+
const config = JSON.parse(configContent)
|
|
211
|
+
|
|
212
|
+
// Display license notification with color
|
|
213
|
+
const blue = '\\x1b[34m'
|
|
214
|
+
const reset = '\\x1b[0m'
|
|
215
|
+
|
|
216
|
+
console.log(\`\${blue}đ This project uses \${config.license.toUpperCase()} License by \${config.owner}\${reset}\`)
|
|
217
|
+
|
|
218
|
+
process.exit(0)
|
|
219
|
+
} catch (error) {
|
|
220
|
+
// Errors should not block git operations
|
|
221
|
+
process.exit(0)
|
|
222
|
+
}
|
|
223
|
+
`
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Generate pre-commit hook script (ESM version)
|
|
228
|
+
* @returns {string} Hook script content
|
|
229
|
+
*/
|
|
230
|
+
function generatePreCommitScriptEsm() {
|
|
231
|
+
return `#!/usr/bin/env node
|
|
232
|
+
import fs from 'fs'
|
|
233
|
+
import path from 'path'
|
|
234
|
+
import { fileURLToPath } from 'url'
|
|
235
|
+
|
|
236
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
237
|
+
const __dirname = path.dirname(__filename)
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
// Read .licenseguardrc from project root (relative to .git/hooks/)
|
|
241
|
+
const configPath = path.resolve(__dirname, '..', '..', '.licenseguardrc')
|
|
242
|
+
|
|
243
|
+
if (!fs.existsSync(configPath)) {
|
|
244
|
+
// No config file, silently exit
|
|
245
|
+
process.exit(0)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const configContent = fs.readFileSync(configPath, 'utf8')
|
|
249
|
+
const config = JSON.parse(configContent)
|
|
250
|
+
|
|
251
|
+
// Display license reminder with color
|
|
252
|
+
const blue = '\\x1b[34m'
|
|
253
|
+
const reset = '\\x1b[0m'
|
|
254
|
+
|
|
255
|
+
console.log(\`\${blue}âšī¸ Reminder: This project is licensed under \${config.license.toUpperCase()}\${reset}\`)
|
|
256
|
+
|
|
257
|
+
process.exit(0)
|
|
258
|
+
} catch (error) {
|
|
259
|
+
// Errors should not block git operations
|
|
260
|
+
process.exit(0)
|
|
261
|
+
}
|
|
262
|
+
`
|
|
263
|
+
}
|
|
264
|
+
|
|
170
265
|
module.exports = {
|
|
171
266
|
isGitRepo,
|
|
172
267
|
initGitRepo,
|
|
173
268
|
installHooks,
|
|
269
|
+
isEsmProject,
|
|
270
|
+
generatePostCheckoutScript,
|
|
271
|
+
generatePreCommitScript,
|
|
272
|
+
generatePostCheckoutScriptEsm,
|
|
273
|
+
generatePreCommitScriptEsm,
|
|
174
274
|
}
|