forked from paularmstrong/normalizr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
133 lines (106 loc) · 3.83 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import * as ImmutableUtils from './schemas/ImmutableUtils';
import EntitySchema from './schemas/Entity';
import UnionSchema from './schemas/Union';
import ValuesSchema from './schemas/Values';
import ArraySchema, * as ArrayUtils from './schemas/Array';
import ObjectSchema, * as ObjectUtils from './schemas/Object';
const visit = (value, parent, key, schema, addEntity, visitedEntities) => {
if (typeof value !== 'object' || !value) {
return value;
}
if (typeof schema === 'object' && (!schema.normalize || typeof schema.normalize !== 'function')) {
const method = Array.isArray(schema) ? ArrayUtils.normalize : ObjectUtils.normalize;
return method(schema, value, parent, key, visit, addEntity, visitedEntities);
}
return schema.normalize(value, parent, key, visit, addEntity, visitedEntities);
};
const addEntities = (entities) => (schema, processedEntity, value, parent, key) => {
const schemaKey = schema.key;
const id = schema.getId(value, parent, key);
if (!(schemaKey in entities)) {
entities[schemaKey] = {};
}
const existingEntity = entities[schemaKey][id];
if (existingEntity) {
entities[schemaKey][id] = schema.merge(existingEntity, processedEntity);
} else {
entities[schemaKey][id] = processedEntity;
}
};
export const schema = {
Array: ArraySchema,
Entity: EntitySchema,
Object: ObjectSchema,
Union: UnionSchema,
Values: ValuesSchema
};
export const normalize = (input, schema) => {
if (!input || typeof input !== 'object') {
throw new Error(
`Unexpected input given to normalize. Expected type to be "object", found "${
input === null ? 'null' : typeof input
}".`
);
}
const entities = {};
const addEntity = addEntities(entities);
const visitedEntities = {};
const result = visit(input, input, null, schema, addEntity, visitedEntities);
return { entities, result };
};
const unvisitEntity = (id, schema, unvisit, getEntity, cache) => {
let entity = getEntity(id, schema);
if (entity === undefined && schema instanceof EntitySchema) {
entity = schema.fallback(id, schema);
}
if (typeof entity !== 'object' || entity === null) {
return entity;
}
if (!cache[schema.key]) {
cache[schema.key] = {};
}
if (!cache[schema.key][id]) {
// Ensure we don't mutate it non-immutable objects
const entityCopy = ImmutableUtils.isImmutable(entity) ? entity : { ...entity };
// Need to set this first so that if it is referenced further within the
// denormalization the reference will already exist.
cache[schema.key][id] = entityCopy;
cache[schema.key][id] = schema.denormalize(entityCopy, unvisit);
}
return cache[schema.key][id];
};
const getUnvisit = (entities) => {
const cache = {};
const getEntity = getEntities(entities);
return function unvisit(input, schema) {
if (typeof schema === 'object' && (!schema.denormalize || typeof schema.denormalize !== 'function')) {
const method = Array.isArray(schema) ? ArrayUtils.denormalize : ObjectUtils.denormalize;
return method(schema, input, unvisit);
}
if (input === undefined || input === null) {
return input;
}
if (schema instanceof EntitySchema) {
return unvisitEntity(input, schema, unvisit, getEntity, cache);
}
return schema.denormalize(input, unvisit);
};
};
const getEntities = (entities) => {
const isImmutable = ImmutableUtils.isImmutable(entities);
return (entityOrId, schema) => {
const schemaKey = schema.key;
if (typeof entityOrId === 'object') {
return entityOrId;
}
if (isImmutable) {
return entities.getIn([schemaKey, entityOrId.toString()]);
}
return entities[schemaKey] && entities[schemaKey][entityOrId];
};
};
export const denormalize = (input, schema, entities) => {
if (typeof input !== 'undefined') {
return getUnvisit(entities)(input, schema);
}
};