Skip to content

Commit

Permalink
Add 'transform', 'keyTransformer', and port to typescript. :arrow_Up:…
Browse files Browse the repository at this point in the history
… 2.0.0
  • Loading branch information
fasterthanlime committed May 5, 2017
1 parent 6314d9c commit ae72d65
Show file tree
Hide file tree
Showing 29 changed files with 710 additions and 232 deletions.
4 changes: 0 additions & 4 deletions .babelrc

This file was deleted.

11 changes: 0 additions & 11 deletions .jshintrc

This file was deleted.

2 changes: 2 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
src
test
18 changes: 18 additions & 0 deletions lib/EntitySchema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import ISchema from "./Schema";
export default class EntitySchema implements ISchema {
_key: string;
_getId: AttributeGetter;
_idAttribute: string | AttributeGetter;
constructor(key: string, options?: IEntitySchemaOptions);
getKey(): string;
getId(entity: any): any;
getIdAttribute(): string | AttributeGetter;
define(nestedSchema: INestedSchema): void;
}
export declare type AttributeGetter = (x: any) => any;
export interface IEntitySchemaOptions {
idAttribute?: string | AttributeGetter;
}
export interface INestedSchema {
[key: string]: ISchema;
}
28 changes: 28 additions & 0 deletions lib/EntitySchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class EntitySchema {
constructor(key, options = {}) {
if (!key || typeof key !== "string") {
throw new Error("A string non-empty key is required");
}
this._key = key;
const idAttribute = options.idAttribute || "id";
this._getId = typeof idAttribute === "function" ? idAttribute : (x) => x[idAttribute];
this._idAttribute = idAttribute;
}
getKey() {
return this._key;
}
getId(entity) {
return this._getId(entity);
}
getIdAttribute() {
return this._idAttribute;
}
define(nestedSchema) {
for (const key of Object.keys(nestedSchema)) {
this[key] = nestedSchema[key];
}
}
}
exports.default = EntitySchema;
6 changes: 6 additions & 0 deletions lib/IterableSchema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import ISchema from "./Schema";
export default class ArraySchema implements ISchema {
_itemSchema: ISchema;
constructor(itemSchema: ISchema);
getItemSchema(): ISchema;
}
14 changes: 14 additions & 0 deletions lib/IterableSchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class ArraySchema {
constructor(itemSchema) {
if (!itemSchema) {
throw new Error("Iterable schema needs a valid item schema");
}
this._itemSchema = itemSchema;
}
getItemSchema() {
return this._itemSchema;
}
}
exports.default = ArraySchema;
2 changes: 2 additions & 0 deletions lib/Schema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export default class ISchema {
}
5 changes: 5 additions & 0 deletions lib/Schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class ISchema {
}
exports.default = ISchema;
6 changes: 6 additions & 0 deletions lib/TransformerSchema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import ISchema from "./Schema";
export default class TransformerSchema implements ISchema {
_transformer: ITransformer;
constructor(transformer: ITransformer);
}
export declare type ITransformer = (x: any) => any;
8 changes: 8 additions & 0 deletions lib/TransformerSchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class TransformerSchema {
constructor(transformer) {
this._transformer = transformer;
}
}
exports.default = TransformerSchema;
26 changes: 26 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import ISchema from "./Schema";
import EntitySchema from "./EntitySchema";
import IterableSchema from "./IterableSchema";
import TransformerSchema, { ITransformer } from "./TransformerSchema";
export declare const Schema: typeof EntitySchema;
export declare function arrayOf(schema: ISchema): IterableSchema;
export declare function valuesOf(schema: ISchema): IterableSchema;
export declare function transform(transformer: ITransformer): TransformerSchema;
export declare function normalize(obj: IVisitable, schema: ISchema, userOptions?: IVisitOptions): {
entities: IBag;
result: any;
};
export declare type IVisitable = {
[key: string]: IVisitable;
} | {
[key: number]: IVisitable;
} | object;
export interface IBag {
[schemaName: string]: {
[id: string]: any;
};
}
export declare type IKeyTransformer = (key: string) => string;
export interface IVisitOptions {
keyTransformer?: IKeyTransformer;
}
127 changes: 127 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const EntitySchema_1 = require("./EntitySchema");
const IterableSchema_1 = require("./IterableSchema");
const TransformerSchema_1 = require("./TransformerSchema");
const nodash_1 = require("./nodash");
function visitObject(obj, schema, bag, opts) {
const k = opts.keyTransformer;
const normalized = {};
for (const key of Object.keys(obj)) {
const itemSchema = schema[key];
const result = visit(obj[key], itemSchema, bag, opts);
if (itemSchema instanceof EntitySchema_1.default) {
normalized[k(key + "_id")] = result;
}
else if (isIterableSchema(itemSchema) && Array.isArray(result)) {
normalized[k(key.replace(/s$/, "") + "_ids")] = result;
}
else if (isTransformerSchema(itemSchema)) {
normalized[k(key)] = itemSchema._transformer(result);
}
else {
normalized[k(key)] = result;
}
}
return normalized;
}
function visitIterable(obj, iterableSchema, bag, opts) {
const itemSchema = iterableSchema.getItemSchema();
const k = opts.keyTransformer;
const f = (fobj) => visit(fobj, itemSchema, bag, opts);
if (Array.isArray(obj)) {
return obj.map(f);
}
else {
const normalized = {};
for (const key of Object.keys(obj)) {
const result = f(obj[key]);
if (itemSchema instanceof EntitySchema_1.default) {
normalized[k(key + "_id")] = result;
}
else if (itemSchema instanceof IterableSchema_1.default && Array.isArray(result)) {
normalized[k(key.replace(/s$/, "") + "_ids")] = result;
}
}
return normalized;
}
}
function mergeIntoEntity(entityA, entityB, entityKey) {
for (const key of Object.keys(entityB)) {
if (!entityA.hasOwnProperty(key) || nodash_1.isEqual(entityA[key], entityB[key])) {
entityA[key] = entityB[key];
continue;
}
// tslint:disable-next-line
console.warn("When merging two " + entityKey + ', found unequal data in their "' + key + '" values. Using the earlier value.', entityA[key], entityB[key]);
}
}
function visitEntity(entity, entitySchema, bag, opts) {
const entityKey = entitySchema.getKey();
const id = entitySchema.getId(entity);
if (!bag.hasOwnProperty(entityKey)) {
bag[entityKey] = {};
}
if (!bag[entityKey].hasOwnProperty(id)) {
bag[entityKey][id] = {};
}
const stored = bag[entityKey][id];
const normalized = visitObject(entity, entitySchema, bag, opts);
mergeIntoEntity(stored, normalized, entityKey);
return id;
}
function visit(obj, schema, bag, opts) {
if (!nodash_1.isObject(obj) || !nodash_1.isObject(schema)) {
return obj;
}
if (isEntitySchema(schema)) {
return visitEntity(obj, schema, bag, opts);
}
else if (isIterableSchema(schema)) {
return visitIterable(obj, schema, bag, opts);
}
else {
return visitObject(obj, schema, bag, opts);
}
}
exports.Schema = EntitySchema_1.default;
function arrayOf(schema) {
return new IterableSchema_1.default(schema);
}
exports.arrayOf = arrayOf;
function valuesOf(schema) {
return new IterableSchema_1.default(schema);
}
exports.valuesOf = valuesOf;
function transform(transformer) {
return new TransformerSchema_1.default(transformer);
}
exports.transform = transform;
function normalize(obj, schema, userOptions = {}) {
const opts = Object.assign({}, userOptions);
if (!opts.keyTransformer) {
opts.keyTransformer = (x) => x;
}
if (!nodash_1.isObject(obj) && !Array.isArray(obj)) {
throw new Error("Normalize accepts an object or an array as its input.");
}
if (!nodash_1.isObject(schema) || Array.isArray(schema)) {
throw new Error("Normalize accepts an object for schema.");
}
const bag = {};
const result = visit(obj, schema, bag, opts);
return {
entities: bag,
result,
};
}
exports.normalize = normalize;
function isEntitySchema(s) {
return s instanceof EntitySchema_1.default;
}
function isTransformerSchema(s) {
return s instanceof TransformerSchema_1.default;
}
function isIterableSchema(s) {
return s instanceof IterableSchema_1.default;
}
2 changes: 2 additions & 0 deletions lib/nodash.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export declare function isObject(obj: any): boolean;
export import isEqual = require("deep-equal");
7 changes: 7 additions & 0 deletions lib/nodash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function isObject(obj) {
return obj && typeof obj === "object";
}
exports.isObject = isObject;
exports.isEqual = require("deep-equal");
18 changes: 10 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "idealizr",
"version": "1.1.0",
"version": "2.0.0",
"description": "Normalizes JSON according to a schema",
"main": "src/index.js",
"main": "lib/index.js",
"repository": {
"type": "git",
"url": "https://github.com/fasterthanlime/idealizr.git"
Expand Down Expand Up @@ -31,18 +31,20 @@
},
"homepage": "https://github.com/fasterthanlime/idealizr",
"scripts": {
"test": "npm run tape && npm run lint",
"tape": "node test/index.js | tap-difflet",
"lint": "standard src/**.js test/**.js",
"prepublish": "npm run compile",
"compile": "tsc",
"test": "npm run compile && npm run tape && npm run lint",
"lint": "tslint ./src/*.ts",
"tape": "node test/index.js",
"cov": "nyc -s npm run tape && nyc report -r text",
"coveralls": "npm run cov && nyc report -r text-lcov | coveralls"
},
"devDependencies": {
"coveralls": "^2.11.8",
"nyc": "^6.0.0",
"standard": "^6.0.7",
"tap-difflet": "^0.4.0",
"tape": "^4.4.0"
"tape": "^4.4.0",
"tslint": "^5.2.0",
"typescript": "^2.2.2"
},
"dependencies": {
"deep-equal": "^1.0.1"
Expand Down
41 changes: 0 additions & 41 deletions src/EntitySchema.js

This file was deleted.

0 comments on commit ae72d65

Please sign in to comment.