Skip to content

Commit

Permalink
feat(package): add tslint
Browse files Browse the repository at this point in the history
related to aurelia#357
  • Loading branch information
jdanyow committed Oct 14, 2016
1 parent bea1cd7 commit 80315ab
Show file tree
Hide file tree
Showing 35 changed files with 494 additions and 432 deletions.
5 changes: 5 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"eg2.tslint"
]
}
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": false
}
10 changes: 0 additions & 10 deletions .vscode/tasks.json

This file was deleted.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"url": "https://github.com/aurelia/validation"
},
"scripts": {
"lint": "cross-env ./node_modules/.bin/tslint --project tsconfig.json",
"pretest": "cross-env npm run lint",
"test": "cross-env ./node_modules/.bin/tsc && cross-env ./node_modules/karma/bin/karma start --single-run",
"test-watch": "concurrently \"./node_modules/.bin/tsc --watch\" \"./node_modules/karma/bin/karma start\"",
"prebuild:compile": "rimraf 'dist/**'",
Expand Down Expand Up @@ -80,6 +82,7 @@
"requirejs": "^2.2.0",
"requirejs-text": "^2.0.12",
"rimraf": "^2.5.4",
"tslint": "^3.15.1",
"typedoc": "^0.5.0",
"typescript": "^2.0.0"
},
Expand Down
20 changes: 10 additions & 10 deletions src/aurelia-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,29 @@ export * from './implementation/validation-rules';

// Configuration

import {Container} from 'aurelia-dependency-injection';
import {Validator} from './validator';
import {StandardValidator} from './implementation/standard-validator';
import {ValidationParser} from './implementation/validation-parser';
import {ValidationRules} from './implementation/validation-rules';
import { Container } from 'aurelia-dependency-injection';
import { Validator } from './validator';
import { StandardValidator } from './implementation/standard-validator';
import { ValidationParser } from './implementation/validation-parser';
import { ValidationRules } from './implementation/validation-rules';

