utilium 2.5.2 → 2.5.4
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 +5 -2
- package/scripts/lice.js +89 -39
- package/scripts/tsconfig.json +10 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "utilium",
|
3
|
-
"version": "2.5.
|
3
|
+
"version": "2.5.4",
|
4
4
|
"description": "Typescript utilities",
|
5
5
|
"funding": {
|
6
6
|
"type": "individual",
|
@@ -41,7 +41,7 @@
|
|
41
41
|
"homepage": "https://github.com/james-pre/utilium#readme",
|
42
42
|
"devDependencies": {
|
43
43
|
"@eslint/js": "^9.12.0",
|
44
|
-
"@types/node": "^
|
44
|
+
"@types/node": "^22.18.0",
|
45
45
|
"eslint": "^9.12.0",
|
46
46
|
"globals": "^15.10.0",
|
47
47
|
"prettier": "^3.2.5",
|
@@ -55,5 +55,8 @@
|
|
55
55
|
},
|
56
56
|
"optionalDependencies": {
|
57
57
|
"@xterm/xterm": "^5.5.0"
|
58
|
+
},
|
59
|
+
"engines": {
|
60
|
+
"node": ">=22.0.0"
|
58
61
|
}
|
59
62
|
}
|
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, globSync, readdirSync, readFileSync, statSync } 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: inputs, 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] <inputs...>
|
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
|
}
|
@@ -95,38 +133,50 @@ async function write_file(path, display) {
|
|
95
133
|
return 'overwritten';
|
96
134
|
}
|
97
135
|
|
98
|
-
function check_dir(dir, display) {
|
99
|
-
|
136
|
+
function* check_dir(dir, display) {
|
137
|
+
if (should_exclude(dir, display)) return 'skipped';
|
100
138
|
|
101
|
-
const
|
139
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
102
140
|
|
103
141
|
for (const entry of entries) {
|
104
142
|
if (entry.isDirectory()) {
|
105
|
-
|
143
|
+
yield* check_dir(join(dir, entry.name), join(display, entry.name));
|
106
144
|
continue;
|
107
145
|
}
|
108
146
|
|
109
147
|
if (!entry.isFile()) continue;
|
110
148
|
|
111
|
-
const op = write ? write_file : check_file;
|
112
|
-
|
149
|
+
const op = opts.write ? write_file : check_file;
|
150
|
+
yield op(join(dir, entry.name), join(display, entry.name));
|
113
151
|
}
|
114
|
-
|
115
|
-
return results;
|
116
152
|
}
|
117
153
|
|
118
|
-
if (!
|
119
|
-
console.error(styleText('red', 'No
|
154
|
+
if (!inputs.length) {
|
155
|
+
console.error(styleText('red', 'No inputs specified'));
|
120
156
|
process.exit(1);
|
121
157
|
}
|
122
158
|
|
123
|
-
|
159
|
+
const globbed = globSync(inputs);
|
160
|
+
|
161
|
+
if (opts.verbose) console.log('Checking:', globbed.join(', '));
|
124
162
|
|
125
163
|
const promises = [];
|
126
164
|
|
127
|
-
for (const
|
128
|
-
const rel = relative(process.cwd(),
|
129
|
-
|
165
|
+
for (const input of globbed) {
|
166
|
+
const rel = relative(process.cwd(), input);
|
167
|
+
if (should_exclude(input, rel)) {
|
168
|
+
promises.push(Promise.resolve('skipped'));
|
169
|
+
continue;
|
170
|
+
}
|
171
|
+
|
172
|
+
const stat = statSync(input);
|
173
|
+
|
174
|
+
if (stat.isDirectory()) {
|
175
|
+
for (const result of check_dir(input, rel)) promises.push(result);
|
176
|
+
} else {
|
177
|
+
const op = opts.write ? write_file : check_file;
|
178
|
+
promises.push(op(input, rel));
|
179
|
+
}
|
130
180
|
}
|
131
181
|
|
132
182
|
const styles = Object.assign(Object.create(null), {
|
@@ -147,7 +197,7 @@ try {
|
|
147
197
|
results[result]++;
|
148
198
|
}
|
149
199
|
console.log(
|
150
|
-
write ? 'Wrote' : 'Checked',
|
200
|
+
opts.write ? 'Wrote' : 'Checked',
|
151
201
|
styleText('blueBright', raw_results.length.toString()),
|
152
202
|
'files:',
|
153
203
|
Object.entries(results)
|