1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | "use strict";
|
27 |
|
28 | var _ = require("lodash");
|
29 | var fs = require("fs");
|
30 | var path = require("path");
|
31 | var mkdirp = require("mkdirp");
|
32 | var moment = require("moment");
|
33 | var MagicString = require("magic-string");
|
34 | var glob = require("glob");
|
35 | var packageNameRegex = require("package-name-regex");
|
36 | var commenting = require("commenting");
|
37 | var spdxExpressionValidate = require("spdx-expression-validate");
|
38 | var spdxSatisfies = require("spdx-satisfies");
|
39 |
|
40 | const EOL = "\n";
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | class Person {
|
49 | |
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | constructor(person) {
|
59 | if (_.isString(person)) {
|
60 | const o = {};
|
61 | let current = "name";
|
62 | for (let i = 0, size = person.length; i < size; ++i) {
|
63 | const character = person.charAt(i);
|
64 | if (character === "<") {
|
65 | current = "email";
|
66 | } else if (character === "(") {
|
67 | current = "url";
|
68 | } else if (character !== ")" && character !== ">") {
|
69 | o[current] = (o[current] || "") + character;
|
70 | }
|
71 | }
|
72 | _.forEach(["name", "email", "url"], (prop) => {
|
73 | if (_.has(o, prop)) {
|
74 | o[prop] = _.trim(o[prop]);
|
75 | }
|
76 | });
|
77 | person = o;
|
78 | }
|
79 | this.name = person.name || null;
|
80 | this.email = person.email || null;
|
81 | this.url = person.url || null;
|
82 | }
|
83 |
|
84 | |
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | text() {
|
91 | let text = `${this.name}`;
|
92 | if (this.email) {
|
93 | text += ` <${this.email}>`;
|
94 | }
|
95 | if (this.url) {
|
96 | text += ` (${this.url})`;
|
97 | }
|
98 | return text;
|
99 | }
|
100 | }
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | class Dependency {
|
106 | |
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 | constructor(pkg) {
|
113 | this.name = pkg.name || null;
|
114 | this.maintainers = pkg.maintainers || [];
|
115 | this.version = pkg.version || null;
|
116 | this.description = pkg.description || null;
|
117 | this.repository = pkg.repository || null;
|
118 | this.homepage = pkg.homepage || null;
|
119 | this.private = pkg.private || false;
|
120 | this.license = pkg.license || null;
|
121 | this.licenseText = pkg.licenseText || null;
|
122 |
|
123 |
|
124 | this.author = pkg.author ? new Person(pkg.author) : null;
|
125 |
|
126 |
|
127 | this.contributors = _.map(
|
128 | _.castArray(pkg.contributors || []),
|
129 | (contributor) => new Person(contributor),
|
130 | );
|
131 |
|
132 |
|
133 |
|
134 | if (!this.license && pkg.licenses) {
|
135 |
|
136 |
|
137 | this.license = `(${_.chain(pkg.licenses)
|
138 | .map((license) => license.type || license)
|
139 | .join(" OR ")
|
140 | .value()})`;
|
141 | }
|
142 | }
|
143 |
|
144 | |
145 |
|
146 |
|
147 |
|
148 |
|
149 | text() {
|
150 | const lines = [];
|
151 | lines.push(`Name: ${this.name}`);
|
152 | lines.push(`Version: ${this.version}`);
|
153 | lines.push(`License: ${this.license}`);
|
154 | lines.push(`Private: ${this.private}`);
|
155 | if (this.description) {
|
156 | lines.push(`Description: ${this.description || false}`);
|
157 | }
|
158 | if (this.repository) {
|
159 | lines.push(`Repository: ${this.repository.url}`);
|
160 | }
|
161 | if (this.homepage) {
|
162 | lines.push(`Homepage: ${this.homepage}`);
|
163 | }
|
164 | if (this.author) {
|
165 | lines.push(`Author: ${this.author.text()}`);
|
166 | }
|
167 | if (!_.isEmpty(this.contributors)) {
|
168 | lines.push(`Contributors:`);
|
169 | const allContributors = _.chain(this.contributors)
|
170 | .map((contributor) => contributor.text())
|
171 | .map((line) => ` ${line}`)
|
172 | .value();
|
173 | lines.push(...allContributors);
|
174 | }
|
175 | if (this.licenseText) {
|
176 | lines.push("License Copyright:");
|
177 | lines.push("===");
|
178 | lines.push("");
|
179 | lines.push(this.licenseText);
|
180 | }
|
181 | return lines.join(EOL);
|
182 | }
|
183 | }
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 | function generateBlockComment(text, commentStyle) {
|
193 | const options = {
|
194 | extension: ".js",
|
195 | };
|
196 | if (commentStyle) {
|
197 | options.style = new commenting.Style(
|
198 | commentStyle.body,
|
199 | commentStyle.start,
|
200 | commentStyle.end,
|
201 | );
|
202 | }
|
203 | return commenting(text.trim(), options);
|
204 | }
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 | const PLUGIN_NAME = "rollup-plugin-license";
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 | function isString(value) {
|
219 | return _.isString(value);
|
220 | }
|
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
227 |
|
228 | function isBoolean(value) {
|
229 | return _.isBoolean(value);
|
230 | }
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 | function isFunction(value) {
|
239 | return _.isFunction(value);
|
240 | }
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 | function isNumber(value) {
|
249 | return _.isNumber(value);
|
250 | }
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 | function isNil(value) {
|
259 | return _.isNil(value);
|
260 | }
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 | function isArray(value) {
|
269 | return _.isArray(value);
|
270 | }
|
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 | function isObject(value) {
|
279 | return (
|
280 | _.isObject(value) &&
|
281 | !isArray(value) &&
|
282 | !isFunction(value) &&
|
283 | !isNil(value) &&
|
284 | !isString(value) &&
|
285 | !isNumber(value)
|
286 | );
|
287 | }
|
288 | const validators = {
|
289 | string() {
|
290 | return {
|
291 | type: "object.type.string",
|
292 | message: "must be a string",
|
293 | schema: null,
|
294 | test: isString,
|
295 | };
|
296 | },
|
297 | boolean() {
|
298 | return {
|
299 | type: "object.type.boolean",
|
300 | message: "must be a boolean",
|
301 | schema: null,
|
302 | test: isBoolean,
|
303 | };
|
304 | },
|
305 | func() {
|
306 | return {
|
307 | type: "object.type.func",
|
308 | message: "must be a function",
|
309 | schema: null,
|
310 | test: isFunction,
|
311 | };
|
312 | },
|
313 | object(schema) {
|
314 | return {
|
315 | type: "object.type.object",
|
316 | message: "must be an object",
|
317 | schema,
|
318 | test: isObject,
|
319 | };
|
320 | },
|
321 | array(schema) {
|
322 | return {
|
323 | type: "object.type.array",
|
324 | message: "must be an array",
|
325 | schema,
|
326 | test: isArray,
|
327 | };
|
328 | },
|
329 | any() {
|
330 | return {
|
331 | type: "object.any",
|
332 | message: null,
|
333 | schema: null,
|
334 | test: () => true,
|
335 | };
|
336 | },
|
337 | };
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 | function formatPath(paths) {
|
346 | let str = "";
|
347 | _.forEach(paths, (p) => {
|
348 | if (_.isNumber(p)) {
|
349 | str += `[${p}]`;
|
350 | } else if (!str) {
|
351 | str += p;
|
352 | } else {
|
353 | str += `.${p}`;
|
354 | }
|
355 | });
|
356 | return str;
|
357 | }
|
358 |
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 |
|
366 |
|
367 |
|
368 | function doItemValidation(value, schema, path) {
|
369 | const validators = _.castArray(schema);
|
370 | const matchedValidators = _.filter(validators, (validator) =>
|
371 | validator.test(value),
|
372 | );
|
373 |
|
374 |
|
375 | if (_.isEmpty(matchedValidators)) {
|
376 | return [
|
377 | {
|
378 | path,
|
379 | message: _.map(
|
380 | validators,
|
381 | (validator) => `"${formatPath(path)}" ${validator.message}`,
|
382 | ).join(" OR "),
|
383 | },
|
384 | ];
|
385 | }
|
386 |
|
387 |
|
388 | return _.chain(matchedValidators)
|
389 | .filter((validator) => validator.schema)
|
390 | .map((validator) => validate(value, validator.schema, path))
|
391 | .flatten()
|
392 | .value();
|
393 | }
|
394 |
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 |
|
403 |
|
404 | function validateObject(obj, schema, current) {
|
405 | const errors = [];
|
406 | _.forEach(obj, (value, k) => {
|
407 | if (_.isNil(value)) {
|
408 | return;
|
409 | }
|
410 | const path = [...current, k];
|
411 | if (!_.has(schema, k)) {
|
412 | errors.push({
|
413 | type: "object.allowUnknown",
|
414 | path,
|
415 | });
|
416 | } else {
|
417 | errors.push(...doItemValidation(value, schema[k], path));
|
418 | }
|
419 | });
|
420 | return errors;
|
421 | }
|
422 |
|
423 |
|
424 |
|
425 |
|
426 |
|
427 |
|
428 |
|
429 |
|
430 |
|
431 |
|
432 |
|
433 |
|
434 |
|
435 | function validateArrayItem(item, idx, schema, current) {
|
436 | const path = [...current, idx];
|
437 | if (_.isUndefined(item)) {
|
438 | return [
|
439 | {
|
440 | path,
|
441 | message: `"${formatPath(path)}" is undefined.`,
|
442 | },
|
443 | ];
|
444 | }
|
445 | if (_.isNull(item)) {
|
446 | return [
|
447 | {
|
448 | path,
|
449 | message: `"${formatPath(path)}" is null.`,
|
450 | },
|
451 | ];
|
452 | }
|
453 | return doItemValidation(item, schema, path);
|
454 | }
|
455 |
|
456 |
|
457 |
|
458 |
|
459 |
|
460 |
|
461 |
|
462 |
|
463 |
|
464 | function validateArray(array, schema, current) {
|
465 | return _.chain(array)
|
466 | .map((item, idx) => validateArrayItem(item, idx, schema, current))
|
467 | .flatten()
|
468 | .value();
|
469 | }
|
470 |
|
471 |
|
472 |
|
473 |
|
474 |
|
475 |
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 |
|
484 |
|
485 |
|
486 | function validate(obj, schema, current = []) {
|
487 | return _.isArray(obj)
|
488 | ? validateArray(obj, schema, current)
|
489 | : validateObject(obj, schema, current);
|
490 | }
|
491 |
|
492 |
|
493 |
|
494 |
|
495 |
|
496 |
|
497 |
|
498 |
|
499 |
|
500 | function validateSchema(obj, schema, current) {
|
501 | return validate(obj, schema, current);
|
502 | }
|
503 |
|
504 |
|
505 |
|
506 |
|
507 |
|
508 | const SCHEMA = {
|
509 | sourcemap: [validators.string(), validators.boolean()],
|
510 | debug: validators.boolean(),
|
511 | cwd: validators.string(),
|
512 | banner: [
|
513 | validators.func(),
|
514 | validators.string(),
|
515 | validators.object({
|
516 | commentStyle: validators.string(),
|
517 | data: validators.any(),
|
518 | content: [
|
519 | validators.func(),
|
520 | validators.string(),
|
521 | validators.object({
|
522 | file: validators.string(),
|
523 | encoding: validators.string(),
|
524 | }),
|
525 | ],
|
526 | }),
|
527 | ],
|
528 | thirdParty: [
|
529 | validators.func(),
|
530 | validators.object({
|
531 | includePrivate: validators.boolean(),
|
532 | allow: [
|
533 | validators.string(),
|
534 | validators.func(),
|
535 | validators.object({
|
536 | test: [validators.string(), validators.func()],
|
537 | failOnUnlicensed: validators.boolean(),
|
538 | failOnViolation: validators.boolean(),
|
539 | }),
|
540 | ],
|
541 | output: [
|
542 | validators.func(),
|
543 | validators.string(),
|
544 | validators.object({
|
545 | file: validators.string(),
|
546 | encoding: validators.string(),
|
547 | template: [validators.string(), validators.func()],
|
548 | }),
|
549 | validators.array([
|
550 | validators.func(),
|
551 | validators.string(),
|
552 | validators.object({
|
553 | file: validators.string(),
|
554 | encoding: validators.string(),
|
555 | template: [validators.string(), validators.func()],
|
556 | }),
|
557 | ]),
|
558 | ],
|
559 | }),
|
560 | ],
|
561 | };
|
562 |
|
563 |
|
564 |
|
565 |
|
566 |
|
567 |
|
568 |
|
569 | function warn(msg) {
|
570 | console.warn(`[${PLUGIN_NAME}] -- ${msg}`);
|
571 | }
|
572 |
|
573 |
|
574 |
|
575 |
|
576 |
|
577 |
|
578 |
|
579 | function doValidation(options) {
|
580 | return validateSchema(options, SCHEMA);
|
581 | }
|
582 |
|
583 |
|
584 |
|
585 |
|
586 |
|
587 |
|
588 |
|
589 | function validateOptions(options) {
|
590 | const errors = doValidation(options);
|
591 | if (_.isEmpty(errors)) {
|
592 | return;
|
593 | }
|
594 | const messages = [];
|
595 | _.forEach(errors, (e) => {
|
596 | if (e.type === "object.allowUnknown") {
|
597 | warn(
|
598 | `Unknown property: "${formatPath(
|
599 | e.path,
|
600 | )}", allowed options are: ${_.keys(SCHEMA).join(", ")}.`,
|
601 | );
|
602 | } else {
|
603 | messages.push(e.message);
|
604 | }
|
605 | });
|
606 | if (!_.isEmpty(messages)) {
|
607 | throw new Error(
|
608 | `[${PLUGIN_NAME}] -- Error during validation of option object: ${messages.join(
|
609 | " ; ",
|
610 | )}`,
|
611 | );
|
612 | }
|
613 | }
|
614 |
|
615 |
|
616 |
|
617 |
|
618 |
|
619 |
|
620 |
|
621 | function licensePluginOptions(options) {
|
622 | validateOptions(options);
|
623 | return options;
|
624 | }
|
625 |
|
626 |
|
627 |
|
628 |
|
629 |
|
630 |
|
631 |
|
632 |
|
633 |
|
634 | function normalizeLicense(license) {
|
635 | if (!license) {
|
636 | return "UNLICENSED";
|
637 | }
|
638 | return license.trim();
|
639 | }
|
640 |
|
641 |
|
642 |
|
643 |
|
644 |
|
645 |
|
646 |
|
647 | function checkUnlicensed(license) {
|
648 | return license.toUpperCase() === "UNLICENSED";
|
649 | }
|
650 |
|
651 |
|
652 |
|
653 |
|
654 |
|
655 |
|
656 |
|
657 | function isUnlicensed(dependency) {
|
658 | const license = normalizeLicense(dependency.license);
|
659 | return checkUnlicensed(license);
|
660 | }
|
661 |
|
662 |
|
663 |
|
664 |
|
665 |
|
666 |
|
667 |
|
668 |
|
669 | function isValid(dependency, allow) {
|
670 | const license = normalizeLicense(dependency.license);
|
671 | if (checkUnlicensed(license)) {
|
672 | return false;
|
673 | }
|
674 | return spdxExpressionValidate(license) && spdxSatisfies(license, allow);
|
675 | }
|
676 | const licenseValidator = {
|
677 | isUnlicensed,
|
678 | isValid,
|
679 | };
|
680 |
|
681 |
|
682 |
|
683 |
|
684 |
|
685 |
|
686 |
|
687 |
|
688 |
|
689 |
|
690 |
|
691 | const COMMENT_STYLES = {
|
692 | regular: {
|
693 | start: "/**",
|
694 | body: " *",
|
695 | end: " */",
|
696 | },
|
697 | ignored: {
|
698 | start: "/*!",
|
699 | body: " *",
|
700 | end: " */",
|
701 | },
|
702 | slash: {
|
703 | start: "//",
|
704 | body: "//",
|
705 | end: "//",
|
706 | },
|
707 | none: null,
|
708 | };
|
709 |
|
710 |
|
711 |
|
712 |
|
713 |
|
714 |
|
715 |
|
716 |
|
717 |
|
718 | function computeDefaultCommentStyle(text) {
|
719 | const trimmedText = text.trim();
|
720 | const start = trimmedText.slice(0, 3);
|
721 | const startWithComment = start === "/**" || start === "/*!";
|
722 | return startWithComment ? "none" : "regular";
|
723 | }
|
724 |
|
725 |
|
726 |
|
727 |
|
728 |
|
729 | class LicensePlugin {
|
730 | |
731 |
|
732 |
|
733 |
|
734 |
|
735 | constructor(options = {}) {
|
736 |
|
737 | this.name = PLUGIN_NAME;
|
738 |
|
739 |
|
740 | this._options = options;
|
741 | this._cwd = this._options.cwd || process.cwd();
|
742 | this._dependencies = {};
|
743 | this._pkg = require(path.join(this._cwd, "package.json"));
|
744 | this._debug = this._options.debug || false;
|
745 |
|
746 |
|
747 | this._sourcemap = this._options.sourcemap !== false;
|
748 |
|
749 |
|
750 |
|
751 |
|
752 | this._cache = {};
|
753 | }
|
754 |
|
755 | |
756 |
|
757 |
|
758 |
|
759 |
|
760 | disableSourceMap() {
|
761 | this._sourcemap = false;
|
762 | }
|
763 |
|
764 | |
765 |
|
766 |
|
767 |
|
768 |
|
769 |
|
770 |
|
771 |
|
772 |
|
773 |
|
774 |
|
775 |
|
776 |
|
777 | scanDependency(id) {
|
778 | if (id.startsWith("\0")) {
|
779 | id = id.replace(/^\0/, "");
|
780 | this.debug(`scanning internal module ${id}`);
|
781 | }
|
782 | if (id.indexOf("virtual:") === 0) {
|
783 | this.debug(`skipping virtual module: ${id}`);
|
784 | return;
|
785 | }
|
786 | this.debug(`scanning ${id}`);
|
787 |
|
788 |
|
789 | let dir = path.resolve(path.parse(id).dir);
|
790 | let pkg = null;
|
791 | const scannedDirs = [];
|
792 | this.debug(`iterative over directory tree, starting with: ${dir}`);
|
793 | while (dir && dir !== this._cwd && !scannedDirs.includes(dir)) {
|
794 |
|
795 | if (_.has(this._cache, dir)) {
|
796 | pkg = this._cache[dir];
|
797 | if (pkg) {
|
798 | this.debug(`found package.json in cache (package: ${pkg.name})`);
|
799 | this.addDependency(pkg);
|
800 | }
|
801 | break;
|
802 | }
|
803 | scannedDirs.push(dir);
|
804 | this.debug(`looking for package.json file in: ${dir}`);
|
805 | const pkgPath = path.join(dir, "package.json");
|
806 | const exists = fs.existsSync(pkgPath);
|
807 | if (exists) {
|
808 | this.debug(`found package.json at: ${pkgPath}, read it`);
|
809 |
|
810 |
|
811 | const pkgJson = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
812 |
|
813 |
|
814 |
|
815 | const license = pkgJson.license || pkgJson.licenses;
|
816 | const hasLicense = license && license.length > 0;
|
817 | const name = pkgJson.name;
|
818 | const version = pkgJson.version;
|
819 | const isValidPackageName = name && packageNameRegex.test(name);
|
820 | if ((isValidPackageName && version) || hasLicense) {
|
821 |
|
822 | pkg = pkgJson;
|
823 |
|
824 |
|
825 | const cwd = this._cwd || process.cwd();
|
826 | const absolutePath = path.join(
|
827 | dir,
|
828 | "[lL][iI][cC][eE][nN][cCsS][eE]*",
|
829 | );
|
830 | const relativeToCwd = path.relative(cwd, absolutePath);
|
831 | const licenseFile = this._findGlob(relativeToCwd, cwd).find(
|
832 | (file) => fs.existsSync(file) && fs.lstatSync(file).isFile(),
|
833 | );
|
834 | if (licenseFile) {
|
835 | pkg.licenseText = fs.readFileSync(licenseFile, "utf-8");
|
836 | }
|
837 |
|
838 |
|
839 | this.addDependency(pkg);
|
840 |
|
841 |
|
842 | break;
|
843 | }
|
844 | }
|
845 |
|
846 |
|
847 | dir = path.resolve(path.join(dir, ".."));
|
848 | this.debug(`going up in the directory tree: ${dir}`);
|
849 | }
|
850 |
|
851 |
|
852 | _.forEach(scannedDirs, (scannedDir) => {
|
853 | this._cache[scannedDir] = pkg;
|
854 | });
|
855 | }
|
856 |
|
857 | |
858 |
|
859 |
|
860 |
|
861 |
|
862 |
|
863 | scanDependencies(dependencies) {
|
864 | this.debug(`Scanning: ${dependencies}`);
|
865 | _.forEach(dependencies, (dependency) => {
|
866 | this.scanDependency(dependency);
|
867 | });
|
868 | }
|
869 |
|
870 | |
871 |
|
872 |
|
873 |
|
874 |
|
875 |
|
876 |
|
877 |
|
878 |
|
879 | prependBanner(code, sourcemap) {
|
880 |
|
881 |
|
882 | const magicString = new MagicString(code);
|
883 | const banner = this._options.banner;
|
884 | const content = this._readBanner(banner);
|
885 | if (content) {
|
886 | magicString.prepend(EOL);
|
887 | magicString.prepend(this._generateBanner(content, banner));
|
888 | }
|
889 | const result = {
|
890 | code: magicString.toString(),
|
891 | };
|
892 | if (this._sourcemap !== false && sourcemap !== false) {
|
893 | result.map = magicString.generateMap({
|
894 | hires: true,
|
895 | });
|
896 | }
|
897 | return result;
|
898 | }
|
899 |
|
900 | |
901 |
|
902 |
|
903 |
|
904 |
|
905 |
|
906 | addDependency(pkg) {
|
907 | var _this$_options$thirdP;
|
908 | const name = pkg.name || "";
|
909 | if (!name) {
|
910 | this.warn("Trying to add dependency without any name, skipping it.");
|
911 | return;
|
912 | }
|
913 | const version = pkg.version || "";
|
914 | const key =
|
915 | (_this$_options$thirdP = this._options.thirdParty) !== null &&
|
916 | _this$_options$thirdP !== void 0 &&
|
917 | _this$_options$thirdP.multipleVersions
|
918 | ? `${name}@${version}`
|
919 | : name;
|
920 | if (!_.has(this._dependencies, key)) {
|
921 | this._dependencies[key] = new Dependency(pkg);
|
922 | }
|
923 | }
|
924 |
|
925 | |
926 |
|
927 |
|
928 |
|
929 |
|
930 |
|
931 |
|
932 | scanThirdParties() {
|
933 | const thirdParty = this._options.thirdParty;
|
934 | if (!thirdParty) {
|
935 | return;
|
936 | }
|
937 | const includePrivate = thirdParty.includePrivate || false;
|
938 | const outputDependencies = _.chain(this._dependencies)
|
939 | .values()
|
940 | .filter((dependency) => includePrivate || !dependency.private)
|
941 | .value();
|
942 | if (_.isFunction(thirdParty)) {
|
943 | thirdParty(outputDependencies);
|
944 | return;
|
945 | }
|
946 | const allow = thirdParty.allow;
|
947 | if (allow) {
|
948 | this._scanLicenseViolations(outputDependencies, allow);
|
949 | }
|
950 | const output = thirdParty.output;
|
951 | if (output) {
|
952 | this._exportThirdParties(outputDependencies, output);
|
953 | }
|
954 | }
|
955 |
|
956 | |
957 |
|
958 |
|
959 |
|
960 |
|
961 |
|
962 | debug(msg) {
|
963 | if (this._debug) {
|
964 | console.debug(`[${this.name}] -- ${msg}`);
|
965 | }
|
966 | }
|
967 |
|
968 | |
969 |
|
970 |
|
971 |
|
972 |
|
973 |
|
974 | warn(msg) {
|
975 | console.warn(`[${this.name}] -- ${msg}`);
|
976 | }
|
977 |
|
978 | |
979 |
|
980 |
|
981 |
|
982 |
|
983 |
|
984 |
|
985 |
|
986 | _findGlob(pattern, cwd) {
|
987 | return glob.sync(pattern, {
|
988 | cwd,
|
989 | });
|
990 | }
|
991 |
|
992 | |
993 |
|
994 |
|
995 |
|
996 |
|
997 |
|
998 |
|
999 | _readBanner(banner) {
|
1000 | if (_.isNil(banner)) {
|
1001 | return null;
|
1002 | }
|
1003 |
|
1004 |
|
1005 | if (_.isString(banner)) {
|
1006 | this.debug("prepend banner from raw string");
|
1007 | return banner;
|
1008 | }
|
1009 |
|
1010 |
|
1011 | const content = _.result(banner, "content");
|
1012 |
|
1013 |
|
1014 | if (_.isString(content)) {
|
1015 | this.debug("prepend banner from content raw string");
|
1016 | return content;
|
1017 | }
|
1018 |
|
1019 |
|
1020 | if (!_.has(content, "file")) {
|
1021 | throw new Error(
|
1022 | `[${this.name}] -- Cannot find banner content, please specify an inline content, or a path to a file`,
|
1023 | );
|
1024 | }
|
1025 | const file = content.file;
|
1026 | const encoding = content.encoding || "utf-8";
|
1027 | this.debug(`prepend banner from file: ${file}`);
|
1028 | this.debug(`use encoding: ${encoding}`);
|
1029 | const filePath = path.resolve(file);
|
1030 | const exists = fs.existsSync(filePath);
|
1031 |
|
1032 |
|
1033 | if (!exists) {
|
1034 | throw new Error(
|
1035 | `[${this.name}] -- Template file ${filePath} does not exist, or cannot be read`,
|
1036 | );
|
1037 | }
|
1038 | return fs.readFileSync(filePath, encoding);
|
1039 | }
|
1040 |
|
1041 | |
1042 |
|
1043 |
|
1044 |
|
1045 |
|
1046 |
|
1047 |
|
1048 |
|
1049 |
|
1050 |
|
1051 |
|
1052 | _generateBanner(content, banner) {
|
1053 |
|
1054 | const tmpl = _.template(content);
|
1055 |
|
1056 |
|
1057 | const pkg = this._pkg;
|
1058 | const dependencies = _.values(this._dependencies);
|
1059 | const data = banner.data ? _.result(banner, "data") : {};
|
1060 | const text = tmpl({
|
1061 | _,
|
1062 | moment,
|
1063 | pkg,
|
1064 | dependencies,
|
1065 | data,
|
1066 | });
|
1067 |
|
1068 |
|
1069 | const style = _.has(banner, "commentStyle")
|
1070 | ? banner.commentStyle
|
1071 | : computeDefaultCommentStyle(text);
|
1072 |
|
1073 |
|
1074 | if (!_.has(COMMENT_STYLES, style)) {
|
1075 | throw new Error(
|
1076 | `Unknown comment style ${style}, please use one of: ${_.keys(
|
1077 | COMMENT_STYLES,
|
1078 | )}`,
|
1079 | );
|
1080 | }
|
1081 | this.debug(`generate banner using comment style: ${style}`);
|
1082 | return COMMENT_STYLES[style]
|
1083 | ? generateBlockComment(text, COMMENT_STYLES[style])
|
1084 | : text;
|
1085 | }
|
1086 |
|
1087 | |
1088 |
|
1089 |
|
1090 |
|
1091 |
|
1092 |
|
1093 |
|
1094 | _scanLicenseViolations(outputDependencies, allow) {
|
1095 | _.forEach(outputDependencies, (dependency) => {
|
1096 | this._scanLicenseViolation(dependency, allow);
|
1097 | });
|
1098 | }
|
1099 |
|
1100 | |
1101 |
|
1102 |
|
1103 |
|
1104 |
|
1105 |
|
1106 |
|
1107 | _scanLicenseViolation(dependency, allow) {
|
1108 | const testFn =
|
1109 | _.isString(allow) || _.isFunction(allow) ? allow : allow.test;
|
1110 | const isValid = _.isFunction(testFn)
|
1111 | ? testFn(dependency)
|
1112 | : licenseValidator.isValid(dependency, testFn);
|
1113 | if (!isValid) {
|
1114 | const failOnUnlicensed = allow.failOnUnlicensed === true;
|
1115 | const failOnViolation = allow.failOnViolation === true;
|
1116 | this._handleInvalidLicense(dependency, failOnUnlicensed, failOnViolation);
|
1117 | }
|
1118 | }
|
1119 |
|
1120 | |
1121 |
|
1122 |
|
1123 |
|
1124 |
|
1125 |
|
1126 |
|
1127 |
|
1128 |
|
1129 |
|
1130 | _handleInvalidLicense(dependency, failOnUnlicensed, failOnViolation) {
|
1131 | if (licenseValidator.isUnlicensed(dependency)) {
|
1132 | this._handleUnlicensedDependency(dependency, failOnUnlicensed);
|
1133 | } else {
|
1134 | this._handleLicenseViolation(dependency, failOnViolation);
|
1135 | }
|
1136 | }
|
1137 |
|
1138 | |
1139 |
|
1140 |
|
1141 |
|
1142 |
|
1143 |
|
1144 |
|
1145 |
|
1146 | _handleUnlicensedDependency(dependency, fail) {
|
1147 | const message = `Dependency "${dependency.name}" does not specify any license.`;
|
1148 | if (!fail) {
|
1149 | this.warn(message);
|
1150 | } else {
|
1151 | throw new Error(message);
|
1152 | }
|
1153 | }
|
1154 |
|
1155 | |
1156 |
|
1157 |
|
1158 |
|
1159 |
|
1160 |
|
1161 |
|
1162 | _handleLicenseViolation(dependency, fail) {
|
1163 | const message =
|
1164 | `Dependency "${dependency.name}" has a license (${dependency.license}) which is not compatible with ` +
|
1165 | `requirement, looks like a license violation to fix.`;
|
1166 | if (!fail) {
|
1167 | this.warn(message);
|
1168 | } else {
|
1169 | throw new Error(message);
|
1170 | }
|
1171 | }
|
1172 |
|
1173 | |
1174 |
|
1175 |
|
1176 |
|
1177 |
|
1178 |
|
1179 |
|
1180 |
|
1181 | _exportThirdParties(outputDependencies, outputs) {
|
1182 | _.forEach(_.castArray(outputs), (output) => {
|
1183 | this._exportThirdPartiesToOutput(outputDependencies, output);
|
1184 | });
|
1185 | }
|
1186 |
|
1187 | |
1188 |
|
1189 |
|
1190 |
|
1191 |
|
1192 |
|
1193 |
|
1194 |
|
1195 | _exportThirdPartiesToOutput(outputDependencies, output) {
|
1196 | if (_.isFunction(output)) {
|
1197 | output(outputDependencies);
|
1198 | return;
|
1199 | }
|
1200 |
|
1201 |
|
1202 |
|
1203 |
|
1204 | const template = _.isString(output.template)
|
1205 | ? (dependencies) =>
|
1206 | _.template(output.template)({
|
1207 | dependencies,
|
1208 | _,
|
1209 | moment,
|
1210 | })
|
1211 | : output.template;
|
1212 | const defaultTemplate = (dependencies) =>
|
1213 | _.isEmpty(dependencies)
|
1214 | ? "No third parties dependencies"
|
1215 | : _.map(dependencies, (d) => d.text()).join(
|
1216 | `${EOL}${EOL}---${EOL}${EOL}`,
|
1217 | );
|
1218 | const text = _.isFunction(template)
|
1219 | ? template(outputDependencies)
|
1220 | : defaultTemplate(outputDependencies);
|
1221 | const isOutputFile = _.isString(output);
|
1222 | const file = isOutputFile ? output : output.file;
|
1223 | const encoding = isOutputFile ? "utf-8" : output.encoding || "utf-8";
|
1224 | this.debug(`exporting third-party summary to ${file}`);
|
1225 | this.debug(`use encoding: ${encoding}`);
|
1226 |
|
1227 |
|
1228 | mkdirp.mkdirp.sync(path.parse(file).dir);
|
1229 | fs.writeFileSync(file, (text || "").trim(), {
|
1230 | encoding,
|
1231 | });
|
1232 | }
|
1233 | }
|
1234 |
|
1235 |
|
1236 |
|
1237 |
|
1238 |
|
1239 |
|
1240 |
|
1241 |
|
1242 | function licensePlugin(options) {
|
1243 | return new LicensePlugin(licensePluginOptions(options));
|
1244 | }
|
1245 |
|
1246 |
|
1247 |
|
1248 |
|
1249 |
|
1250 |
|
1251 |
|
1252 | function rollupPluginLicense(options = {}) {
|
1253 | const plugin = licensePlugin(options);
|
1254 | return {
|
1255 | |
1256 |
|
1257 |
|
1258 |
|
1259 | name: plugin.name,
|
1260 | |
1261 |
|
1262 |
|
1263 |
|
1264 |
|
1265 |
|
1266 |
|
1267 |
|
1268 |
|
1269 | renderChunk(code, chunk, outputOptions = {}) {
|
1270 | plugin.scanDependencies(
|
1271 | _.chain(chunk.modules)
|
1272 | .toPairs()
|
1273 | .reject((mod) => mod[1].isAsset)
|
1274 | .filter((mod) => mod[1].renderedLength > 0)
|
1275 | .map((mod) => mod[0])
|
1276 | .value(),
|
1277 | );
|
1278 | return plugin.prependBanner(code, outputOptions.sourcemap !== false);
|
1279 | },
|
1280 | |
1281 |
|
1282 |
|
1283 |
|
1284 |
|
1285 |
|
1286 |
|
1287 | generateBundle() {
|
1288 | plugin.scanThirdParties();
|
1289 | },
|
1290 | };
|
1291 | }
|
1292 |
|
1293 | module.exports = rollupPluginLicense;
|