/**
* Aurelia Validation Configuration API
*/
export class AureliaValidationConfiguration {
private validatorType: { new(...args: any[]): Validator } = StandardValidator;
private validatorType: { new (...args: any[]): Validator } = StandardValidator;

/**
* Use a custom Validator implementation.
*/
customValidator(type: { new(...args: any[]): Validator }) {
this.validatorType = type;
public customValidator(type: { new (...args: any[]): Validator }) {
this.validatorType = type;
}

/**
* Applies the configuration.
*/
apply(container: Container) {
public apply(container: Container) {
const validator = container.get(this.validatorType);
container.registerInstance(Validator, validator);
}
Expand All @@ -52,7 +52,7 @@ export class AureliaValidationConfiguration {
*/
export function configure(
frameworkConfig: { container: Container, globalResources: (...resources: string[]) => any },
callback?: (config: AureliaValidationConfiguration) => void
callback?: (config: AureliaValidationConfiguration) => void
) {
// the fluent rule definition API needs the parser to translate messages
// to interpolation expressions.
Expand Down
12 changes: 6 additions & 6 deletions src/implementation/rule.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Expression} from 'aurelia-binding';
import { Expression } from 'aurelia-binding';

/**
* Information related to a property that is the subject of validation.
Expand All @@ -7,24 +7,24 @@ export interface RuleProperty {
/**
* The property name. null indicates the rule targets the object itself.
*/
name: string|null;
name: string | null;

/**
* The displayName of the property (or object).
*/
displayName: string|null;
displayName: string | null;
}

/**
* A rule definition. Associations a rule with a property or object.
*/
export interface Rule<TObject, TValue> {
property: RuleProperty;
condition: (value: TValue, object?: TObject) => boolean|Promise<boolean>;
condition: (value: TValue, object?: TObject) => boolean | Promise<boolean>;
config: Object;
when: { (object: TObject): boolean }|null;
when: { (object: TObject): boolean } | null;
messageKey: string;
message: Expression|null;
message: Expression | null;
sequence: number;
tag?: string;
}
12 changes: 6 additions & 6 deletions src/implementation/rules.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Rule} from './rule';
import { Rule } from './rule';

/**
* Sets, unsets and retrieves rules on an object or constructor function.
Expand All @@ -7,12 +7,12 @@ export class Rules {
/**
* The name of the property that stores the rules.
*/
static key = '__rules__';
private static key = '__rules__';

/**
* Applies the rules to a target.
*/
static set(target: any, rules: Rule<any, any>[][]): void {
public static set(target: any, rules: Rule<any, any>[][]): void {
if (target instanceof Function) {
target = target.prototype;
}
Expand All @@ -25,17 +25,17 @@ export class Rules {
/**
* Removes rules from a target.
*/
static unset(target: any): void {
public static unset(target: any): void {
if (target instanceof Function) {
target = target.prototype;
}
}
target[Rules.key] = null;
}

/**
* Retrieves the target's rules.
*/
static get(target: any): Rule<any, any>[][]|null {
public static get(target: any): Rule<any, any>[][] | null {
return target[Rules.key] || null;
}
}
115 changes: 61 additions & 54 deletions src/implementation/standard-validator.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import {Expression, LookupFunctions} from 'aurelia-binding';
import {ViewResources} from 'aurelia-templating';
import {Validator} from '../validator';
import {ValidationError} from '../validation-error';
import {Rule} from './rule';
import {Rules} from './rules';
import {ValidationMessageProvider} from './validation-messages';
import { Expression, LookupFunctions } from 'aurelia-binding';
import { ViewResources } from 'aurelia-templating';
import { Validator } from '../validator';
import { ValidationError } from '../validation-error';
import { Rule } from './rule';
import { Rules } from './rules';
import { ValidationMessageProvider } from './validation-messages';

/**
* Validates.
* Responsible for validating objects and properties.
*/
export class StandardValidator extends Validator {
static inject = [ValidationMessageProvider, ViewResources];
public static inject = [ValidationMessageProvider, ViewResources];

private messageProvider: ValidationMessageProvider;
private lookupFunctions: LookupFunctions;
Expand All @@ -24,6 +24,42 @@ export class StandardValidator extends Validator {
this.getDisplayName = messageProvider.getDisplayName.bind(messageProvider);
}

/**
* Validates the specified property.
* @param object The object to validate.
* @param propertyName The name of the property to validate.
* @param rules Optional. If unspecified, the rules will be looked up using the metadata
* for the object created by ValidationRules....on(class/object)
*/
public validateProperty(object: any, propertyName: string, rules?: any): Promise<ValidationError[]> {
return this.validate(object, propertyName, rules || null);
}

/**
* Validates all rules for specified object and it's properties.
* @param object The object to validate.
* @param rules Optional. If unspecified, the rules will be looked up using the metadata
* for the object created by ValidationRules....on(class/object)
*/
public validateObject(object: any, rules?: any): Promise<ValidationError[]> {
return this.validate(object, null, rules || null);
}

/**
* Determines whether a rule exists in a set of rules.
* @param rules The rules to search.
* @parem rule The rule to find.
*/
public ruleExists(rules: Rule<any, any>[][], rule: Rule<any, any>): boolean {
let i = rules.length;
while (i--) {
if (rules[i].indexOf(rule) !== -1) {
return true;
}
}
return false;
}

private getMessage(rule: Rule<any, any>, object: any, value: any): string {
const expression: Expression = rule.message || this.messageProvider.getMessage(rule.messageKey);
let { name: propertyName, displayName } = rule.property;
Expand All @@ -36,25 +72,28 @@ export class StandardValidator extends Validator {
$value: value,
$object: object,
$config: rule.config,
$getDisplayName: this.getDisplayName
$getDisplayName: this.getDisplayName
};
return expression.evaluate(
{ bindingContext: object, overrideContext },
this.lookupFunctions);
}

private validateRuleSequence(object: any, propertyName: string|null, ruleSequence: Rule<any, any>[][], sequence: number): Promise<ValidationError[]> {
private validateRuleSequence(
object: any, propertyName: string | null,
ruleSequence: Rule<any, any>[][], sequence: number
): Promise<ValidationError[]> {
// are we validating all properties or a single property?
const validateAllProperties = propertyName === null || propertyName === undefined;

const rules = ruleSequence[sequence];
const errors: ValidationError[] = [];

// validate each rule.
const promises: Promise<boolean>[] = [];
for (let i = 0; i < rules.length; i++) {
const rule = rules[i];

// is the rule related to the property we're validating.
if (!validateAllProperties && rule.property.name !== propertyName) {
continue;
Expand All @@ -75,21 +114,25 @@ export class StandardValidator extends Validator {
if (!isValid) {
const message = this.getMessage(rule, object, value);
errors.push(new ValidationError(rule, message, object, rule.property.name));
}
}
}));
}

return Promise.all(promises)
.then(() => {
sequence++;
if (errors.length === 0 && sequence < ruleSequence.length) {
return this.validateRuleSequence(object, propertyName, ruleSequence, sequence);
}
return errors;
});
});
}

private validate(object: any, propertyName: string|null, rules: Rule<any, any>[][]|null): Promise<ValidationError[]> {
private validate(
object: any,
propertyName: string | null,
rules: Rule<any, any>[][] | null
): Promise<ValidationError[]> {
// rules specified?
if (!rules) {
// no. attempt to locate the rules.
Expand All @@ -100,43 +143,7 @@ export class StandardValidator extends Validator {
if (!rules) {
return Promise.resolve([]);
}

return this.validateRuleSequence(object, propertyName, rules, 0);
}

/**
* Validates the specified property.
* @param object The object to validate.
* @param propertyName The name of the property to validate.
* @param rules Optional. If unspecified, the rules will be looked up using the metadata
* for the object created by ValidationRules....on(class/object)
*/
validateProperty(object: any, propertyName: string, rules?: any): Promise<ValidationError[]> {
return this.validate(object, propertyName, rules || null);
}

/**
* Validates all rules for specified object and it's properties.
* @param object The object to validate.
* @param rules Optional. If unspecified, the rules will be looked up using the metadata
* for the object created by ValidationRules....on(class/object)
*/
validateObject(object: any, rules?: any): Promise<ValidationError[]> {
return this.validate(object, null, rules || null);
}

/**
* Determines whether a rule exists in a set of rules.
* @param rules The rules to search.
* @parem rule The rule to find.
*/
ruleExists(rules: Rule<any, any>[][], rule: Rule<any, any>): boolean {
let i = rules.length;
while (i--) {
if (rules[i].indexOf(rule) !== -1) {
return true;
}
}
return false;
return this.validateRuleSequence(object, propertyName, rules, 0);
}
}
14 changes: 7 additions & 7 deletions src/implementation/validation-messages.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Expression} from 'aurelia-binding';
import {ValidationParser} from './validation-parser';
import { Expression } from 'aurelia-binding';
import { ValidationParser } from './validation-parser';

export interface ValidationMessages {
[key: string]: string;
Expand All @@ -21,21 +21,21 @@ export const validationMessages: ValidationMessages = {
minItems: `\${$displayName} must contain at least \${$config.count} item\${$config.count === 1 ? '' : 's'}.`,
maxItems: `\${$displayName} cannot contain more than \${$config.count} item\${$config.count === 1 ? '' : 's'}.`,
equals: `\${$displayName} must be \${$config.expectedValue}.`,
}
};

/**
* Retrieves validation messages and property display names.
*/
export class ValidationMessageProvider {
static inject = [ValidationParser];
public static inject = [ValidationParser];

constructor(private parser: ValidationParser) {}
constructor(private parser: ValidationParser) { }

/**
* Returns a message binding expression that corresponds to the key.
* @param key The message key.
*/
getMessage(key: string): Expression {
public getMessage(key: string): Expression {
let message: string;
if (key in validationMessages) {
message = validationMessages[key];
Expand All @@ -51,7 +51,7 @@ export class ValidationMessageProvider {
* Override this with your own custom logic.
* @param propertyName The property name.
*/
getDisplayName(propertyName: string): string {
public getDisplayName(propertyName: string): string {
// split on upper-case letters.
const words = propertyName.split(/(?=[A-Z])/).join(' ');
// capitalize first letter.
Expand Down
Loading

0 comments on commit 80315ab

Please sign in to comment.