license-checker-plugin 1.0.0 → 1.0.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.
@@ -15,8 +15,12 @@ export interface LicensePluginOptions {
15
15
  includeHomepage?: boolean;
16
16
  /** Include the author/publisher in each entry. */
17
17
  includeAuthor?: boolean;
18
- /** Exclude specific packages from the output by name. */
19
- excludePackages?: string[];
18
+ /**
19
+ * Exclude specific packages from the output.
20
+ * Can be a list of package names, or a predicate function
21
+ * `(packageName: string) => boolean`.
22
+ */
23
+ excludePackages?: (string | ((name: string) => boolean))[];
20
24
  /**
21
25
  * Allow only these licenses. When set, the build fails if any bundled
22
26
  * package has a license not in this list.
@@ -78,6 +82,8 @@ export declare class LicensePluginCore {
78
82
  private buildOutputItems;
79
83
  private recordReport;
80
84
  private mergeReports;
85
+ private readLicenseFromPackage;
86
+ private readLicenseFileText;
81
87
  format(items: OutputItem[]): string;
82
88
  private filterLicenseFields;
83
89
  private createFormatter;
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LicensePluginCore = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
4
6
  const LicenseDatabase_1 = require("./checker/LicenseDatabase");
7
+ const BuiltInLicenseChecker_1 = require("./checker/BuiltInLicenseChecker");
5
8
  const HtmlFormatter_1 = require("./formatter/HtmlFormatter");
6
9
  const JsonFormatter_1 = require("./formatter/JsonFormatter");
7
10
  const MarkdownFormatter_1 = require("./formatter/MarkdownFormatter");
@@ -68,11 +71,15 @@ class LicensePluginCore {
68
71
  resolveLicenseEntries(packages) {
69
72
  const entries = [];
70
73
  for (const pkgInfo of packages.values()) {
71
- if (this.options.excludePackages.includes(pkgInfo.name))
74
+ if (this.options.excludePackages.some((e) => (typeof e === 'function' ? e(pkgInfo.name) : e === pkgInfo.name)))
72
75
  continue;
76
+ let licenseInfo = this.db.getLicense(pkgInfo.name, pkgInfo.version);
77
+ if (licenseInfo.license === 'UNKNOWN') {
78
+ licenseInfo = this.readLicenseFromPackage(pkgInfo);
79
+ }
73
80
  entries.push({
74
81
  info: pkgInfo,
75
- licenseInfo: this.filterLicenseFields(this.db.getLicense(pkgInfo.name, pkgInfo.version)),
82
+ licenseInfo: this.filterLicenseFields(licenseInfo),
76
83
  });
77
84
  }
78
85
  return entries;
@@ -119,6 +126,67 @@ class LicensePluginCore {
119
126
  }
120
127
  return merged;
121
128
  }
129
+ readLicenseFromPackage(pkgInfo) {
130
+ try {
131
+ const raw = (0, fs_1.readFileSync)(pkgInfo.packageJsonPath, 'utf-8');
132
+ const pkg = JSON.parse(raw);
133
+ let licenseStr;
134
+ if (pkg.license) {
135
+ licenseStr = typeof pkg.license === 'string' ? pkg.license : pkg.license.type;
136
+ }
137
+ else if (Array.isArray(pkg.licenses)) {
138
+ licenseStr = pkg.licenses.map((l) => l.type || 'UNKNOWN').join(' AND ');
139
+ }
140
+ if (!licenseStr)
141
+ return { license: 'UNKNOWN' };
142
+ licenseStr = (0, BuiltInLicenseChecker_1.normalizeLicense)(licenseStr);
143
+ const result = { license: licenseStr };
144
+ if (this.options.includeLicenseText) {
145
+ const licenseText = this.readLicenseFileText(pkgInfo.path);
146
+ if (licenseText) {
147
+ result.licenseText = licenseText;
148
+ }
149
+ }
150
+ return result;
151
+ }
152
+ catch {
153
+ return { license: 'UNKNOWN' };
154
+ }
155
+ }
156
+ readLicenseFileText(packageDir) {
157
+ const basenames = [
158
+ /^LICENSE$/i, /^LICENSE\-\w+$/i, /^LICENCE$/i, /^LICENCE\-\w+$/i,
159
+ /^MIT-LICENSE$/i, /^COPYING$/i, /^COPYRIGHT$/i,
160
+ ];
161
+ try {
162
+ const files = (0, fs_1.readdirSync)(packageDir);
163
+ const candidates = [];
164
+ for (const file of files) {
165
+ const fullPath = (0, path_1.join)(packageDir, file);
166
+ try {
167
+ if (!(0, fs_1.statSync)(fullPath).isFile())
168
+ continue;
169
+ }
170
+ catch {
171
+ continue;
172
+ }
173
+ for (let i = 0; i < basenames.length; i++) {
174
+ if (basenames[i].test(file)) {
175
+ candidates.push({ file: fullPath, order: i });
176
+ break;
177
+ }
178
+ }
179
+ }
180
+ candidates.sort((a, b) => a.order - b.order);
181
+ if (candidates.length > 0) {
182
+ return (0, fs_1.readFileSync)(candidates[0].file, 'utf-8');
183
+ }
184
+ }
185
+ catch {
186
+ // ignore
187
+ }
188
+ return undefined;
189
+ }
122
190
  format(items) {
123
191
  return this.createFormatter().generate(items);
124
192
  }
@@ -187,56 +187,66 @@ function parseAuthor(author) {
187
187
  }
188
188
  async function findPackages(startPath) {
189
189
  const packages = [];
190
- const nodeModulesPath = path.join(startPath, 'node_modules');
191
- try {
192
- await (0, promises_1.stat)(nodeModulesPath);
193
- }
194
- catch {
195
- return packages;
196
- }
197
- try {
198
- const entries = await (0, promises_1.readdir)(nodeModulesPath, { withFileTypes: true });
199
- for (const entry of entries) {
200
- if (!entry.isDirectory())
201
- continue;
202
- if (entry.name.startsWith('.'))
203
- continue;
204
- if (entry.name.startsWith('@')) {
205
- const scopeDir = path.join(nodeModulesPath, entry.name);
206
- try {
207
- const scopeEntries = await (0, promises_1.readdir)(scopeDir, { withFileTypes: true });
208
- for (const scopeEntry of scopeEntries) {
209
- if (!scopeEntry.isDirectory())
210
- continue;
211
- const pkgPath = path.join(scopeDir, scopeEntry.name);
212
- try {
213
- await (0, promises_1.stat)(path.join(pkgPath, 'package.json'));
214
- packages.push(pkgPath);
215
- }
216
- catch {
217
- // Skip packages without package.json
190
+ const visited = new Set();
191
+ async function scan(nodeModulesPath) {
192
+ if (visited.has(nodeModulesPath))
193
+ return;
194
+ visited.add(nodeModulesPath);
195
+ try {
196
+ await (0, promises_1.stat)(nodeModulesPath);
197
+ }
198
+ catch {
199
+ return;
200
+ }
201
+ try {
202
+ const entries = await (0, promises_1.readdir)(nodeModulesPath, { withFileTypes: true });
203
+ for (const entry of entries) {
204
+ if (!entry.isDirectory())
205
+ continue;
206
+ if (entry.name.startsWith('.'))
207
+ continue;
208
+ if (entry.name.startsWith('@')) {
209
+ const scopeDir = path.join(nodeModulesPath, entry.name);
210
+ try {
211
+ const scopeEntries = await (0, promises_1.readdir)(scopeDir, { withFileTypes: true });
212
+ for (const scopeEntry of scopeEntries) {
213
+ if (!scopeEntry.isDirectory())
214
+ continue;
215
+ const pkgPath = path.join(scopeDir, scopeEntry.name);
216
+ try {
217
+ await (0, promises_1.stat)(path.join(pkgPath, 'package.json'));
218
+ packages.push(pkgPath);
219
+ // Recursively scan nested node_modules
220
+ await scan(path.join(pkgPath, 'node_modules'));
221
+ }
222
+ catch {
223
+ // Skip packages without package.json
224
+ }
218
225
  }
219
226
  }
227
+ catch {
228
+ // Ignore errors
229
+ }
220
230
  }
221
- catch {
222
- // Ignore errors
223
- }
224
- }
225
- else {
226
- const pkgPath = path.join(nodeModulesPath, entry.name);
227
- try {
228
- await (0, promises_1.stat)(path.join(pkgPath, 'package.json'));
229
- packages.push(pkgPath);
230
- }
231
- catch {
232
- // Skip packages without package.json
231
+ else {
232
+ const pkgPath = path.join(nodeModulesPath, entry.name);
233
+ try {
234
+ await (0, promises_1.stat)(path.join(pkgPath, 'package.json'));
235
+ packages.push(pkgPath);
236
+ // Recursively scan nested node_modules
237
+ await scan(path.join(pkgPath, 'node_modules'));
238
+ }
239
+ catch {
240
+ // Skip packages without package.json
241
+ }
233
242
  }
234
243
  }
235
244
  }
245
+ catch {
246
+ // Ignore errors
247
+ }
236
248
  }
237
- catch {
238
- // Ignore errors
239
- }
249
+ await scan(path.join(startPath, 'node_modules'));
240
250
  return packages;
241
251
  }
242
252
  async function processPackage(packagePath, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "license-checker-plugin",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A bundler-agnostic plugin to generate third-party license notices for bundled packages. Supports webpack 5, Rspack, and Vite.",
5
5
  "author": "Axetroy <axetroy.dev@gmail.com>",
6
6
  "repository": {
@@ -1,3 +0,0 @@
1
- export declare function readFileIfExists(filePath: string): string | null;
2
- export declare function readJsonFile(filePath: string): Record<string, unknown> | null;
3
- export declare function findFileInDir(dir: string, filename: string): string | null;
package/dist/utils/fs.js DELETED
@@ -1,61 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.readFileIfExists = readFileIfExists;
37
- exports.readJsonFile = readJsonFile;
38
- exports.findFileInDir = findFileInDir;
39
- const fs = __importStar(require("fs"));
40
- const path = __importStar(require("path"));
41
- function readFileIfExists(filePath) {
42
- try {
43
- return fs.readFileSync(filePath, 'utf-8');
44
- }
45
- catch (_a) {
46
- return null;
47
- }
48
- }
49
- function readJsonFile(filePath) {
50
- try {
51
- const content = fs.readFileSync(filePath, 'utf-8');
52
- return JSON.parse(content);
53
- }
54
- catch (_a) {
55
- return null;
56
- }
57
- }
58
- function findFileInDir(dir, filename) {
59
- const filePath = path.join(dir, filename);
60
- return fs.existsSync(filePath) ? filePath : null;
61
- }
@@ -1 +0,0 @@
1
- export declare function hashString(str: string): string;
@@ -1,7 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hashString = hashString;
4
- const crypto_1 = require("crypto");
5
- function hashString(str) {
6
- return (0, crypto_1.createHash)('md5').update(str).digest('hex');
7
- }
@@ -1,3 +0,0 @@
1
- export declare function findPackageRoot(modulePath: string): string | null;
2
- export declare function isNodeModule(filePath: string): boolean;
3
- export declare function getNodeModuleName(filePath: string): string | null;
@@ -1,64 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.findPackageRoot = findPackageRoot;
37
- exports.isNodeModule = isNodeModule;
38
- exports.getNodeModuleName = getNodeModuleName;
39
- const path = __importStar(require("path"));
40
- const fs_1 = require("./fs");
41
- function findPackageRoot(modulePath) {
42
- let dir = path.dirname(modulePath);
43
- const root = path.parse(dir).root;
44
- while (dir !== root) {
45
- const pkgJsonPath = path.join(dir, 'package.json');
46
- const pkg = (0, fs_1.readJsonFile)(pkgJsonPath);
47
- if (pkg && typeof pkg.name === 'string' && dir.includes('node_modules')) {
48
- return dir;
49
- }
50
- const parent = path.dirname(dir);
51
- if (parent === dir) {
52
- break;
53
- }
54
- dir = parent;
55
- }
56
- return null;
57
- }
58
- function isNodeModule(filePath) {
59
- return filePath.includes('node_modules');
60
- }
61
- function getNodeModuleName(filePath) {
62
- const match = filePath.match(/node_modules[\\/]((?:@[^\\/]+[\\/])?[^\\/]+)/);
63
- return match ? match[1] : null;
64
- }