Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@fimbul/wotan

Package Overview
Dependencies
Maintainers
2
Versions
180
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fimbul/wotan - npm Package Compare versions

Comparing version 0.2.0-dev.20180223 to 0.2.0

2

bin/main.js
#! /usr/bin/env node
require('import-local')(__dirname + '/../src/cli.js') || require('../src/cli.js');
(require('import-local')(__dirname + '/../src/cli.js') || require('../src/cli.js')).run(process.argv.slice(2));
{
"name": "@fimbul/wotan",
"version": "0.2.0-dev.20180223",
"version": "0.2.0",
"description": "Pluggable TypeScript and JavaScript linter",

@@ -5,0 +5,0 @@ "bin": "bin/main.js",

@@ -156,3 +156,3 @@ # wotan

* `-e --exclude <glob>` excludes all files that match the given glob pattern from linting. This option can be used multiple times to specify multiple patterns. For example `-e '**/*.js' -e '**/*.d.ts'`. It is recommended to wrap the glob patterns in single quotes to prevent the shell from expanding them.
* `-f --format <name>` the name or path of a formatter. This can either be a file name, the name of a node module contianing a formatter, or the name of a builtin formatter. Currently available builtin formatters are `json` and `stylish` (default).
* `-f --formatter <name>` the name or path of a formatter. This can either be a file name, the name of a node module contianing a formatter, or the name of a builtin formatter. Currently available builtin formatters are `json` and `stylish` (default).
* `--fix [true|false]` automatically fixes all fixable failures in your code and writes the result back to disk. There are some precautions to prevent overlapping fixes from destroying you code. You should however commit your changes before using this feature.

@@ -176,2 +176,41 @@ * `-p --project <name>` specifies the path to the `tsconfig.json` file to use. This option is used to find all files contained in your project. It also enables rules that require type information.

### Adding CLI defaults to `.fimbullinter.yaml`
If you find yourself using Wotan with the same CLI arguments over and over again, you can simply save them as defaults to a file called `.fimbullinter.yaml`. By default Wotan uses this file for CLI defaults if it's present in your current working directory.
There's a subcommand to create and update this file, so you don't need to know any implementation details to guess the file structure.
Let's assume you always use the following CLI arguments:
```sh
wotan -p tsconfig.build.json -c config/.wotanrc.yaml -e '**/*.d.ts'
```
To save these as defaults, simply use the `save` subcommand:
```sh
wotan save -p tsconfig.build.json -c config/.wotanrc.yaml -e '**/*.d.ts'
```
You just created a `.fimbullinter.yaml` file with the following contents:
```yaml
config: config/.wotanrc.yaml
exclude:
- "**/*.d.ts"
project: tsconfig.build.json
```
The next time you execute `wotan` in that directory, this default configuration is automatically picked up.
Defaults can be overridden or cleared by explicitly specifying them as CLI arguments, for example:
```sh
wotan -p tsconfig.json -e '' # overrides 'project' and clears 'exclude'
wotan save -c '' # clear 'config' option and update .fimbullinter.yaml
```
Note that `.fimbullinter.yaml` can also be used to store configuration for plugin modules. See the documentation of the plugins you use if this applies to you. In that case you need to edit the file manually. Using `wotan save` will not alter third party configuration.
## Diagnosing Misbehavior

@@ -178,0 +217,0 @@

import { Command } from './commands';
export declare function parseArguments(args: string[]): Command;
import { GlobalOptions } from './types';
import { LintOptions } from './runner';
export declare function parseArguments(args: string[], globalOptions?: GlobalOptions): Command;
export interface ParsedGlobalOptions extends LintOptions {
modules: string[];
formatter: string | undefined;
}
export declare function parseGlobalOptions(options: GlobalOptions | undefined): ParsedGlobalOptions;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const error_1 = require("./error");
function parseArguments(args) {
const debug = require("debug");
const log = debug('wotan:argparse');
function parseArguments(args, globalOptions) {
args = args.map(trimSingleQuotes);
const command = args[0];
switch (command) {
const commandName = args[0];
let command;
let defaults;
switch (commandName) {
case "lint":
return parseLintCommand(args.slice(1));
command = parseLintCommand(args.slice(1), defaults = parseGlobalOptions(globalOptions), "lint");
break;
case "save":
command = parseLintCommand(args.slice(1), defaults = parseGlobalOptions(globalOptions), "save");
break;
default:
command =
parseLintCommand(args, defaults = parseGlobalOptions(globalOptions), "lint");
break;
case "test":
return parseTestCommand(args.slice(1));
command = parseTestCommand(args.slice(1));
break;
case "show":
return parseShowCommand(args.slice(1));
command = parseShowCommand(args.slice(1), defaults = parseGlobalOptions(globalOptions));
break;
case "validate":
return parseValidateCommand(args.slice(1));
default:
return parseLintCommand(args);
command = parseValidateCommand(args.slice(1));
}
log("Parsed '%s' command as %O", command.command, command);
if (defaults !== undefined)
log('used defaults %O', defaults);
return command;
}
exports.parseArguments = parseArguments;
function parseLintCommand(args) {
const result = {
command: "lint",
modules: [],
config: undefined,
files: [],
exclude: [],
project: undefined,
format: undefined,
fix: false,
extensions: undefined,
function parseGlobalOptions(options) {
if (options === undefined)
return {
modules: [],
config: undefined,
files: [],
exclude: [],
project: undefined,
formatter: undefined,
fix: false,
extensions: undefined,
};
return {
modules: expectStringOrStringArray(options, 'modules') || [],
config: expectStringOption(options, 'config'),
files: expectStringOrStringArray(options, 'files') || [],
exclude: expectStringOrStringArray(options, 'exclude') || [],
project: expectStringOption(options, 'project'),
formatter: expectStringOption(options, 'formatter'),
fix: expectBooleanOrNumberOption(options, 'fix'),
extensions: (expectStringOrStringArray(options, 'extensions') || []).map(sanitizeExtensionArgument),
};
}
exports.parseGlobalOptions = parseGlobalOptions;
function expectStringOrStringArray(options, option) {
const value = options[option];
if (Array.isArray(value) && value.every((v) => typeof v === 'string'))
return value;
if (typeof value === 'string')
return [value];
if (value !== undefined)
log("Expected a value of type 'string | string[]' for option '%s'.", option);
return;
}
function expectStringOption(options, option) {
const value = options[option];
if (typeof value === 'string')
return value;
if (value !== undefined)
log("Expected a value of type 'string' for option '%s'.", option);
return;
}
function expectBooleanOrNumberOption(options, option) {
const value = options[option];
if (typeof value === 'boolean' || typeof value === 'number')
return value;
if (value !== undefined)
log("Expected a value of type 'boolean | number' for option '%s'.", option);
return false;
}
function parseLintCommand(args, defaults, command) {
const result = Object.assign({ command }, defaults);
const exclude = [];
const extensions = [];
const modules = [];
const files = [];
outer: for (let i = 0; i < args.length; ++i) {

@@ -38,15 +98,18 @@ const arg = args[i];

case '--project':
result.project = expectStringArgument(args, ++i, arg);
result.project = expectStringArgument(args, ++i, arg) || undefined;
break;
case '-e':
case '--exclude':
result.exclude.push(expectStringArgument(args, ++i, arg));
result.exclude = exclude;
const opt = expectStringArgument(args, ++i, arg);
if (opt !== '')
exclude.push(opt);
break;
case '-f':
case '--format':
result.format = expectStringArgument(args, ++i, arg);
case '--formatter':
result.formatter = expectStringArgument(args, ++i, arg) || undefined;
break;
case '-c':
case '--config':
result.config = expectStringArgument(args, ++i, arg);
result.config = expectStringArgument(args, ++i, arg) || undefined;
break;

@@ -56,18 +119,14 @@ case '--fix':

break;
case '--ext': {
const extensions = expectStringArgument(args, ++i, arg).split(/,/g).map(sanitizeExtensionArgument);
if (result.extensions === undefined) {
result.extensions = extensions;
}
else {
result.extensions.push(...extensions);
}
case '--ext':
result.extensions = extensions;
extensions.push(...expectStringArgument(args, ++i, arg).split(/,/g).map(sanitizeExtensionArgument).filter(isTruthy));
break;
}
case '-m':
case '--module':
result.modules.push(...expectStringArgument(args, ++i, arg).split(/,/g));
result.modules = modules;
modules.push(...expectStringArgument(args, ++i, arg).split(/,/g).filter(isTruthy));
break;
case '--':
result.files.push(...args.slice(i + 1));
result.files = files;
files.push(...args.slice(i + 1).filter(isTruthy));
break outer;

@@ -77,12 +136,23 @@ default:

throw new error_1.ConfigurationError(`Unknown option '${arg}'.`);
result.files.push(arg);
result.files = files;
if (arg !== '')
files.push(arg);
}
}
if (result.extensions !== undefined && (result.project !== undefined || result.files.length === 0))
throw new error_1.ConfigurationError("Options '--ext' and '--project' cannot be used together.");
if (result.extensions !== undefined) {
if (result.extensions.length === 0) {
result.extensions = undefined;
}
else if (result.project !== undefined || result.files.length === 0) {
throw new error_1.ConfigurationError("Options '--ext' and '--project' cannot be used together.");
}
}
return result;
}
function isTruthy(v) {
return v !== '';
}
function sanitizeExtensionArgument(ext) {
ext = ext.trim();
return ext.startsWith('.') ? ext : `.${ext}`;
return ext === '' || ext.startsWith('.') ? ext : `.${ext}`;
}

@@ -113,6 +183,6 @@ function parseTestCommand(args) {

case '--module':
result.modules.push(...expectStringArgument(args, ++i, arg).split(/,/g));
result.modules.push(...expectStringArgument(args, ++i, arg).split(/,/g).filter(isTruthy));
break;
case '--':
result.files.push(...args.slice(i + 1));
result.files.push(...args.slice(i + 1).filter(isTruthy));
break outer;

@@ -122,3 +192,4 @@ default:

throw new error_1.ConfigurationError(`Unknown option '${arg}'.`);
result.files.push(arg);
if (arg !== '')
result.files.push(arg);
}

@@ -130,7 +201,7 @@ }

}
function parseShowCommand(args) {
function parseShowCommand(args, defaults) {
const files = [];
const modules = [];
let modules;
let format;
let config;
let config = defaults.config;
outer: for (let i = 0; i < args.length; ++i) {

@@ -145,10 +216,12 @@ const arg = args[i];

case '--config':
config = expectStringArgument(args, ++i, arg);
config = expectStringArgument(args, ++i, arg) || undefined;
break;
case '-m':
case '--module':
modules.push(...expectStringArgument(args, ++i, arg).split(/,/g));
if (modules === undefined)
modules = [];
modules.push(...expectStringArgument(args, ++i, arg).split(/,/g).filter(isTruthy));
break;
case '--':
files.push(...args.slice(i + 1));
files.push(...args.slice(i + 1).filter(isTruthy));
break outer;

@@ -158,3 +231,4 @@ default:

throw new error_1.ConfigurationError(`Unknown option '${arg}'.`);
files.push(arg);
if (arg !== '')
files.push(arg);
}

@@ -168,4 +242,4 @@ }

format,
modules,
config,
modules: modules === undefined ? defaults.modules : modules,
command: "show",

@@ -172,0 +246,0 @@ file: files[0],

@@ -1,1 +0,3 @@

export {};
import { GlobalOptions } from './types';
export declare function run(argv: string[]): Promise<void>;
export declare function loadConfig(dir: string): Promise<GlobalOptions>;

@@ -5,7 +5,14 @@ "use strict";

const error_1 = require("./error");
function run() {
const fs = require("fs");
const path = require("path");
const debug = require("debug");
const log = debug('wotan:cli');
function run(argv) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (argv.length === 1 && /^(')?(?:-v|version)\1$/.test(argv[0]))
return console.log(require('../package.json').version);
try {
const args = (yield Promise.resolve().then(() => require('./argparse'))).parseArguments(process.argv.slice(2));
if (!(yield (yield Promise.resolve().then(() => require('./commands'))).runCommand(args)))
const config = yield loadConfig('.');
const args = (yield Promise.resolve().then(() => require('./argparse'))).parseArguments(argv, config);
if (!(yield (yield Promise.resolve().then(() => require('./commands'))).runCommand(args, undefined, config)))
process.exitCode = 2;

@@ -19,8 +26,27 @@ }

}
if (process.argv.length === 3 && /^(')?(?:-v|version)\1$/.test(process.argv[2])) {
console.log(require('../package.json').version);
exports.run = run;
function loadConfig(dir) {
const fileName = path.join(dir, '.fimbullinter.yaml');
return new Promise((resolve) => {
return fs.readFile(fileName, { encoding: 'utf8' }, (err, content) => {
if (err) {
log("Not using '%s': %s", fileName, err.code);
return resolve({});
}
return Promise.resolve().then(() => require('js-yaml')).then((yaml) => {
try {
resolve(yaml.safeLoad(content, { schema: yaml.JSON_SCHEMA, strict: true }) || {});
log("Using global options from '%s'", fileName);
}
catch (e) {
log("Not using '%s': %s", fileName, e && e.message);
resolve({});
}
});
});
});
}
else {
run();
}
exports.loadConfig = loadConfig;
if (require.main === module)
run(process.argv.slice(2));
//# sourceMappingURL=cli.js.map
import 'reflect-metadata';
import { LintOptions } from './runner';
import { Format } from './types';
import { Format, GlobalOptions } from './types';
import { Container } from 'inversify';
export declare const enum CommandName {
Lint = "lint",
Save = "save",
Validate = "validate",

@@ -15,5 +16,7 @@ Show = "show",

}
export interface LintCommand extends LintOptions, BaseCommand<CommandName.Lint> {
format: string | undefined;
export interface BaseLintCommand<T extends CommandName.Lint | CommandName.Save> extends LintOptions, BaseCommand<T> {
formatter: string | undefined;
}
export declare type LintCommand = BaseLintCommand<CommandName.Lint>;
export declare type SaveCommand = BaseLintCommand<CommandName.Save>;
export interface TestCommand extends BaseCommand<CommandName.Test> {

@@ -33,3 +36,3 @@ files: string[];

}
export declare type Command = LintCommand | ShowCommand | ValidateCommand | TestCommand;
export declare function runCommand(command: Command, diContainer?: Container): Promise<boolean>;
export declare type Command = LintCommand | SaveCommand | ShowCommand | ValidateCommand | TestCommand;
export declare function runCommand(command: Command, diContainer?: Container, globalOptions?: GlobalOptions): Promise<boolean>;

@@ -22,3 +22,5 @@ "use strict";

const resolve = require("resolve");
function runCommand(command, diContainer) {
const debug = require("debug");
const log = debug('wotan:commands');
function runCommand(command, diContainer, globalOptions = {}) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {

@@ -29,3 +31,3 @@ const container = new inversify_1.Container({ defaultScope: inversify_1.BindingScopeEnum.Singleton });

for (const moduleName of command.modules)
container.load(loadModule(moduleName));
container.load(loadModule(moduleName, globalOptions));
switch (command.command) {

@@ -35,2 +37,6 @@ case "lint":

break;
case "save":
container.bind(AbstractCommandRunner).to(SaveCommandRunner);
container.bind(types_1.GlobalOptions).toConstantValue(globalOptions);
break;
case "validate":

@@ -56,3 +62,4 @@ container.bind(AbstractCommandRunner).to(ValidateCommandRunner);

exports.runCommand = runCommand;
function loadModule(moduleName) {
function loadModule(moduleName, options) {
log("Loading module '%s'.", moduleName);
try {

@@ -68,6 +75,7 @@ moduleName = resolve.sync(moduleName, {

}
log("Found module at '%s'.", moduleName);
const m = require(moduleName);
if (!m || !(m.module instanceof inversify_1.ContainerModule))
throw new error_1.ConfigurationError(`Module '${moduleName}' does not a 'module' binding that extends 'ContainerModule'.`);
return m.module;
if (!m || typeof m.createModule !== 'function')
throw new error_1.ConfigurationError(`Module '${moduleName}' does not export a function 'createModule'.`);
return m.createModule(options);
}

@@ -88,3 +96,3 @@ let AbstractCommandRunner = class AbstractCommandRunner {

run(options) {
const formatter = new (this.formatterLoader.loadFormatter(options.format === undefined ? 'stylish' : options.format))();
const formatter = new (this.formatterLoader.loadFormatter(options.formatter === undefined ? 'stylish' : options.formatter))();
const result = this.runner.lintCollection(options);

@@ -121,2 +129,35 @@ let success = true;

}
let SaveCommandRunner = class SaveCommandRunner extends AbstractCommandRunner {
constructor(fs, logger, directories, options) {
super();
this.fs = fs;
this.logger = logger;
this.directories = directories;
this.options = options;
}
run(_a) {
var { command: _command } = _a, config = tslib_1.__rest(_a, ["command"]);
const newContent = utils_1.format(Object.assign({}, this.options, config, { fix: config.fix || undefined }), "yaml");
const filePath = path.join(this.directories.getCurrentDirectory(), '.fimbullinter.yaml');
if (newContent.trim() === '{}') {
try {
this.fs.remove(filePath);
this.logger.log("Removed '.fimbullinter.yaml'.");
}
catch (_b) { }
}
else {
this.fs.writeFile(filePath, newContent);
this.logger.log("Updated '.fimbullinter.yaml'.");
}
return true;
}
};
SaveCommandRunner = tslib_1.__decorate([
inversify_1.injectable(),
tslib_1.__metadata("design:paramtypes", [cached_file_system_1.CachedFileSystem,
types_1.MessageHandler,
types_1.DirectoryService,
types_1.GlobalOptions])
], SaveCommandRunner);
let ValidateCommandRunner = class ValidateCommandRunner extends AbstractCommandRunner {

@@ -123,0 +164,0 @@ constructor() {

@@ -1,2 +0,2 @@

import { Configuration, CacheFactory, ReducedConfiguration, GlobalSettings, DirectoryService, ConfigurationProvider } from '../types';
import { Configuration, CacheFactory, ReducedConfiguration, Settings, DirectoryService, ConfigurationProvider } from '../types';
import { CachedFileSystem } from './cached-file-system';

@@ -15,4 +15,4 @@ export declare class ConfigurationManager {

getProcessor(config: Configuration, fileName: string): string | undefined;
getSettings(config: Configuration, fileName: string): GlobalSettings;
getSettings(config: Configuration, fileName: string): Settings;
load(fileName: string): Configuration;
}
import * as ts from 'typescript';
import { WrappedAst } from 'tsutils';
export declare abstract class GlobalOptions {
readonly [key: string]: {} | null | undefined;
}
export declare type LintResult = Iterable<[string, FileSummary]>;

@@ -44,3 +47,3 @@ export declare type FileSummary = LintAndFixFileResult;

readonly deprecated?: boolean | string;
supports?(sourceFile: ts.SourceFile, options: any, settings: GlobalSettings): boolean;
supports?(sourceFile: ts.SourceFile, options: any, settings: Settings): boolean;
new (context: RuleContext): AbstractRule;

@@ -51,3 +54,3 @@ }

readonly sourceFile: ts.SourceFile;
readonly settings: GlobalSettings;
readonly settings: Settings;
readonly options: {} | null | undefined;

@@ -65,4 +68,3 @@ addFailure(start: number, end: number, message: string, fix?: Replacement | Replacement[]): void;

}
export interface GlobalSettings extends ReadonlyMap<string, {} | null | undefined> {
}
export declare type Settings = ReadonlyMap<string, {} | null | undefined>;
export declare abstract class AbstractRule {

@@ -72,3 +74,3 @@ readonly context: RuleContext;

static deprecated?: boolean | string;
static supports?(sourceFile: ts.SourceFile, options: any, settings: GlobalSettings): boolean;
static supports?(sourceFile: ts.SourceFile, options: any, settings: Settings): boolean;
static validateConfig?(config: any): string[] | string | undefined;

@@ -110,3 +112,3 @@ readonly sourceFile: ts.SourceFile;

readonly rules?: ReadonlyMap<string, Configuration.RuleConfig>;
readonly settings?: GlobalSettings;
readonly settings?: Settings;
readonly filename: string;

@@ -177,3 +179,3 @@ readonly overrides?: ReadonlyArray<Configuration.Override>;

fileName: string;
getSettings(): GlobalSettings;
getSettings(): Settings;
readFile(): string;

@@ -185,3 +187,3 @@ }

targetFileName: string;
settings: GlobalSettings;
settings: Settings;
}

@@ -197,3 +199,3 @@ export interface ProcessorUpdateResult {

protected targetFileName: string;
protected settings: GlobalSettings;
protected settings: Settings;
constructor(context: ProcessorContext);

@@ -200,0 +202,0 @@ abstract preprocess(): string;

@@ -5,2 +5,5 @@ "use strict";

const utils_1 = require("./utils");
class GlobalOptions {
}
exports.GlobalOptions = GlobalOptions;
class Replacement {

@@ -7,0 +10,0 @@ constructor() { }

@@ -49,3 +49,3 @@ "use strict";

function format(value, fmt = "yaml") {
value = convertToPrintable(value);
value = convertToPrintable(value) || {};
switch (fmt) {

@@ -52,0 +52,0 @@ case "json":

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc