-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0b290fa
Showing
11 changed files
with
770 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"deno.enable": true, | ||
"deno.lint": true, | ||
"deno.unstable": true, | ||
"deno.suggest.imports.hosts": { | ||
"https://deno.land": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# The MIT License (MIT) | ||
|
||
## Copyright © 2021 Im-Beast | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the “Software”), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
the Software, and to permit persons to whom the Software is furnished to do so, | ||
subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# 📝 Anzu | ||
Anzu is very light CLI tool for checking whether files have license header | ||
|
||
![Anzu help output](./docs/help.png) | ||
![Anzu example output](./docs/example-output.png) | ||
|
||
## Installation | ||
- Without explicit permissions: | ||
- `deno install https://deno.land/[email protected]/src/cli.ts -n anzu` | ||
- With explicit permissions: | ||
- `deno install --allow-read --allow-write --allow-net https://deno.land/[email protected]/src/cli.ts -n anzu` | ||
|
||
|
||
## 📚 Why not [deno_license_checker](https://github.com/kt3k/deno_license_checker) | ||
I got discouraged from deno_license_checker because of several things, majorly: | ||
- Its size – 177KB | ||
- Performance | ||
- Requirement of config file | ||
- Doesn't ask for permissions – they have to be specified otherwise it'll exit | ||
|
||
Anzu solves some of these problems: | ||
- It's significantly smaller – 27KB | ||
- In my case its multiple times faster (benchmarks needed) | ||
- Every option has to be set in CLI | ||
- If you want to launch same command using one command – just create bash script that does that (see [here](./find-license.sh)) | ||
- If permissions aren't specified it automatically requests you for them | ||
|
||
Why not Anzu? | ||
- It's not compatible with windows (Im 99.9% sure, but didn't tested it) | ||
- If you prefer having external config file Anzu probably isn't for you | ||
|
||
What can Anzu also do? | ||
- Additionally it can load license template from given URL and Path | ||
- It can search for license using RegExp | ||
- Exclude files and directories using regexp | ||
- You can see full functionality using `-h` option. | ||
|
||
## 🤝 Contributing | ||
|
||
I'm open to any idea and criticism. Feel free to add any commits, issues and | ||
pull requests! | ||
|
||
## 📝 Licensing | ||
|
||
This project is available under MIT License conditions. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Order of options doesn't matter – they get sorted automatically by priority | ||
# Check licenses in ./ that match /.+\.ts/ js regexp | ||
# Ignore files that match "deps.ts" regexp pattern | ||
# Look for license with this pattern (when license is regexp it cannot be prepended!) | ||
# Prepend license to the top of the file when | ||
|
||
deno run ./src/cli.ts \ | ||
-i ./ "/.+\.ts/" \ | ||
-e "deps.ts" \ | ||
-l "// Copyright 2021 Im-Beast. All rights reserved. MIT license." \ | ||
-p | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// Copyright 2021 Im-Beast. All rights reserved. MIT license. | ||
export * from "./src/cli.ts"; | ||
export * from "./src/deps.ts"; | ||
export * from "./src/license_check.ts"; | ||
export * from "./src/parse_args.ts"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,278 @@ | ||
// Copyright 2021 Im-Beast. All rights reserved. MIT license. | ||
import { | ||
bold, | ||
cyan, | ||
gray, | ||
green, | ||
italic, | ||
magenta, | ||
red, | ||
yellow, | ||
} from "./deps.ts"; | ||
import { | ||
checkDirectoryForLicenses, | ||
compileLicense, | ||
compileStringRegexp, | ||
PrependLicense, | ||
} from "./license_check.ts"; | ||
import { parseArgs } from "./parse_args.ts"; | ||
|
||
type Option = | ||
& { | ||
message: string; | ||
value?: string[]; | ||
priority: number; | ||
} | ||
& ({ | ||
args?: string[]; | ||
func?: (...args: string[]) => void; | ||
} | { | ||
args?: never; | ||
func?: never; | ||
}); | ||
|
||
interface Options { | ||
[name: string]: Option; | ||
} | ||
|
||
const optionLinks = new Map<string, Option>(); | ||
|
||
const options: Options = { | ||
help: { | ||
message: "Shows this message", | ||
func() { | ||
console.log(options.help.message); | ||
Deno.exit(0); | ||
}, | ||
priority: 3, | ||
}, | ||
license: { | ||
message: "License text", | ||
args: ["path|string|regexp|url"], | ||
priority: 2, | ||
}, | ||
prepend: { | ||
message: "Prepend license header to files missing one", | ||
args: ["?partial <false>"], | ||
priority: 2, | ||
}, | ||
normalizeNewlines: { | ||
message: "converts \\n to newline in license field", | ||
priority: 2, | ||
}, | ||
quiet: { | ||
message: "Do not print anything except errors", | ||
priority: 2, | ||
}, | ||
throwErrors: { | ||
message: "Program will throw on errors instead of exiting", | ||
priority: 2, | ||
}, | ||
exclude: { | ||
message: "Exclude given regexps", | ||
args: ["file-regexp", "?dir-regexp"], | ||
priority: 2, | ||
}, | ||
input: { | ||
message: "Directory that will be searched using regexp pattern", | ||
args: ["path", "file-regexp", "?dir-regexp </.+/>"], | ||
async func( | ||
path: string, | ||
$fileRegexp: string, | ||
$dirRegexp = "^(?!node_modules).+", | ||
) { | ||
if (!options.license.value?.length) { | ||
cliError( | ||
`License pattern is missing, set it using ${ | ||
styleArg("--license") | ||
} option`, | ||
); | ||
Deno.exit(1); | ||
} | ||
|
||
let prepend: PrependLicense = PrependLicense.Never; | ||
|
||
if (options.prepend.value) { | ||
prepend = PrependLicense.FullyMissing; | ||
|
||
if (options.prepend.value.length) { | ||
try { | ||
if (JSON.parse(options.prepend.value[0])) { | ||
prepend = PrependLicense.PartialOrFullyMissing; | ||
} | ||
} catch (error) { | ||
cliError( | ||
`Failed parsing argument of ${ | ||
styleArg("-p|--prepend") | ||
} option ${error.message}`, | ||
error, | ||
); | ||
Deno.exit(1); | ||
} | ||
} | ||
} | ||
|
||
let normalizeNewlines = false; | ||
if (options.normalizeNewlines.value) { | ||
normalizeNewlines = true; | ||
} | ||
|
||
const license = await compileLicense( | ||
options.license.value[0], | ||
normalizeNewlines, | ||
); | ||
if (!license) { | ||
cliError(`Given ${styleArg("--license")} argument is invalid!`); | ||
Deno.exit(1); | ||
} | ||
|
||
try { | ||
let excludeFileRegexp: RegExp | undefined = undefined; | ||
let excludeDirRegexp: RegExp | undefined = undefined; | ||
|
||
if (options.exclude?.value?.length) { | ||
excludeFileRegexp = compileStringRegexp(options.exclude.value[0]) || | ||
new RegExp(options.exclude.value[0]); | ||
if (options.exclude?.value?.length > 1) { | ||
excludeDirRegexp = compileStringRegexp(options.exclude.value[1]) || | ||
new RegExp(options.exclude.value[1]); | ||
} | ||
} | ||
|
||
const fileRegexp = compileStringRegexp($fileRegexp) || | ||
new RegExp($fileRegexp); | ||
const dirRegexp = compileStringRegexp($dirRegexp) || | ||
new RegExp($dirRegexp); | ||
|
||
try { | ||
await checkDirectoryForLicenses({ | ||
fileRegexp, | ||
dirRegexp, | ||
excludeFileRegexp, | ||
excludeDirRegexp, | ||
license, | ||
log: !options.quiet.value, | ||
path, | ||
prepend, | ||
}); | ||
} catch (error) { | ||
cliError( | ||
`Failed while researching directory – ${error.message}`, | ||
error, | ||
); | ||
} | ||
} catch (error) { | ||
cliError(`Given regex is invalid – ${error.message}`, error); | ||
} | ||
}, | ||
priority: 1, | ||
}, | ||
}; | ||
|
||
/** | ||
* Error handler | ||
* @param message – message to be displayed when error happened | ||
*/ | ||
function cliError(message: string, error?: Error): void { | ||
if (options.throwErrors.value) { | ||
throw error || new Error(message); | ||
} else { | ||
console.log(`${red("Error")} ${yellow(">")} ${message}`); | ||
} | ||
} | ||
|
||
function styleArg(arg: string): string { | ||
const optional = arg[0] === "?"; | ||
|
||
let text = optional ? magenta("?") + arg.slice(1) : arg; | ||
text = text.replace(/(\<|\>)/g, yellow("$1")); | ||
text = text.replace(/(\|)/g, green("$1")); | ||
text = gray(text); | ||
|
||
return green(`[${text}]`); | ||
} | ||
|
||
if (import.meta.main) { | ||
let message = bold("Anzu - deno license checker\n\n"); | ||
for (const [name, command] of Object.entries(options)) { | ||
if (optionLinks.get(name)) { | ||
throw new Error("Option with this name already exists!"); | ||
} | ||
|
||
let short = `-${name[0]}`; | ||
let i = 1; | ||
while (optionLinks.get(short)) { | ||
short = `-${name.slice(i++)}`; | ||
} | ||
const long = `--${name}`; | ||
|
||
message += `${cyan(short)} ${cyan(long)} ${ | ||
command.args?.length ? `${command.args.map(styleArg).join(" ")} ` : "" | ||
}${gray("–")} ${command.message}\n`; | ||
|
||
optionLinks.set(short, command); | ||
optionLinks.set(long, command); | ||
} | ||
|
||
message += italic(`\nLegend ${ | ||
styleArg( | ||
`"${ | ||
magenta("?") | ||
}" – argument is optional | "<value>" – default value | "|" – or`, | ||
) | ||
}`); | ||
|
||
options.help.message = message; | ||
|
||
const cliArgs = parseArgs(Deno.args); | ||
|
||
interface Action { | ||
priority: number; | ||
func: () => void; | ||
} | ||
|
||
const actions: Action[] = []; | ||
const entries = Object.entries(cliArgs); | ||
|
||
if (entries.length === 0) { | ||
entries.push(["--help", []]); | ||
} | ||
|
||
for (const [name, args] of entries) { | ||
const option = optionLinks.get(name); | ||
if (!option) { | ||
cliError(`Option ${cyan(name)} has not been found`); | ||
Deno.exit(1); | ||
} | ||
|
||
if (option.args) { | ||
for (const [i, arg] of option.args.entries()) { | ||
if (arg[0] !== "?" && !args[i]) { | ||
cliError( | ||
`Required option argument ${styleArg(arg)} for ${ | ||
cyan(name) | ||
} is missing`, | ||
); | ||
Deno.exit(1); | ||
} | ||
} | ||
} | ||
|
||
const action: Action = { | ||
priority: option.priority, | ||
func: () => { | ||
if (option.func) { | ||
option.func(...args); | ||
} else { | ||
option.value = args; | ||
} | ||
}, | ||
}; | ||
|
||
actions.push(action); | ||
} | ||
|
||
for (const { func } of actions.sort((a, b) => b.priority - a.priority)) { | ||
func(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export { | ||
bold, | ||
cyan, | ||
gray, | ||
green, | ||
italic, | ||
magenta, | ||
red, | ||
white, | ||
yellow, | ||
} from "https://deno.land/[email protected]/fmt/colors.ts"; |
Oops, something went wrong.