utilium 2.5.2 → 2.5.3
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/package.json +1 -1
- package/scripts/lice.js +67 -27
- package/scripts/tsconfig.json +10 -0
package/package.json
CHANGED
package/scripts/lice.js
CHANGED
@@ -2,37 +2,78 @@
|
|
2
2
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
3
3
|
// Copyright (c) 2025 James Prevett
|
4
4
|
|
5
|
-
import { readdirSync } from 'node:fs';
|
5
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
6
6
|
import { readFile, writeFile } from 'node:fs/promises';
|
7
|
-
import { join, matchesGlob, relative } from 'node:path';
|
7
|
+
import { dirname, join, matchesGlob, relative } from 'node:path';
|
8
8
|
import { parseArgs, styleText } from 'node:util';
|
9
9
|
|
10
|
-
const {
|
11
|
-
positionals: dirs,
|
12
|
-
values: { license: expectedLicense, write, verbose, force, exclude },
|
13
|
-
} = parseArgs({
|
10
|
+
const { positionals: dirs, values: opts } = parseArgs({
|
14
11
|
allowPositionals: true,
|
15
12
|
options: {
|
16
|
-
|
17
|
-
|
18
|
-
verbose: { type: 'boolean', short: 'v', default: false },
|
13
|
+
auto: { type: 'boolean', short: 'a', default: false },
|
14
|
+
exclude: { type: 'string', short: 'x', default: [], multiple: true },
|
19
15
|
force: { type: 'boolean', short: 'f', default: false },
|
20
|
-
|
16
|
+
help: { type: 'boolean', short: 'h', default: false },
|
17
|
+
license: { type: 'string', short: 'l' },
|
18
|
+
verbose: { type: 'boolean', short: 'v', default: false },
|
19
|
+
write: { type: 'boolean', short: 'w', default: false },
|
21
20
|
},
|
22
21
|
});
|
23
22
|
|
24
|
-
if (
|
25
|
-
console.error(
|
23
|
+
if (opts.help) {
|
24
|
+
console.error(`Usage: lice [options] <dirs...>
|
25
|
+
|
26
|
+
Options:
|
27
|
+
-f, --force Force overwrite of existing license headers
|
28
|
+
-h, --help Show this help message
|
29
|
+
-a, --auto Detect the SPDX identifier in package.json
|
30
|
+
-l, --license Specify the SPDX license identifier to check for.
|
31
|
+
-v, --verbose Enable verbose output
|
32
|
+
-w, --write Write the license header if missing
|
33
|
+
-x, --exclude Glob pattern to exclude files
|
34
|
+
`);
|
35
|
+
process.exit(0);
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
*
|
40
|
+
* @returns {string|null}
|
41
|
+
*/
|
42
|
+
function get_license() {
|
43
|
+
for (let dir = process.cwd(); dir != '/'; dir = dirname(dir)) {
|
44
|
+
const pkgPath = join(dir, 'package.json');
|
45
|
+
if (!existsSync(pkgPath)) continue;
|
46
|
+
|
47
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
48
|
+
if (pkg.spdx) return pkg.spdx;
|
49
|
+
if (pkg.spdxLicense) return pkg.spdxLicense;
|
50
|
+
if (pkg.license) return pkg.license;
|
51
|
+
}
|
52
|
+
|
53
|
+
return null;
|
54
|
+
}
|
55
|
+
|
56
|
+
const expectedLicense = opts.license || (opts.auto && get_license());
|
57
|
+
|
58
|
+
if (opts.write && !expectedLicense) {
|
59
|
+
console.error(styleText('red', 'You must specify a license to write.'));
|
26
60
|
process.exit(1);
|
27
61
|
}
|
28
62
|
|
63
|
+
function should_exclude(path, display) {
|
64
|
+
for (const pattern of opts.exclude) {
|
65
|
+
if (!matchesGlob(path, pattern)) continue;
|
66
|
+
console.log(styleText('whiteBright', 'Skipped:'), display);
|
67
|
+
return true;
|
68
|
+
}
|
69
|
+
|
70
|
+
return false;
|
71
|
+
}
|
72
|
+
|
29
73
|
const licenseSpec = /^\s*\/(?:\/|\*) SPDX-License-Identifier: (.+)/;
|
30
74
|
|
31
75
|
async function check_file(path, display) {
|
32
|
-
if (
|
33
|
-
console.log(styleText('whiteBright', 'Skipped:'), display);
|
34
|
-
return 'skipped';
|
35
|
-
}
|
76
|
+
if (should_exclude(path, display)) return 'skipped';
|
36
77
|
|
37
78
|
const content = await readFile(path, 'utf-8');
|
38
79
|
|
@@ -44,14 +85,14 @@ async function check_file(path, display) {
|
|
44
85
|
}
|
45
86
|
|
46
87
|
if (!expectedLicense) {
|
47
|
-
if (verbose) console.log(styleText(['dim'], 'Found:'), display);
|
88
|
+
if (opts.verbose) console.log(styleText(['dim'], 'Found:'), display);
|
48
89
|
return 'with license';
|
49
90
|
}
|
50
91
|
|
51
92
|
const [, license] = match;
|
52
93
|
|
53
94
|
if (license == expectedLicense) {
|
54
|
-
if (verbose) console.log(styleText(['green', 'dim'], 'Correct:'), display);
|
95
|
+
if (opts.verbose) console.log(styleText(['green', 'dim'], 'Correct:'), display);
|
55
96
|
return 'correct';
|
56
97
|
}
|
57
98
|
|
@@ -60,10 +101,7 @@ async function check_file(path, display) {
|
|
60
101
|
}
|
61
102
|
|
62
103
|
async function write_file(path, display) {
|
63
|
-
if (
|
64
|
-
console.log(styleText('whiteBright', 'Skipped:'), display);
|
65
|
-
return 'skipped';
|
66
|
-
}
|
104
|
+
if (should_exclude(path, display)) return 'skipped';
|
67
105
|
|
68
106
|
const content = await readFile(path, 'utf-8');
|
69
107
|
|
@@ -79,13 +117,13 @@ async function write_file(path, display) {
|
|
79
117
|
const [, license] = match;
|
80
118
|
|
81
119
|
if (license == expectedLicense) {
|
82
|
-
if (verbose) console.log(styleText(['green', 'dim'], 'Correct:'), display);
|
120
|
+
if (opts.verbose) console.log(styleText(['green', 'dim'], 'Correct:'), display);
|
83
121
|
return 'correct';
|
84
122
|
}
|
85
123
|
|
86
124
|
process.stdout.write(styleText('yellow', 'Mismatch: ') + display);
|
87
125
|
|
88
|
-
if (!force) {
|
126
|
+
if (!opts.force) {
|
89
127
|
console.log(styleText('whiteBright', ' (skipped)'));
|
90
128
|
return 'skipped';
|
91
129
|
}
|
@@ -96,6 +134,8 @@ async function write_file(path, display) {
|
|
96
134
|
}
|
97
135
|
|
98
136
|
function check_dir(dir, display) {
|
137
|
+
if (should_exclude(dir, display)) return 'skipped';
|
138
|
+
|
99
139
|
const entries = readdirSync(dir, { withFileTypes: true });
|
100
140
|
|
101
141
|
const results = [];
|
@@ -108,7 +148,7 @@ function check_dir(dir, display) {
|
|
108
148
|
|
109
149
|
if (!entry.isFile()) continue;
|
110
150
|
|
111
|
-
const op = write ? write_file : check_file;
|
151
|
+
const op = opts.write ? write_file : check_file;
|
112
152
|
results.push(op(join(dir, entry.name), join(display, entry.name)));
|
113
153
|
}
|
114
154
|
|
@@ -120,7 +160,7 @@ if (!dirs.length) {
|
|
120
160
|
process.exit(1);
|
121
161
|
}
|
122
162
|
|
123
|
-
if (verbose) console.log('Checking:', dirs.join(', '));
|
163
|
+
if (opts.verbose) console.log('Checking:', dirs.join(', '));
|
124
164
|
|
125
165
|
const promises = [];
|
126
166
|
|
@@ -147,7 +187,7 @@ try {
|
|
147
187
|
results[result]++;
|
148
188
|
}
|
149
189
|
console.log(
|
150
|
-
write ? 'Wrote' : 'Checked',
|
190
|
+
opts.write ? 'Wrote' : 'Checked',
|
151
191
|
styleText('blueBright', raw_results.length.toString()),
|
152
192
|
'files:',
|
153
193
|
Object.entries(results)
|