Skip to content

Commit

Permalink
Add file license header script (#2472)
Browse files Browse the repository at this point in the history
* Use new file license header

Signed-off-by: Chris Gervang <chris@gervang.com>

* Use header file in modules

Signed-off-by: Chris Gervang <chris@gervang.com>

* fix path

Signed-off-by: Chris Gervang <chris@gervang.com>

* copy over the license script

Signed-off-by: Chris Gervang <chris@gervang.com>

* remove copyright trim

Signed-off-by: Chris Gervang <chris@gervang.com>

* remove uber-licence dep

Signed-off-by: Chris Gervang <chris@gervang.com>

* Find ts, tsx files. Ignore dist folder

Signed-off-by: Chris Gervang <chris@gervang.com>

* modules

Signed-off-by: Chris Gervang <chris@gervang.com>

* fix ES modules

Signed-off-by: Chris Gervang <chris@gervang.com>

* more discoverable file header

Signed-off-by: Chris Gervang <chris@gervang.com>

---------

Signed-off-by: Chris Gervang <chris@gervang.com>
  • Loading branch information
chrisgervang committed Dec 19, 2023
1 parent f33b09f commit 5092486
Show file tree
Hide file tree
Showing 23 changed files with 374 additions and 298 deletions.
2 changes: 2 additions & 0 deletions FILE-HEADER
@@ -0,0 +1,2 @@
SPDX-License-Identifier: MIT
Copyright contributors to the kepler.gl project
12 changes: 7 additions & 5 deletions package.json
Expand Up @@ -76,9 +76,9 @@
"build:types": "tsc --project tsconfig.production.json",
"analyze": "yarn analyze:bundle",
"analyze:bundle": "NODE_OPTIONS=--openssl-legacy-provider webpack --config ./webpack/bundle.js --progress --env.prod",
"check-licence": "uber-licence --dry",
"add-licence": "uber-licence",
"prepublish": "yarn fix-dependencies && yarn workspaces run stab && yarn workspaces run prepublish && uber-licence && yarn build:umd && yarn build:types",
"check-licence": "babel-node ./scripts/license-header/bin --license ./FILE-HEADER --dry",
"add-licence": "babel-node ./scripts/license-header/bin --license ./FILE-HEADER",
"prepublish": "yarn fix-dependencies && yarn workspaces run stab && yarn workspaces run prepublish && yarn add-licence && yarn build:umd && yarn build:types",
"docs": "babel-node ./scripts/documentation.js",
"typedoc": "typedoc --theme markdown --out typedoc --inputFiles ./src/reducers --inputFiles ./src/actions --excludeExternals --excludeNotExported --excludePrivate",
"example-version": "babel-node ./scripts/edit-version.js",
Expand Down Expand Up @@ -179,17 +179,20 @@
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"gl": "^6.0.2",
"global": "^4.4.0",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"jsdom": "^16.4.0",
"json-loader": "^0.5.4",
"maplibre-gl": "^3.6.2",
"minimist": "^1.1.0",
"nyc": "^15.1.0",
"prettier": "1.19.1",
"progress-bar-webpack-plugin": "^2.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-loader": "^4.13.0",
"readdirp": "^2.1.0",
"redux-mock-store": "^1.2.1",
"sinon": "^2.4.1",
"sinon-stub-promise": "^4.0.0",
Expand All @@ -203,8 +206,7 @@
"tape": "^4.9.2",
"tape-catch": "^1.0.6",
"typedoc-plugin-markdown": "^3.0.11",
"typescript": "4.5.5",
"uber-licence": "^3.1.1",
"typescript": "4.5.5",
"url-loader": "^4.1.1",
"watchify": "^3.6.1",
"webpack": "^4.29.0",
Expand Down
44 changes: 44 additions & 0 deletions scripts/license-header/README.md
@@ -0,0 +1,44 @@
Utility to add license header to your files.

Forked from [uber-licence](https://github.com/uber/uber-licence).

Running the `license-header` binary adds licencing information to every javascript file in your project.

You can run `license-header --dry` where it does not mutate any files and instead outputs the number of files it would change.

You can use `--file` and `--dir` to specify your own file and directory filters to select source files to consider.

## Recommended usage

```
// package.json
{
"scripts": {
"check-license": "license-header --dry",
"add-license": "license-header"
},
"devDependencies": {
"minimist": "^1.1.0",
"readdirp": "^2.1.0",
"pre-commit": "0.0.9"
},
"pre-commit": [
"test",
"check-license"
],
"pre-commit.silent": true
}
```

Add missing headers.

```bash
yarn run babel-node ./scripts/license-header/bin
```

Migrate to a new header.

```bash
yarn run babel-node ./scripts/license-header/bin --license ./scripts/license-header/FILE-HEADER --legacy ./LICENSE
```

127 changes: 127 additions & 0 deletions scripts/license-header/bin.mjs
@@ -0,0 +1,127 @@
// Forked from uber-licence, MIT

import readdirp from 'readdirp';
import minimist from 'minimist';
import {readFileSync} from 'fs';
import process from 'global/process.js';
import console from 'global/console.js';

import LicenseFixer from './license-fixer.mjs';

var argv = minimist(process.argv.slice(2));
var cwd = process.cwd();

/*eslint no-process-exit: 0, no-console: 0*/
// jscs:disable maximumLineLength
if (argv.help || argv.h) {
console.log('license-header');
console.log(' ');
console.log(' This binary will add a license to the top');
console.log(' of all your files');
console.log('');
console.log(' Options:');
console.log(' --dry does not write to files');
console.log(' --file pattern of files to modify');
console.log(' --dir pattern for directories containing files');
console.log(' --license intended license (file)');
console.log(' --legacy licenses to replace');
console.log(' --verbose log skipped and empty files');
console.log(' --silent do not log fixed files');
process.exit(0);
}

var fileFilter = ['*.js', '*.ts', '*.tsx'];
if (typeof argv.file === 'string') {
fileFilter = [argv.file];
} else if (Array.isArray(argv.file)) {
fileFilter = argv.file;
}

var directoryFilter = ['!.git', '!node_modules', '!coverage', '!env', '!.tox', '!vendor', '!Godeps', '!dist'];
if (typeof argv.dir === 'string') {
directoryFilter = [argv.dir];
} else if (Array.isArray(argv.dir)) {
directoryFilter = argv.dir;
}

var licenses = null;

if (typeof argv.license === 'string') {
licenses = licenses || [];
licenses.push(argv.license);
} else if (Array.isArray(argv.license)) {
licenses = licenses || [];
Array.prototype.push.apply(licenses, argv.license);
}

if (typeof argv.legacy === 'string') {
licenses = licenses || [];
licenses.push(argv.legacy);
} else if (Array.isArray(argv.legacy)) {
licenses = licenses || [];
Array.prototype.push.apply(licenses, argv.legacy);
}

if (licenses) {
for (var i = 0; i < licenses.length; i++) {
// Replace file names with content of files
licenses[i] = readFileSync(licenses[i], 'utf8');
}
} else {
console.error('no license provided');
process.exit(1);
}

var licenseFixer = new LicenseFixer({
dry: argv.dry,
silent: argv.silent,
verbose: argv.verbose
});

// Set the intended license text
licenseFixer.setLicense(licenses[0]);
// Add a license to match and replace.
// There can be multiple recognized licenses, for migration purposes.
for (var i = 0; i < licenses.length; i++) {
licenseFixer.addLicense(licenses[i]);
}

readTree({
root: cwd,
fileFilter: fileFilter,
directoryFilter: directoryFilter
}, processFiles);

function readTree(options, callback) {
var stream = readdirp(options);
var files = [];
stream.on('data', onData);
stream.on('end', onEnd);
stream.on('error', onEnd);
function onData(event) {
files.push(event.path);
}
function onEnd(err) {
callback(err, files);
}
}

function processFiles(err, files) {
if (err) {
console.error(err.message);
process.exit(1);
return;
}

var fixed = 0;
for (var filesIndex = 0; filesIndex < files.length; filesIndex++) {
var file = files[filesIndex];
fixed = fixed + licenseFixer.fixFile(file);
}

if (argv.dry) {
process.exit(fixed);
} else {
process.exit(0);
}
}
163 changes: 163 additions & 0 deletions scripts/license-header/license-fixer.mjs
@@ -0,0 +1,163 @@
// Forked from uber-licence, MIT

import {readFileSync, writeFileSync} from 'fs';
import console from 'global/console.js';

function LicenseFixer(options) {
options = options || {};
this.slashLicense = null;
this.hashLicense = null;
this.licenseExpressions = [];
this.dry = options.dry || false;
this.silent = options.silent || false;
this.verbose = options.verbose || false;
Object.seal(this);
}

LicenseFixer.prototype.addLicense = function addLicense(license) {
this.licenseExpressions.push(createLicenseExpression(license));
};

function createLicenseExpression(license) {
license = license.trim();
// Transform the license into a regular expression that matches the exact
// license as well as similar licenses, with different dates and line
// wraps.
var pattern = license.split(/\s+/).map(relaxLicenseTerm).join('') + '\\s*';
return new RegExp(pattern, 'gmi');
}

function relaxLicenseTerm(term) {
// There has been at least one occasion where someone replaced all single
// quotes with double quotes throughout a file and got an extra license.
return '\\s*((//|#)\\s*)*' + // wrap around any comment or spacing
regexpEscape(term)
.replace(/\d{4}/g, '\\d{4}') // dates to date patterns
.replace(/['"]/g, '[\'"]'); // relax quotes
}

var regexpEscapePattern = /[|\\{}()[\]^$+*?.]/g;

function regexpEscape(string) {
return string.replace(regexpEscapePattern, '\\$&');
}

LicenseFixer.prototype.setLicense = function setLicense(license) {
this.slashLicense = createSlashLicense(license);
this.hashLicense = createHashLicense(license);
};

function createSlashLicense(license) {
return license.trim().split('\n').map(slashPrefix).join('');
}

function createHashLicense(license) {
return license.trim().split('\n').map(hashPrefix).join('');
}

function slashPrefix(line) {
return ('// ' + line).trim() + '\n';
}

function hashPrefix(line) {
return ('# ' + line).trim() + '\n';
}

LicenseFixer.prototype.getLicenseForFile = function getLicenseForFile(file) {
if (file.match(/\.(js|go|java|ts|tsx)$/)) {
return this.slashLicense;
} else if (file.match(/\.(pyx?|pxd)$/)) {
return this.hashLicense;
}
return null;
};

LicenseFixer.prototype.fixContent = function fixContent(file, content) {
var preamble = '';
// Check for shebang
var foundShebang = content.match(/^#!|#\s*(en)?coding=/m);
if (foundShebang) {
var shebangIndex = content.indexOf('\n');
if (shebangIndex >= 0) {
preamble += content.slice(0, shebangIndex + 1);
content = content.slice(shebangIndex + 1).trim() + '\n';
}
}

// check for @flow header
var foundFlowHeader = content.match(/^\/\/ @flow|^\/\* @flow \*\//m);
if (foundFlowHeader) {
var flowIndex = content.indexOf('\n');
if (flowIndex >= 0) {
preamble += content.slice(0, flowIndex + 1);
content = content.slice(flowIndex + 1).trim() + '\n';
}
}

if (foundShebang || foundFlowHeader) {
preamble += '\n';
}

// Remove old licenses
for (var i = 0; i < this.licenseExpressions.length; i++) {
// string replace hangs in some pathelogical cases of repeated licenses
var match = this.licenseExpressions[i].exec(content);
while (match) {
content = content.slice(0, match.index) + content.slice(match.index + match[0].length);
match = this.licenseExpressions[i].exec(content);
}
}

var license = this.getLicenseForFile(file);
if (license === null) {
if (!this.silent) {
console.error(`unrecognized file type ${file}`);
}
return null;
}

// Reintroduce the preamble and license
content = preamble + license + '\n' + content;

return content;
};

LicenseFixer.prototype.fixFile = function fixFile(file) {
var original = readFileSync(file, 'utf8');

if (original.length === 0) {
// Ignore empty files
if (this.verbose) {
console.log(`empty ${file}`);
}
return false;
}

var content = this.fixContent(file, original);

if (content === null) {
// Return true on error so dry run fails
return true;
}

if (original === content) {
// No change
if (this.verbose) {
console.log(`skip ${file}`);
}
return false;
}

if (!this.silent) {
console.log(`fix ${file}`);
}

if (this.dry) {
return true;
}

writeFileSync(file, content, 'utf8');
return true;
};

export default LicenseFixer
2 changes: 1 addition & 1 deletion src/actions/package.json
Expand Up @@ -22,7 +22,7 @@
"build": "rm -fr dist && babel src --out-dir dist --source-maps inline --extensions '.ts,.tsx,.js,.jsx' --ignore '**/*.d.ts'",
"build:umd": "NODE_OPTIONS=--openssl-legacy-provider webpack --config ./webpack/umd.js --progress --env.prod",
"build:types": "tsc --project ./tsconfig.production.json",
"prepublish": "uber-licence && yarn build && yarn build:types",
"prepublish": "babel-node ../../scripts/license-header/bin --license ../../FILE-HEADER && yarn build && yarn build:types",
"stab": "mkdir -p dist && touch dist/index.js"
},
"files": [
Expand Down

0 comments on commit 5092486

Please sign in to comment.