Skip to content

Commit

Permalink
Merge pull request #17 from tyler-johnson/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
tyler-johnson authored Oct 10, 2018
2 parents 92766d9 + fccc501 commit e3cc95b
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 66 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module.exports = {
},
globals: {
"ts-jest": {
"tsConfigFile": "tsconfig.json"
"tsConfig": "tsconfig.json"
}
},
testMatch: [
Expand Down
32 changes: 16 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"lodash.topath": "^4.5.2"
},
"devDependencies": {
"@types/jest": "^23.3.2",
"@types/jest": "^23.3.5",
"@types/node": "^10.11.7",
"autorelease": "^1.6.0",
"autorelease-github": "^1.1.1",
"autorelease-travis": "^1.3.0",
Expand All @@ -36,10 +37,10 @@
"jest": "^23.6.0",
"npm-run-all": "^4.1.1",
"shx": "^0.3.2",
"ts-jest": "^23.10.1",
"ts-jest": "^23.10.4",
"tslint": "^5.11.0",
"typedoc": "^0.11.1",
"typescript": "^3.0.1"
"typescript": "^3.1.2"
},
"keywords": [],
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion src/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class Field extends Record(DEFAULTS) {
return "field";
}

getChildField(key: string) {
getChildField(key: string | null) {
return this.children.find((field) => field.key === key);
}

Expand Down
48 changes: 28 additions & 20 deletions src/rules/section.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// rule for section composition

import { List, Set } from "immutable";
import { List } from "immutable";
import { Rule } from "../schema";
import { Field } from "../field";

function isPlainObject(o: any): o is {} {
function isPlainObject(o: any): o is { [key: string]: any; } {
return o != null && o.constructor === Object;
}

Expand All @@ -16,32 +16,40 @@ const sectionRule: Rule = {
if (typeof value === "undefined") value = {};
if (!isPlainObject(value)) return value;

const keys = Object.keys(value);
field.children.forEach((o) => {
if (o.key && !keys.includes(o.key)) keys.push(o.key);
});

return keys.reduce((m, k) => {
const child = field.getChildField(k);
if (!child) m[k] = value[k];
else m[k] = this.transform(value[k], child);
return m;
}, {} as { [key: string]: any; });
const result: { [key: string]: any; } = {};
const keys: string[] = Object.keys(value); // one list for order
const uniqKeys = new Set<string>(keys); // another list for uniqueness

for (const child of field.children) {
if (child.key && !uniqKeys.has(child.key)) {
keys.push(child.key);
uniqKeys.add(child.key);
}
}

for (const key of keys) {
const child = field.getChildField(key);
if (!child) result[key] = value[key];
else result[key] = this.transform(value[key], child);
}

return result;
},
join(a, b) {
if (b.type !== "section") return a;

const keys = Set(a.children.map((o) => o.key))
.union(b.children.map((o) => o.key));
const keys = new Set<string | null>();
let children = List<Field>();

for (const { key } of a.children.concat(b.children)) {
if (keys.has(key)) continue;
keys.add(key);

const children = keys.reduce((m, key) => {
if (!key) return m;
const aopt = a.getChildField(key);
const bopt = b.getChildField(key);
if (!aopt && !bopt) return m;
const opt = !aopt ? bopt : !bopt ? aopt : this.join(aopt, bopt);
return m.push(opt);
}, List());
if (opt) children = children.push(opt);
}

return a.merge({ children });
}
Expand Down
69 changes: 50 additions & 19 deletions src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Record, List } from "immutable";
import { Field } from "./field";
import { isIterable } from "./utils";
import { isIterable, warnOnce } from "./utils";

export interface Rule {
match: (this: Schema, field: Field) => any;
match: (this: Schema, field: Field) => boolean;
transform?: (this: Schema, value: any, field: Field) => any;
normalize?: (this: Schema, field: Field) => any;
join?: (this: Schema, field: Field, joinWith: Field) => any;
normalize?: (this: Schema, field: Field) => Field;
join?: (this: Schema, field: Field, joinWith: Field) => Field;
}

export interface SchemaRecord {
Expand Down Expand Up @@ -55,33 +55,60 @@ export class Schema extends Record(DEFAULTS) {
return "schema";
}

reduce(field: Field, fn: (this: Schema, rule: Rule, field: Field) => Field) {
for (const rule of this.rules) {
if (rule.match.call(this, field)) {
field = fn.call(this, rule, field);
}
}

return field;
}

apply(field: Field, prop: keyof Rule, ...args: any[]) {
return this.rules.reduce((fieldResult, rule) => {
warnOnce(
"apply() method on form-blueprint schema is deprecated." +
" Use Schema#reduce() instead and run methods directly."
);

return this.reduce(field, (rule, f) => {
const method = rule[prop];
if (typeof method !== "function") return fieldResult;
if (!rule.match.call(this, fieldResult)) return fieldResult;
const res = method.apply(this, [fieldResult].concat(args));
return Field.isField(res) ? res : fieldResult;
}, field);
if (typeof method !== "function") return f;
const res = method.apply(this, [ f, ...args ]);
return Field.isField(res) ? res : f;
});
}

normalize(field: Field): Field {
field = this.apply(field, "normalize");
field = this.reduce(field, (rule, f) => {
return rule.normalize ? rule.normalize.call(this, f) : f;
});

return field.merge({
children: field.children.map((c) => this.normalize(c))
});
}

transform(value: any, field: Field) {
return this.rules.reduce((val, rule) => {
if (!rule.transform) return val;
if (!rule.match.call(this, field)) return val;
return rule.transform.call(this, val, field);
}, value);
this.reduce(field, (rule, f) => {
if (rule.transform) {
value = rule.transform.call(this, value, f);
}

return f;
});

return value;
}

join(field: Field, ...toJoin: Field[]) {
return toJoin.reduce((m, b) => this.apply(m, "join", b), field);
for (const join of toJoin) {
field = this.reduce(field, (rule, f) => {
return rule.join ? rule.join.call(this, f, join) : f;
});
}

return field;
}

addRule(rule: Rule) {
Expand All @@ -91,10 +118,14 @@ export class Schema extends Record(DEFAULTS) {
}

concat(rules: SchemaCreate) {
const schema = Schema.create(rules);
let newRules = this.rules;

for (const rule of Schema.create(rules).rules) {
if (!newRules.includes(rule)) newRules = newRules.push(rule);
}

return this.merge({
rules: this.rules.concat(schema.rules)
rules: newRules
});
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
export function isIterable(i: any): i is Iterable<any> {
return Boolean(i && typeof i[Symbol.iterator] === "function");
}

const warnings = new Set();
export function warnOnce(message: string) {
if (!warnings.has(message)) {
warnings.add(message);
console.warn(message);
}
}
12 changes: 6 additions & 6 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@
"target": "es5",
"module": "commonjs",
"declaration": true,
"experimentalDecorators": true,
"preserveSymlinks": true,
"strict": true,
"strictFunctionTypes": false,
"downlevelIteration": true,
"jsx": "react",
"esModuleInterop": true,
"types": [
"node", "jest"
],
"lib": [
"es5", "es6", "es7", "dom",
"es2015", "es2016", "es2017", "es2018"
"dom", "es2017"
]
},
"include": [
"./src/**/*"
"./src/**/*.ts"
]
}

0 comments on commit e3cc95b

Please sign in to comment.