Files
openstapps/configuration/eslint-config/copyright-header-rule.js

115 lines
2.8 KiB
JavaScript

const fs = require("fs");
const path = require("node:path");
const child_process = require("child_process");
/** @type {import('eslint').Rule.RuleModule} */
const copyrightHeaderRule = {
meta: {
schema: [{
type: 'object',
properties: {
author: {type: 'string'},
license: {type: 'string'},
fixedDate: {type: 'string'},
},
required: ['author', 'license']
}],
type: 'problem',
docs: {
description: 'Files should start with a copyright header'
},
messages: {
missing: 'Missing a copyright header at the start of the file',
invalid: 'Invalid copyright header',
tryThisHeader: 'Use this copyright header:\n\n/*{{expected}}*/'
},
fixable: "code",
hasSuggestions: true,
},
create: function(context) {
const code = context.getSourceCode()
const {author, license, fixedDate} = context.options[0]
const year = fixedDate ? new Date(fixedDate) : getAuthorDate(context.getPhysicalFilename())
if (!year) return {}
const expected = license
.replace('{{year}}', year.getFullYear().toString())
.replace('{{author}}', author)
const comment = code.getAllComments().find(it => it.type === 'Block')
if (!comment) {
context.report({
loc: {
line: 0,
column: 0,
},
messageId: 'missing',
suggest: [
{
messageId: 'tryThisHeader',
data: {expected},
fix(fixer) {
return fixer.insertTextBeforeRange([0, 0], `/*${expected}*/\n`)
}
}
]
})
return {}
}
if (comment.value !== expected && comment.loc && comment.range) {
const range = comment.range
context.report({
loc: comment.loc,
messageId: 'invalid',
suggest: [
{
messageId: 'tryThisHeader',
data: {expected},
fix(fixer) {
return fixer.replaceTextRange(range, `/*${expected}*/`)
}
}
]
})
}
return {}
}
}
/**
* Retrieves the last edited date of a file
*
* Uses git history if available, last modified date otherwise.
*
* @param filePath {string}
* @return {Date | undefined}
*/
function getAuthorDate(filePath) {
// just to be on the safe side
const sanitizedPath = path.resolve(filePath)
try {
const result = child_process.execSync(`git log -1 --pretty="format:%ad" "${sanitizedPath}"`, {
cwd: path.dirname(sanitizedPath),
stdio: 'pipe',
})
const date = new Date(result.toString())
if (!Number.isNaN(date.getTime())) {
return date
}
} catch {}
try {
const stats = fs.statSync(sanitizedPath)
const date = new Date(stats.mtime)
return Number.isNaN(date.getTime()) ? undefined : date
} catch {}
return undefined
}
module.exports = copyrightHeaderRule