kibana/packages/kbn-eslint-plugin-eslint/rules/require_license_header.js

120 lines
3.7 KiB
JavaScript
Raw Normal View History

/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
2018-04-20 21:13:37 +02:00
const babelEslint = require('babel-eslint');
const { assert, normalizeWhitespace, init } = require('../lib');
function isHashbang(text) {
return text.trim().startsWith('#!') && !text.trim().includes('\n');
}
2018-04-20 21:13:37 +02:00
module.exports = {
meta: {
fixable: 'code',
schema: [
{
type: 'object',
properties: {
license: {
type: 'string',
},
2018-04-20 21:13:37 +02:00
},
additionalProperties: false,
2018-04-20 21:13:37 +02:00
},
],
2018-04-20 21:13:37 +02:00
},
create: context => {
return {
Program(program) {
const license = init(context, program, function() {
2018-04-20 21:13:37 +02:00
const options = context.options[0] || {};
const license = options.license;
assert(!!license, '"license" option is required');
const parsed = babelEslint.parse(license);
assert(!parsed.body.length, '"license" option must only include a single comment');
assert(
parsed.comments.length === 1,
'"license" option must only include a single comment'
);
2018-04-20 21:13:37 +02:00
return {
source: license,
nodeValue: normalizeWhitespace(parsed.comments[0].value),
2018-04-20 21:13:37 +02:00
};
});
if (!license) {
return;
}
const sourceCode = context.getSourceCode();
const comment = sourceCode
.getAllComments()
.find(node => normalizeWhitespace(node.value) === license.nodeValue);
2018-04-20 21:13:37 +02:00
// no licence comment
if (!comment) {
context.report({
message: 'File must start with a license header',
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: sourceCode.lines[0].length - 1 },
2018-04-20 21:13:37 +02:00
},
fix(fixer) {
if (isHashbang(sourceCode.lines[0])) {
return undefined;
}
2018-04-20 21:13:37 +02:00
return fixer.replaceTextRange([0, 0], license.source + '\n\n');
},
2018-04-20 21:13:37 +02:00
});
return;
}
// ensure there is nothing before the comment
const sourceBeforeNode = sourceCode
.getText()
.slice(0, sourceCode.getIndexFromLoc(comment.loc.start));
if (sourceBeforeNode.length && !isHashbang(sourceBeforeNode)) {
2018-04-20 21:13:37 +02:00
context.report({
node: comment,
message: 'License header must be at the very beginning of the file',
fix(fixer) {
// replace leading whitespace if possible
if (sourceBeforeNode.trim() === '') {
return fixer.replaceTextRange([0, sourceBeforeNode.length], '');
}
// inject content at top and remove node from current location
// if removing whitespace is not possible
return [
fixer.remove(comment),
fixer.replaceTextRange([0, 0], license.source + '\n\n'),
];
},
2018-04-20 21:13:37 +02:00
});
}
},
};
},
2018-04-20 21:13:37 +02:00
};