ajoq 0.0.2-0 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.cjs +74 -34
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +26 -8
- package/build/index.js +72 -26
- package/build/index.js.map +1 -1
- package/eslint.config.mjs +79 -0
- package/package.json +17 -2
- package/src/index.ts +99 -42
- package/.prettierrc +0 -7
- package/tsconfig.json +0 -22
package/build/index.cjs
CHANGED
|
@@ -9,22 +9,25 @@ function _export(target, all) {
|
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
_export(exports, {
|
|
12
|
-
|
|
13
|
-
return
|
|
14
|
-
},
|
|
15
|
-
SORT_MAP: function() {
|
|
16
|
-
return SORT_MAP;
|
|
17
|
-
},
|
|
18
|
-
createQuery: function() {
|
|
19
|
-
return createQuery;
|
|
12
|
+
createFilter: function() {
|
|
13
|
+
return createFilter;
|
|
20
14
|
},
|
|
21
15
|
createSort: function() {
|
|
22
16
|
return createSort;
|
|
23
17
|
},
|
|
24
|
-
|
|
25
|
-
return
|
|
18
|
+
stringify: function() {
|
|
19
|
+
return stringify;
|
|
26
20
|
}
|
|
27
21
|
});
|
|
22
|
+
const stringify = (value)=>{
|
|
23
|
+
if (value === undefined) {
|
|
24
|
+
return 'undefined';
|
|
25
|
+
}
|
|
26
|
+
if (value instanceof RegExp) {
|
|
27
|
+
return `/${value.source}/${value.flags}`;
|
|
28
|
+
}
|
|
29
|
+
return JSON.stringify(value);
|
|
30
|
+
};
|
|
28
31
|
const conditions = {
|
|
29
32
|
$eq: (valuePath, value)=>`(${valuePath} === ${value})`,
|
|
30
33
|
$ne: (valuePath, value)=>`(${valuePath} !== ${value})`,
|
|
@@ -32,48 +35,84 @@ const conditions = {
|
|
|
32
35
|
$gte: (valuePath, value)=>`(${valuePath} >= ${value})`,
|
|
33
36
|
$lt: (valuePath, value)=>`(${valuePath} < ${value})`,
|
|
34
37
|
$lte: (valuePath, value)=>`(${valuePath} <= ${value})`,
|
|
35
|
-
$in: (valuePath, value)
|
|
36
|
-
|
|
38
|
+
$in: (valuePath, value, context)=>{
|
|
39
|
+
const name = context.register(`new Set(${value})`);
|
|
40
|
+
return `${name}.has(${valuePath})`;
|
|
41
|
+
},
|
|
42
|
+
$nin: (valuePath, value, context)=>`!${conditions.$in(valuePath, value, context)}`,
|
|
43
|
+
$sub: (valuePath, value, context)=>{
|
|
44
|
+
const name = context.register(`new Set(${value})`);
|
|
45
|
+
return `${valuePath}.every((value) => ${name}.has(value))`;
|
|
46
|
+
},
|
|
47
|
+
$nsub: (valuePath, value, context)=>`!${conditions.$sub(valuePath, value, context)}`,
|
|
48
|
+
$sup: (valuePath, value, context)=>{
|
|
49
|
+
const name = context.register(`new Set(${valuePath})`);
|
|
50
|
+
return `${value}.every((value) => ${name}.has(value))`;
|
|
51
|
+
},
|
|
52
|
+
$nsup: (valuePath, value, context)=>`!${conditions.$sup(valuePath, value, context)}`,
|
|
53
|
+
$con: (valuePath, value)=>`${valuePath}.includes(${value})`,
|
|
54
|
+
$ncon: (valuePath, value)=>`!${valuePath}.includes(${value})`,
|
|
37
55
|
$exists: (valuePath, value)=>`((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,
|
|
38
56
|
$match: (valuePath, value)=>`${value}.test('' + ${valuePath})`,
|
|
39
57
|
$nmatch: (valuePath, value)=>`!${value}.test('' + ${valuePath})`,
|
|
40
58
|
$bits: (valuePath, value)=>`((${valuePath} & ${value}) !== 0)`,
|
|
41
|
-
$nbits: (valuePath, value)=>`((${valuePath} & ${value}) === 0)
|
|
59
|
+
$nbits: (valuePath, value)=>`((${valuePath} & ${value}) === 0)`,
|
|
60
|
+
$typeof: (valuePath, value)=>`(typeof ${valuePath} === ${value})`,
|
|
61
|
+
$ntypeof: (valuePath, value)=>`(typeof ${valuePath} !== ${value})`
|
|
42
62
|
};
|
|
43
63
|
const operations = {
|
|
44
64
|
$and: (value)=>`(${value.join(' && ')})`,
|
|
45
65
|
$or: (value)=>`(${value.join(' || ')})`,
|
|
46
|
-
$nor: (value)
|
|
47
|
-
$not: (value)
|
|
66
|
+
$nor: (value)=>`!(${value.join(' || ')})`,
|
|
67
|
+
$not: (value)=>`!(${value})`
|
|
48
68
|
};
|
|
49
|
-
|
|
69
|
+
class Context {
|
|
70
|
+
counter = 0;
|
|
71
|
+
values = [];
|
|
72
|
+
cacheMap = new Map();
|
|
73
|
+
register(code) {
|
|
74
|
+
if (!this.cacheMap.has(code)) {
|
|
75
|
+
const name = `v$$${this.counter++}`;
|
|
76
|
+
this.values.push(`const ${name} = ${code};`);
|
|
77
|
+
this.cacheMap.set(code, name);
|
|
78
|
+
}
|
|
79
|
+
return this.cacheMap.get(code);
|
|
80
|
+
}
|
|
81
|
+
toString() {
|
|
82
|
+
return this.values.join('\n');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const filterCodeGen = (query, valuePath, context)=>{
|
|
50
86
|
if (typeof query === 'object' && query !== null) {
|
|
51
87
|
const condition = Object.entries(query).map(([key, value])=>{
|
|
52
88
|
if (key in conditions) {
|
|
53
|
-
return conditions[key](valuePath,
|
|
89
|
+
return conditions[key](valuePath, stringify(value), context);
|
|
54
90
|
}
|
|
55
91
|
if (key in operations) {
|
|
56
92
|
const values = Array.isArray(value) ? value : [
|
|
57
93
|
value
|
|
58
94
|
];
|
|
59
|
-
return operations[key](values.map((
|
|
95
|
+
return operations[key](values.map((v)=>filterCodeGen(v, valuePath, context)));
|
|
60
96
|
}
|
|
61
|
-
const valueSubPath = `${valuePath}?.[${
|
|
97
|
+
const valueSubPath = `${valuePath}?.[${stringify(key)}]`;
|
|
62
98
|
if (value instanceof RegExp) {
|
|
63
|
-
return conditions.$match(valueSubPath, value.toString());
|
|
99
|
+
return conditions.$match(valueSubPath, value.toString(), context);
|
|
64
100
|
}
|
|
65
101
|
if (typeof value === 'object' && value !== null) {
|
|
66
|
-
return
|
|
102
|
+
return filterCodeGen(value, valueSubPath, context);
|
|
67
103
|
}
|
|
68
|
-
return conditions.$eq(valueSubPath, JSON.stringify(value));
|
|
104
|
+
return conditions.$eq(valueSubPath, JSON.stringify(value), context);
|
|
69
105
|
});
|
|
70
|
-
|
|
106
|
+
if (condition.length > 0) {
|
|
107
|
+
return `${condition.join(' && ')}`;
|
|
108
|
+
}
|
|
71
109
|
}
|
|
72
110
|
return 'true';
|
|
73
111
|
};
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
112
|
+
const createFilter = (query)=>{
|
|
113
|
+
const context = new Context();
|
|
114
|
+
const code = filterCodeGen(query, 'data', context);
|
|
115
|
+
const filter = new Function('data', `${context}\nreturn ${code};`);
|
|
77
116
|
return (data)=>filter(data);
|
|
78
117
|
};
|
|
79
118
|
const SORT_EXPR = /^(asc|desc|1|-1)$/i;
|
|
@@ -83,7 +122,7 @@ const SORT_MAP = {
|
|
|
83
122
|
1: 1,
|
|
84
123
|
'-1': -1
|
|
85
124
|
};
|
|
86
|
-
const sortCodeGen = (sort, valuePath
|
|
125
|
+
const sortCodeGen = (sort, valuePath)=>{
|
|
87
126
|
if (typeof sort === 'object' && sort !== null) {
|
|
88
127
|
const condition = Object.entries(sort).map(([key, value])=>{
|
|
89
128
|
const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;
|
|
@@ -91,9 +130,11 @@ const sortCodeGen = (sort, valuePath, path)=>{
|
|
|
91
130
|
return sortCodeGen(value, [
|
|
92
131
|
valueSubPathA,
|
|
93
132
|
valueSubPathB
|
|
94
|
-
]
|
|
133
|
+
]);
|
|
95
134
|
});
|
|
96
|
-
|
|
135
|
+
if (condition.length > 0) {
|
|
136
|
+
return condition.join(' || ');
|
|
137
|
+
}
|
|
97
138
|
}
|
|
98
139
|
if (typeof sort === 'string' && SORT_EXPR.test(sort)) {
|
|
99
140
|
return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort]})`;
|
|
@@ -103,14 +144,13 @@ const sortCodeGen = (sort, valuePath, path)=>{
|
|
|
103
144
|
}
|
|
104
145
|
return `0`;
|
|
105
146
|
};
|
|
106
|
-
const createSort = (sort
|
|
147
|
+
const createSort = (sort)=>{
|
|
107
148
|
const code = sortCodeGen(sort, [
|
|
108
149
|
'a',
|
|
109
150
|
'b'
|
|
110
|
-
]
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
return (data)=>filter(data);
|
|
151
|
+
]);
|
|
152
|
+
const sortFn = new Function('a', 'b', `return ${code};`);
|
|
153
|
+
return (a, b)=>sortFn(a, b);
|
|
114
154
|
};
|
|
115
155
|
|
|
116
156
|
//# sourceMappingURL=index.cjs.map
|
package/build/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type Query<T> = {\n [P in keyof T]?: QueryCondition<T[P]>;\n} & Operations<T>;\n\ntype QueryCondition<FieldType> =\n | FieldType\n | Conditions<FieldType>;\n\ninterface Conditions<ValueType> {\n $eq?: ValueType;\n $ne?: ValueType;\n $gt?: ValueType;\n $gte?: ValueType;\n $lt?: ValueType;\n $lte?: ValueType;\n $in?: ValueType[];\n $nin?: ValueType[];\n $exists?: boolean;\n $match?: RegExp | string;\n $nmatch?: RegExp | string;\n $bits?: number;\n $nbits?: number;\n};\n\ntype ConditionKeys = keyof Conditions<unknown>;\n\ninterface Operations<T> {\n $and?: Query<T>[];\n $or?: Query<T>[];\n $nor?: Query<T>[];\n $not?: Query<T>;\n};\n\ntype OperationKeys = keyof Operations<unknown>;\n\nconst conditions: Record<ConditionKeys, any> = {\n $eq: (valuePath: string, value: unknown) => `(${valuePath} === ${value})`,\n $ne: (valuePath: string, value: unknown) => `(${valuePath} !== ${value})`,\n $gt: (valuePath: string, value: unknown) => `(${valuePath} > ${value})`,\n $gte: (valuePath: string, value: unknown) => `(${valuePath} >= ${value})`,\n $lt: (valuePath: string, value: unknown) => `(${valuePath} < ${value})`,\n $lte: (valuePath: string, value: unknown) => `(${valuePath} <= ${value})`,\n $in: (valuePath: string, value: unknown) => `(${value}.includes(${valuePath}))`,\n $nin: (valuePath: string, value: unknown) => `(!${value}.includes(${valuePath}))`,\n $exists: (valuePath: string, value: unknown) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,\n $match: (valuePath: string, value: unknown) => `${value}.test('' + ${valuePath})`,\n $nmatch: (valuePath: string, value: unknown) => `!${value}.test('' + ${valuePath})`,\n $bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,\n $nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,\n};\n\nconst operations: Record<keyof Operations<unknown>, any> = {\n $and: (value: unknown[]) => `(${value.join(' && ')})`,\n $or: (value: unknown[]) => `(${value.join(' || ')})`,\n $nor: (value: unknown[]) => `(!(${value.join(' || ')}))`,\n $not: (value: unknown) => `(!(${value}))`,\n}\n\n\nconst queryCodeGen = <T extends object>(query: Query<T>, valuePath: string, path: string): string => {\n if (typeof query === 'object' && query !== null) {\n const condition = Object.entries(query).map(([key, value]) => {\n if (key in conditions) {\n return conditions[key as ConditionKeys](valuePath, JSON.stringify(value));\n }\n if (key in operations) {\n const values = Array.isArray(value) ? value : [value];\n return operations[key as OperationKeys](values.map((value) => queryCodeGen(value, valuePath, path)));\n }\n const valueSubPath = `${valuePath}?.[${JSON.stringify(key)}]`;\n if (value instanceof RegExp) {\n return conditions.$match(valueSubPath, value.toString());\n }\n if (typeof value === 'object' && value !== null) {\n return queryCodeGen(value, valueSubPath, path);\n }\n return conditions.$eq(valueSubPath, JSON.stringify(value));\n });\n\n return `${condition.join(' && ')}`;\n }\n return 'true';\n}\n\nexport const createQuery = <T extends object>(query: Query<T>, rootName: string) => {\n const code = queryCodeGen(query, 'data', rootName);\n const filter = new Function('data', `return ${code || 'true'};`);\n return (data: T) => filter(data);\n};\n\nexport type SortType = 'asc' | 'desc' | 1 | -1;\n\nexport type Sort<T> = T extends object\n ? {\n [K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;\n }\n : SortType;\n\nexport const SORT_EXPR = /^(asc|desc|1|-1)$/i\n\nexport const SORT_MAP: Record<SortType, 1 | -1> = {\n asc: 1,\n desc: -1,\n 1: 1,\n '-1': -1,\n};\n\nexport const sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string], path: string): string => {\n if (typeof sort === 'object' && sort !== null) {\n const condition = Object.entries(sort).map(([key, value]) => {\n const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;\n const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;\n return sortCodeGen(value, [valueSubPathA, valueSubPathB], path);\n });\n return condition.join(' || ');\n }\n if (typeof sort === 'string' && SORT_EXPR.test(sort)) {\n return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n if (typeof sort === 'number' && SORT_EXPR.test('' + sort)) {\n return `((${valuePath[0]} - ${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n return `0`;\n};\n\nexport const createSort = <T>(sort: Sort<T>, rootName: string) => {\n const code = sortCodeGen(sort, ['a', 'b'], rootName);\n const filter = new Function('a', 'b', `return ${code || 0};`);\n console.log(filter.toString());\n return (data: T) => filter(data);\n};\n"],"names":["SORT_EXPR","SORT_MAP","createQuery","createSort","sortCodeGen","conditions","$eq","valuePath","value","$ne","$gt","$gte","$lt","$lte","$in","$nin","$exists","$match","$nmatch","$bits","$nbits","operations","$and","join","$or","$nor","$not","queryCodeGen","query","path","condition","Object","entries","map","key","JSON","stringify","values","Array","isArray","valueSubPath","RegExp","toString","rootName","code","filter","Function","data","asc","desc","sort","valueSubPathA","valueSubPathB","test","console","log"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type Query<T> = {\n [P in keyof T]?: QueryCondition<T[P]>;\n} & Operations<T>;\n\nexport type QueryCondition<FieldType> = FieldType | Conditions<FieldType> | Operations<QueryCondition<FieldType>>;\n\nexport interface Conditions<ValueType> {\n $eq?: ValueType;\n $ne?: ValueType;\n $gt?: ValueType;\n $gte?: ValueType;\n $lt?: ValueType;\n $lte?: ValueType;\n $in?: ValueType[];\n $nin?: ValueType[];\n $sub?: ValueType[];\n $nsub?: ValueType[];\n $sup?: ValueType[];\n $nsup?: ValueType[];\n $con?: ValueType;\n $ncon?: ValueType;\n $exists?: boolean;\n $match?: RegExp | string;\n $nmatch?: RegExp | string;\n $bits?: number;\n $nbits?: number;\n $typeof: string;\n $ntypeof: string;\n}\n\nexport type ConditionKeys = keyof Conditions<unknown>;\n\nexport interface Operations<T> {\n $and?: Query<T>[];\n $or?: Query<T>[];\n $nor?: Query<T>[];\n $not?: Query<T>;\n}\n\nexport type OperationKeys = keyof Operations<unknown>;\n\nexport const stringify = (value: unknown) => {\n if (value === undefined) {\n return 'undefined';\n }\n if (value instanceof RegExp) {\n return `/${value.source}/${value.flags}`;\n }\n return JSON.stringify(value);\n};\n\nexport interface Condition {\n (valuePath: string, value: any, context: Context): string;\n}\n\nconst conditions: Record<ConditionKeys, Condition> = {\n $eq: (valuePath: string, value: unknown) => `(${valuePath} === ${value})`,\n $ne: (valuePath: string, value: unknown) => `(${valuePath} !== ${value})`,\n $gt: (valuePath: string, value: unknown) => `(${valuePath} > ${value})`,\n $gte: (valuePath: string, value: unknown) => `(${valuePath} >= ${value})`,\n $lt: (valuePath: string, value: unknown) => `(${valuePath} < ${value})`,\n $lte: (valuePath: string, value: unknown) => `(${valuePath} <= ${value})`,\n $in: (valuePath: string, value: unknown, context: Context) => {\n const name = context.register(`new Set(${value})`);\n return `${name}.has(${valuePath})`;\n },\n $nin: (valuePath: string, value: unknown, context: Context) => `!${conditions.$in(valuePath, value, context)}`,\n $sub: (valuePath: string, value: unknown, context: Context) => {\n const name = context.register(`new Set(${value})`);\n return `${valuePath}.every((value) => ${name}.has(value))`;\n },\n $nsub: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sub(valuePath, value, context)}`,\n $sup: (valuePath: string, value: unknown, context: Context) => {\n const name = context.register(`new Set(${valuePath})`);\n return `${value}.every((value) => ${name}.has(value))`;\n },\n $nsup: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sup(valuePath, value, context)}`,\n $con: (valuePath: string, value: unknown) => `${valuePath}.includes(${value})`,\n $ncon: (valuePath: string, value: unknown) => `!${valuePath}.includes(${value})`,\n $exists: (valuePath: string, value: boolean) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,\n $match: (valuePath: string, value: RegExp) => `${value}.test('' + ${valuePath})`,\n $nmatch: (valuePath: string, value: RegExp) => `!${value}.test('' + ${valuePath})`,\n $bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,\n $nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,\n $typeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} === ${value})`,\n $ntypeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} !== ${value})`,\n};\n\nconst operations: Record<keyof Operations<unknown>, any> = {\n $and: (value: unknown[]) => `(${value.join(' && ')})`,\n $or: (value: unknown[]) => `(${value.join(' || ')})`,\n $nor: (value: unknown[]) => `!(${value.join(' || ')})`,\n $not: (value: unknown[]) => `!(${value})`,\n};\n\nclass Context {\n private counter = 0;\n private values: string[] = [];\n private cacheMap = new Map<string, string>();\n\n register(code: string) {\n if (!this.cacheMap.has(code)) {\n const name = `v$$${this.counter++}`;\n this.values.push(`const ${name} = ${code};`);\n this.cacheMap.set(code, name);\n }\n return this.cacheMap.get(code)!;\n }\n toString() {\n return this.values.join('\\n');\n }\n}\n\nconst filterCodeGen = <T extends object>(query: Query<T> | undefined, valuePath: string, context: Context): string => {\n if (typeof query === 'object' && query !== null) {\n const condition = Object.entries(query).map(([key, value]) => {\n if (key in conditions) {\n return conditions[key as ConditionKeys](valuePath, stringify(value), context);\n }\n if (key in operations) {\n const values = Array.isArray(value) ? value : [value];\n return operations[key as OperationKeys](values.map((v) => filterCodeGen(v, valuePath, context)));\n }\n const valueSubPath = `${valuePath}?.[${stringify(key)}]`;\n if (value instanceof RegExp) {\n return conditions.$match(valueSubPath, value.toString(), context);\n }\n if (typeof value === 'object' && value !== null) {\n return filterCodeGen(value, valueSubPath, context);\n }\n return conditions.$eq(valueSubPath, JSON.stringify(value), context);\n });\n if (condition.length > 0) {\n return `${condition.join(' && ')}`;\n }\n }\n return 'true';\n};\n\nexport const createFilter = <T extends object>(query?: Query<T>) => {\n const context = new Context();\n const code = filterCodeGen(query, 'data', context);\n const filter = new Function('data', `${context}\\nreturn ${code};`);\n return (data: T) => filter(data);\n};\n\nexport type SortType = 'asc' | 'desc' | 1 | -1;\n\nexport type Sort<T> = T extends object\n ? {\n [K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;\n }\n : SortType;\n\nconst SORT_EXPR = /^(asc|desc|1|-1)$/i;\n\nconst SORT_MAP: Record<SortType, 1 | -1> = {\n asc: 1,\n desc: -1,\n 1: 1,\n '-1': -1,\n};\n\nconst sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string]): string => {\n if (typeof sort === 'object' && sort !== null) {\n const condition = Object.entries(sort).map(([key, value]) => {\n const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;\n const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;\n return sortCodeGen(value, [valueSubPathA, valueSubPathB]);\n });\n if (condition.length > 0) {\n return condition.join(' || ');\n }\n }\n if (typeof sort === 'string' && SORT_EXPR.test(sort)) {\n return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n if (typeof sort === 'number' && SORT_EXPR.test('' + sort)) {\n return `((${valuePath[0]} - ${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n return `0`;\n};\n\nexport const createSort = <T>(sort: Sort<T>) => {\n const code = sortCodeGen(sort, ['a', 'b']);\n const sortFn = new Function('a', 'b', `return ${code};`);\n return (a: T, b: T) => sortFn(a, b);\n};\n"],"names":["createFilter","createSort","stringify","value","undefined","RegExp","source","flags","JSON","conditions","$eq","valuePath","$ne","$gt","$gte","$lt","$lte","$in","context","name","register","$nin","$sub","$nsub","$sup","$nsup","$con","$ncon","$exists","$match","$nmatch","$bits","$nbits","$typeof","$ntypeof","operations","$and","join","$or","$nor","$not","Context","counter","values","cacheMap","Map","code","has","push","set","get","toString","filterCodeGen","query","condition","Object","entries","map","key","Array","isArray","v","valueSubPath","length","filter","Function","data","SORT_EXPR","SORT_MAP","asc","desc","sortCodeGen","sort","valueSubPathA","valueSubPathB","test","sortFn","a","b"],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export type Query<T> = {
|
|
2
2
|
[P in keyof T]?: QueryCondition<T[P]>;
|
|
3
3
|
} & Operations<T>;
|
|
4
|
-
type QueryCondition<FieldType> = FieldType | Conditions<FieldType
|
|
5
|
-
interface Conditions<ValueType> {
|
|
4
|
+
export type QueryCondition<FieldType> = FieldType | Conditions<FieldType> | Operations<QueryCondition<FieldType>>;
|
|
5
|
+
export interface Conditions<ValueType> {
|
|
6
6
|
$eq?: ValueType;
|
|
7
7
|
$ne?: ValueType;
|
|
8
8
|
$gt?: ValueType;
|
|
@@ -11,25 +11,43 @@ interface Conditions<ValueType> {
|
|
|
11
11
|
$lte?: ValueType;
|
|
12
12
|
$in?: ValueType[];
|
|
13
13
|
$nin?: ValueType[];
|
|
14
|
+
$sub?: ValueType[];
|
|
15
|
+
$nsub?: ValueType[];
|
|
16
|
+
$sup?: ValueType[];
|
|
17
|
+
$nsup?: ValueType[];
|
|
18
|
+
$con?: ValueType;
|
|
19
|
+
$ncon?: ValueType;
|
|
14
20
|
$exists?: boolean;
|
|
15
21
|
$match?: RegExp | string;
|
|
16
22
|
$nmatch?: RegExp | string;
|
|
17
23
|
$bits?: number;
|
|
18
24
|
$nbits?: number;
|
|
25
|
+
$typeof: string;
|
|
26
|
+
$ntypeof: string;
|
|
19
27
|
}
|
|
20
|
-
|
|
28
|
+
export type ConditionKeys = keyof Conditions<unknown>;
|
|
29
|
+
export interface Operations<T> {
|
|
21
30
|
$and?: Query<T>[];
|
|
22
31
|
$or?: Query<T>[];
|
|
23
32
|
$nor?: Query<T>[];
|
|
24
33
|
$not?: Query<T>;
|
|
25
34
|
}
|
|
26
|
-
export
|
|
35
|
+
export type OperationKeys = keyof Operations<unknown>;
|
|
36
|
+
export declare const stringify: (value: unknown) => string;
|
|
37
|
+
export interface Condition {
|
|
38
|
+
(valuePath: string, value: any, context: Context): string;
|
|
39
|
+
}
|
|
40
|
+
declare class Context {
|
|
41
|
+
private counter;
|
|
42
|
+
private values;
|
|
43
|
+
private cacheMap;
|
|
44
|
+
register(code: string): string;
|
|
45
|
+
toString(): string;
|
|
46
|
+
}
|
|
47
|
+
export declare const createFilter: <T extends object>(query?: Query<T>) => (data: T) => any;
|
|
27
48
|
export type SortType = 'asc' | 'desc' | 1 | -1;
|
|
28
49
|
export type Sort<T> = T extends object ? {
|
|
29
50
|
[K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;
|
|
30
51
|
} : SortType;
|
|
31
|
-
export declare const
|
|
32
|
-
export declare const SORT_MAP: Record<SortType, 1 | -1>;
|
|
33
|
-
export declare const sortCodeGen: <T>(sort: Sort<T>, valuePath: [string, string], path: string) => string;
|
|
34
|
-
export declare const createSort: <T>(sort: Sort<T>, rootName: string) => (data: T) => any;
|
|
52
|
+
export declare const createSort: <T>(sort: Sort<T>) => (a: T, b: T) => any;
|
|
35
53
|
export {};
|
package/build/index.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
export const stringify = (value)=>{
|
|
2
|
+
if (value === undefined) {
|
|
3
|
+
return 'undefined';
|
|
4
|
+
}
|
|
5
|
+
if (value instanceof RegExp) {
|
|
6
|
+
return `/${value.source}/${value.flags}`;
|
|
7
|
+
}
|
|
8
|
+
return JSON.stringify(value);
|
|
9
|
+
};
|
|
1
10
|
const conditions = {
|
|
2
11
|
$eq: (valuePath, value)=>`(${valuePath} === ${value})`,
|
|
3
12
|
$ne: (valuePath, value)=>`(${valuePath} !== ${value})`,
|
|
@@ -5,58 +14,94 @@ const conditions = {
|
|
|
5
14
|
$gte: (valuePath, value)=>`(${valuePath} >= ${value})`,
|
|
6
15
|
$lt: (valuePath, value)=>`(${valuePath} < ${value})`,
|
|
7
16
|
$lte: (valuePath, value)=>`(${valuePath} <= ${value})`,
|
|
8
|
-
$in: (valuePath, value)
|
|
9
|
-
|
|
17
|
+
$in: (valuePath, value, context)=>{
|
|
18
|
+
const name = context.register(`new Set(${value})`);
|
|
19
|
+
return `${name}.has(${valuePath})`;
|
|
20
|
+
},
|
|
21
|
+
$nin: (valuePath, value, context)=>`!${conditions.$in(valuePath, value, context)}`,
|
|
22
|
+
$sub: (valuePath, value, context)=>{
|
|
23
|
+
const name = context.register(`new Set(${value})`);
|
|
24
|
+
return `${valuePath}.every((value) => ${name}.has(value))`;
|
|
25
|
+
},
|
|
26
|
+
$nsub: (valuePath, value, context)=>`!${conditions.$sub(valuePath, value, context)}`,
|
|
27
|
+
$sup: (valuePath, value, context)=>{
|
|
28
|
+
const name = context.register(`new Set(${valuePath})`);
|
|
29
|
+
return `${value}.every((value) => ${name}.has(value))`;
|
|
30
|
+
},
|
|
31
|
+
$nsup: (valuePath, value, context)=>`!${conditions.$sup(valuePath, value, context)}`,
|
|
32
|
+
$con: (valuePath, value)=>`${valuePath}.includes(${value})`,
|
|
33
|
+
$ncon: (valuePath, value)=>`!${valuePath}.includes(${value})`,
|
|
10
34
|
$exists: (valuePath, value)=>`((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,
|
|
11
35
|
$match: (valuePath, value)=>`${value}.test('' + ${valuePath})`,
|
|
12
36
|
$nmatch: (valuePath, value)=>`!${value}.test('' + ${valuePath})`,
|
|
13
37
|
$bits: (valuePath, value)=>`((${valuePath} & ${value}) !== 0)`,
|
|
14
|
-
$nbits: (valuePath, value)=>`((${valuePath} & ${value}) === 0)
|
|
38
|
+
$nbits: (valuePath, value)=>`((${valuePath} & ${value}) === 0)`,
|
|
39
|
+
$typeof: (valuePath, value)=>`(typeof ${valuePath} === ${value})`,
|
|
40
|
+
$ntypeof: (valuePath, value)=>`(typeof ${valuePath} !== ${value})`
|
|
15
41
|
};
|
|
16
42
|
const operations = {
|
|
17
43
|
$and: (value)=>`(${value.join(' && ')})`,
|
|
18
44
|
$or: (value)=>`(${value.join(' || ')})`,
|
|
19
|
-
$nor: (value)
|
|
20
|
-
$not: (value)
|
|
45
|
+
$nor: (value)=>`!(${value.join(' || ')})`,
|
|
46
|
+
$not: (value)=>`!(${value})`
|
|
21
47
|
};
|
|
22
|
-
|
|
48
|
+
class Context {
|
|
49
|
+
counter = 0;
|
|
50
|
+
values = [];
|
|
51
|
+
cacheMap = new Map();
|
|
52
|
+
register(code) {
|
|
53
|
+
if (!this.cacheMap.has(code)) {
|
|
54
|
+
const name = `v$$${this.counter++}`;
|
|
55
|
+
this.values.push(`const ${name} = ${code};`);
|
|
56
|
+
this.cacheMap.set(code, name);
|
|
57
|
+
}
|
|
58
|
+
return this.cacheMap.get(code);
|
|
59
|
+
}
|
|
60
|
+
toString() {
|
|
61
|
+
return this.values.join('\n');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const filterCodeGen = (query, valuePath, context)=>{
|
|
23
65
|
if (typeof query === 'object' && query !== null) {
|
|
24
66
|
const condition = Object.entries(query).map(([key, value])=>{
|
|
25
67
|
if (key in conditions) {
|
|
26
|
-
return conditions[key](valuePath,
|
|
68
|
+
return conditions[key](valuePath, stringify(value), context);
|
|
27
69
|
}
|
|
28
70
|
if (key in operations) {
|
|
29
71
|
const values = Array.isArray(value) ? value : [
|
|
30
72
|
value
|
|
31
73
|
];
|
|
32
|
-
return operations[key](values.map((
|
|
74
|
+
return operations[key](values.map((v)=>filterCodeGen(v, valuePath, context)));
|
|
33
75
|
}
|
|
34
|
-
const valueSubPath = `${valuePath}?.[${
|
|
76
|
+
const valueSubPath = `${valuePath}?.[${stringify(key)}]`;
|
|
35
77
|
if (value instanceof RegExp) {
|
|
36
|
-
return conditions.$match(valueSubPath, value.toString());
|
|
78
|
+
return conditions.$match(valueSubPath, value.toString(), context);
|
|
37
79
|
}
|
|
38
80
|
if (typeof value === 'object' && value !== null) {
|
|
39
|
-
return
|
|
81
|
+
return filterCodeGen(value, valueSubPath, context);
|
|
40
82
|
}
|
|
41
|
-
return conditions.$eq(valueSubPath, JSON.stringify(value));
|
|
83
|
+
return conditions.$eq(valueSubPath, JSON.stringify(value), context);
|
|
42
84
|
});
|
|
43
|
-
|
|
85
|
+
if (condition.length > 0) {
|
|
86
|
+
return `${condition.join(' && ')}`;
|
|
87
|
+
}
|
|
44
88
|
}
|
|
45
89
|
return 'true';
|
|
46
90
|
};
|
|
47
|
-
export const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
91
|
+
export const createFilter = (query)=>{
|
|
92
|
+
const context = new Context();
|
|
93
|
+
const code = filterCodeGen(query, 'data', context);
|
|
94
|
+
const filter = new Function('data', `${context}\nreturn ${code};`);
|
|
50
95
|
return (data)=>filter(data);
|
|
51
96
|
};
|
|
52
|
-
|
|
53
|
-
|
|
97
|
+
const SORT_EXPR = /^(asc|desc|1|-1)$/i;
|
|
98
|
+
const SORT_MAP = {
|
|
54
99
|
asc: 1,
|
|
55
100
|
desc: -1,
|
|
56
101
|
1: 1,
|
|
57
102
|
'-1': -1
|
|
58
103
|
};
|
|
59
|
-
|
|
104
|
+
const sortCodeGen = (sort, valuePath)=>{
|
|
60
105
|
if (typeof sort === 'object' && sort !== null) {
|
|
61
106
|
const condition = Object.entries(sort).map(([key, value])=>{
|
|
62
107
|
const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;
|
|
@@ -64,9 +109,11 @@ export const sortCodeGen = (sort, valuePath, path)=>{
|
|
|
64
109
|
return sortCodeGen(value, [
|
|
65
110
|
valueSubPathA,
|
|
66
111
|
valueSubPathB
|
|
67
|
-
]
|
|
112
|
+
]);
|
|
68
113
|
});
|
|
69
|
-
|
|
114
|
+
if (condition.length > 0) {
|
|
115
|
+
return condition.join(' || ');
|
|
116
|
+
}
|
|
70
117
|
}
|
|
71
118
|
if (typeof sort === 'string' && SORT_EXPR.test(sort)) {
|
|
72
119
|
return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort]})`;
|
|
@@ -76,14 +123,13 @@ export const sortCodeGen = (sort, valuePath, path)=>{
|
|
|
76
123
|
}
|
|
77
124
|
return `0`;
|
|
78
125
|
};
|
|
79
|
-
export const createSort = (sort
|
|
126
|
+
export const createSort = (sort)=>{
|
|
80
127
|
const code = sortCodeGen(sort, [
|
|
81
128
|
'a',
|
|
82
129
|
'b'
|
|
83
|
-
]
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
return (data)=>filter(data);
|
|
130
|
+
]);
|
|
131
|
+
const sortFn = new Function('a', 'b', `return ${code};`);
|
|
132
|
+
return (a, b)=>sortFn(a, b);
|
|
87
133
|
};
|
|
88
134
|
|
|
89
135
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type Query<T> = {\n [P in keyof T]?: QueryCondition<T[P]>;\n} & Operations<T>;\n\ntype QueryCondition<FieldType> =\n | FieldType\n | Conditions<FieldType>;\n\ninterface Conditions<ValueType> {\n $eq?: ValueType;\n $ne?: ValueType;\n $gt?: ValueType;\n $gte?: ValueType;\n $lt?: ValueType;\n $lte?: ValueType;\n $in?: ValueType[];\n $nin?: ValueType[];\n $exists?: boolean;\n $match?: RegExp | string;\n $nmatch?: RegExp | string;\n $bits?: number;\n $nbits?: number;\n};\n\ntype ConditionKeys = keyof Conditions<unknown>;\n\ninterface Operations<T> {\n $and?: Query<T>[];\n $or?: Query<T>[];\n $nor?: Query<T>[];\n $not?: Query<T>;\n};\n\ntype OperationKeys = keyof Operations<unknown>;\n\nconst conditions: Record<ConditionKeys, any> = {\n $eq: (valuePath: string, value: unknown) => `(${valuePath} === ${value})`,\n $ne: (valuePath: string, value: unknown) => `(${valuePath} !== ${value})`,\n $gt: (valuePath: string, value: unknown) => `(${valuePath} > ${value})`,\n $gte: (valuePath: string, value: unknown) => `(${valuePath} >= ${value})`,\n $lt: (valuePath: string, value: unknown) => `(${valuePath} < ${value})`,\n $lte: (valuePath: string, value: unknown) => `(${valuePath} <= ${value})`,\n $in: (valuePath: string, value: unknown) => `(${value}.includes(${valuePath}))`,\n $nin: (valuePath: string, value: unknown) => `(!${value}.includes(${valuePath}))`,\n $exists: (valuePath: string, value: unknown) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,\n $match: (valuePath: string, value: unknown) => `${value}.test('' + ${valuePath})`,\n $nmatch: (valuePath: string, value: unknown) => `!${value}.test('' + ${valuePath})`,\n $bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,\n $nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,\n};\n\nconst operations: Record<keyof Operations<unknown>, any> = {\n $and: (value: unknown[]) => `(${value.join(' && ')})`,\n $or: (value: unknown[]) => `(${value.join(' || ')})`,\n $nor: (value: unknown[]) => `(!(${value.join(' || ')}))`,\n $not: (value: unknown) => `(!(${value}))`,\n}\n\n\nconst queryCodeGen = <T extends object>(query: Query<T>, valuePath: string, path: string): string => {\n if (typeof query === 'object' && query !== null) {\n const condition = Object.entries(query).map(([key, value]) => {\n if (key in conditions) {\n return conditions[key as ConditionKeys](valuePath, JSON.stringify(value));\n }\n if (key in operations) {\n const values = Array.isArray(value) ? value : [value];\n return operations[key as OperationKeys](values.map((value) => queryCodeGen(value, valuePath, path)));\n }\n const valueSubPath = `${valuePath}?.[${JSON.stringify(key)}]`;\n if (value instanceof RegExp) {\n return conditions.$match(valueSubPath, value.toString());\n }\n if (typeof value === 'object' && value !== null) {\n return queryCodeGen(value, valueSubPath, path);\n }\n return conditions.$eq(valueSubPath, JSON.stringify(value));\n });\n\n return `${condition.join(' && ')}`;\n }\n return 'true';\n}\n\nexport const createQuery = <T extends object>(query: Query<T>, rootName: string) => {\n const code = queryCodeGen(query, 'data', rootName);\n const filter = new Function('data', `return ${code || 'true'};`);\n return (data: T) => filter(data);\n};\n\nexport type SortType = 'asc' | 'desc' | 1 | -1;\n\nexport type Sort<T> = T extends object\n ? {\n [K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;\n }\n : SortType;\n\nexport const SORT_EXPR = /^(asc|desc|1|-1)$/i\n\nexport const SORT_MAP: Record<SortType, 1 | -1> = {\n asc: 1,\n desc: -1,\n 1: 1,\n '-1': -1,\n};\n\nexport const sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string], path: string): string => {\n if (typeof sort === 'object' && sort !== null) {\n const condition = Object.entries(sort).map(([key, value]) => {\n const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;\n const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;\n return sortCodeGen(value, [valueSubPathA, valueSubPathB], path);\n });\n return condition.join(' || ');\n }\n if (typeof sort === 'string' && SORT_EXPR.test(sort)) {\n return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n if (typeof sort === 'number' && SORT_EXPR.test('' + sort)) {\n return `((${valuePath[0]} - ${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n return `0`;\n};\n\nexport const createSort = <T>(sort: Sort<T>, rootName: string) => {\n const code = sortCodeGen(sort, ['a', 'b'], rootName);\n const filter = new Function('a', 'b', `return ${code || 0};`);\n console.log(filter.toString());\n return (data: T) => filter(data);\n};\n"],"names":["conditions","$eq","valuePath","value","$ne","$gt","$gte","$lt","$lte","$in","$nin","$exists","$match","$nmatch","$bits","$nbits","operations","$and","join","$or","$nor","$not","queryCodeGen","query","path","condition","Object","entries","map","key","JSON","stringify","values","Array","isArray","valueSubPath","RegExp","toString","createQuery","rootName","code","filter","Function","data","SORT_EXPR","SORT_MAP","asc","desc","sortCodeGen","sort","valueSubPathA","valueSubPathB","test","createSort","console","log"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type Query<T> = {\n [P in keyof T]?: QueryCondition<T[P]>;\n} & Operations<T>;\n\nexport type QueryCondition<FieldType> = FieldType | Conditions<FieldType> | Operations<QueryCondition<FieldType>>;\n\nexport interface Conditions<ValueType> {\n $eq?: ValueType;\n $ne?: ValueType;\n $gt?: ValueType;\n $gte?: ValueType;\n $lt?: ValueType;\n $lte?: ValueType;\n $in?: ValueType[];\n $nin?: ValueType[];\n $sub?: ValueType[];\n $nsub?: ValueType[];\n $sup?: ValueType[];\n $nsup?: ValueType[];\n $con?: ValueType;\n $ncon?: ValueType;\n $exists?: boolean;\n $match?: RegExp | string;\n $nmatch?: RegExp | string;\n $bits?: number;\n $nbits?: number;\n $typeof: string;\n $ntypeof: string;\n}\n\nexport type ConditionKeys = keyof Conditions<unknown>;\n\nexport interface Operations<T> {\n $and?: Query<T>[];\n $or?: Query<T>[];\n $nor?: Query<T>[];\n $not?: Query<T>;\n}\n\nexport type OperationKeys = keyof Operations<unknown>;\n\nexport const stringify = (value: unknown) => {\n if (value === undefined) {\n return 'undefined';\n }\n if (value instanceof RegExp) {\n return `/${value.source}/${value.flags}`;\n }\n return JSON.stringify(value);\n};\n\nexport interface Condition {\n (valuePath: string, value: any, context: Context): string;\n}\n\nconst conditions: Record<ConditionKeys, Condition> = {\n $eq: (valuePath: string, value: unknown) => `(${valuePath} === ${value})`,\n $ne: (valuePath: string, value: unknown) => `(${valuePath} !== ${value})`,\n $gt: (valuePath: string, value: unknown) => `(${valuePath} > ${value})`,\n $gte: (valuePath: string, value: unknown) => `(${valuePath} >= ${value})`,\n $lt: (valuePath: string, value: unknown) => `(${valuePath} < ${value})`,\n $lte: (valuePath: string, value: unknown) => `(${valuePath} <= ${value})`,\n $in: (valuePath: string, value: unknown, context: Context) => {\n const name = context.register(`new Set(${value})`);\n return `${name}.has(${valuePath})`;\n },\n $nin: (valuePath: string, value: unknown, context: Context) => `!${conditions.$in(valuePath, value, context)}`,\n $sub: (valuePath: string, value: unknown, context: Context) => {\n const name = context.register(`new Set(${value})`);\n return `${valuePath}.every((value) => ${name}.has(value))`;\n },\n $nsub: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sub(valuePath, value, context)}`,\n $sup: (valuePath: string, value: unknown, context: Context) => {\n const name = context.register(`new Set(${valuePath})`);\n return `${value}.every((value) => ${name}.has(value))`;\n },\n $nsup: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sup(valuePath, value, context)}`,\n $con: (valuePath: string, value: unknown) => `${valuePath}.includes(${value})`,\n $ncon: (valuePath: string, value: unknown) => `!${valuePath}.includes(${value})`,\n $exists: (valuePath: string, value: boolean) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,\n $match: (valuePath: string, value: RegExp) => `${value}.test('' + ${valuePath})`,\n $nmatch: (valuePath: string, value: RegExp) => `!${value}.test('' + ${valuePath})`,\n $bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,\n $nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,\n $typeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} === ${value})`,\n $ntypeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} !== ${value})`,\n};\n\nconst operations: Record<keyof Operations<unknown>, any> = {\n $and: (value: unknown[]) => `(${value.join(' && ')})`,\n $or: (value: unknown[]) => `(${value.join(' || ')})`,\n $nor: (value: unknown[]) => `!(${value.join(' || ')})`,\n $not: (value: unknown[]) => `!(${value})`,\n};\n\nclass Context {\n private counter = 0;\n private values: string[] = [];\n private cacheMap = new Map<string, string>();\n\n register(code: string) {\n if (!this.cacheMap.has(code)) {\n const name = `v$$${this.counter++}`;\n this.values.push(`const ${name} = ${code};`);\n this.cacheMap.set(code, name);\n }\n return this.cacheMap.get(code)!;\n }\n toString() {\n return this.values.join('\\n');\n }\n}\n\nconst filterCodeGen = <T extends object>(query: Query<T> | undefined, valuePath: string, context: Context): string => {\n if (typeof query === 'object' && query !== null) {\n const condition = Object.entries(query).map(([key, value]) => {\n if (key in conditions) {\n return conditions[key as ConditionKeys](valuePath, stringify(value), context);\n }\n if (key in operations) {\n const values = Array.isArray(value) ? value : [value];\n return operations[key as OperationKeys](values.map((v) => filterCodeGen(v, valuePath, context)));\n }\n const valueSubPath = `${valuePath}?.[${stringify(key)}]`;\n if (value instanceof RegExp) {\n return conditions.$match(valueSubPath, value.toString(), context);\n }\n if (typeof value === 'object' && value !== null) {\n return filterCodeGen(value, valueSubPath, context);\n }\n return conditions.$eq(valueSubPath, JSON.stringify(value), context);\n });\n if (condition.length > 0) {\n return `${condition.join(' && ')}`;\n }\n }\n return 'true';\n};\n\nexport const createFilter = <T extends object>(query?: Query<T>) => {\n const context = new Context();\n const code = filterCodeGen(query, 'data', context);\n const filter = new Function('data', `${context}\\nreturn ${code};`);\n return (data: T) => filter(data);\n};\n\nexport type SortType = 'asc' | 'desc' | 1 | -1;\n\nexport type Sort<T> = T extends object\n ? {\n [K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;\n }\n : SortType;\n\nconst SORT_EXPR = /^(asc|desc|1|-1)$/i;\n\nconst SORT_MAP: Record<SortType, 1 | -1> = {\n asc: 1,\n desc: -1,\n 1: 1,\n '-1': -1,\n};\n\nconst sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string]): string => {\n if (typeof sort === 'object' && sort !== null) {\n const condition = Object.entries(sort).map(([key, value]) => {\n const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;\n const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;\n return sortCodeGen(value, [valueSubPathA, valueSubPathB]);\n });\n if (condition.length > 0) {\n return condition.join(' || ');\n }\n }\n if (typeof sort === 'string' && SORT_EXPR.test(sort)) {\n return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n if (typeof sort === 'number' && SORT_EXPR.test('' + sort)) {\n return `((${valuePath[0]} - ${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n return `0`;\n};\n\nexport const createSort = <T>(sort: Sort<T>) => {\n const code = sortCodeGen(sort, ['a', 'b']);\n const sortFn = new Function('a', 'b', `return ${code};`);\n return (a: T, b: T) => sortFn(a, b);\n};\n"],"names":["stringify","value","undefined","RegExp","source","flags","JSON","conditions","$eq","valuePath","$ne","$gt","$gte","$lt","$lte","$in","context","name","register","$nin","$sub","$nsub","$sup","$nsup","$con","$ncon","$exists","$match","$nmatch","$bits","$nbits","$typeof","$ntypeof","operations","$and","join","$or","$nor","$not","Context","counter","values","cacheMap","Map","code","has","push","set","get","toString","filterCodeGen","query","condition","Object","entries","map","key","Array","isArray","v","valueSubPath","length","createFilter","filter","Function","data","SORT_EXPR","SORT_MAP","asc","desc","sortCodeGen","sort","valueSubPathA","valueSubPathB","test","createSort","sortFn","a","b"],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import prettier from "eslint-plugin-prettier";
|
|
2
|
+
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
|
3
|
+
import tsParser from "@typescript-eslint/parser";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import js from "@eslint/js";
|
|
7
|
+
import { FlatCompat } from "@eslint/eslintrc";
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
const compat = new FlatCompat({
|
|
12
|
+
baseDirectory: __dirname,
|
|
13
|
+
recommendedConfig: js.configs.recommended,
|
|
14
|
+
allConfig: js.configs.all
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export default [{
|
|
18
|
+
ignores: [
|
|
19
|
+
"**/docs",
|
|
20
|
+
"**/build",
|
|
21
|
+
"**/coverage",
|
|
22
|
+
"**/node_modules",
|
|
23
|
+
"**/scripts",
|
|
24
|
+
"src/*.tmp.ts",
|
|
25
|
+
"src/__tests__/*",
|
|
26
|
+
],
|
|
27
|
+
}, ...compat.extends("eslint-config-airbnb-base").map(config => ({
|
|
28
|
+
...config,
|
|
29
|
+
files: ["**/*.js"],
|
|
30
|
+
})), {
|
|
31
|
+
files: ["**/*.js"],
|
|
32
|
+
|
|
33
|
+
plugins: {
|
|
34
|
+
prettier,
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
languageOptions: {
|
|
38
|
+
globals: {},
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
rules: {
|
|
42
|
+
"prettier/prettier": "error",
|
|
43
|
+
},
|
|
44
|
+
}, ...compat.extends(
|
|
45
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
46
|
+
"plugin:@typescript-eslint/recommended",
|
|
47
|
+
).map(config => ({
|
|
48
|
+
...config,
|
|
49
|
+
files: ["**/*.ts"],
|
|
50
|
+
})), {
|
|
51
|
+
files: ["**/*.ts"],
|
|
52
|
+
|
|
53
|
+
plugins: {
|
|
54
|
+
"@typescript-eslint": typescriptEslint,
|
|
55
|
+
prettier,
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
languageOptions: {
|
|
59
|
+
globals: {},
|
|
60
|
+
parser: tsParser,
|
|
61
|
+
ecmaVersion: 5,
|
|
62
|
+
sourceType: "script",
|
|
63
|
+
|
|
64
|
+
parserOptions: {
|
|
65
|
+
project: "./tsconfig.json",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
rules: {
|
|
70
|
+
"prettier/prettier": "error",
|
|
71
|
+
"@typescript-eslint/no-explicit-any": 0,
|
|
72
|
+
"@typescript-eslint/no-floating-promises": 1,
|
|
73
|
+
"@typescript-eslint/no-unused-vars": 2,
|
|
74
|
+
"@typescript-eslint/no-unsafe-declaration-merging": 0,
|
|
75
|
+
"@typescript-eslint/ban-ts-comment": 1,
|
|
76
|
+
"@typescript-eslint/ban-types": 0,
|
|
77
|
+
"@typescript-eslint/no-empty-object-type": 0
|
|
78
|
+
},
|
|
79
|
+
}];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ajoq",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -14,12 +14,27 @@
|
|
|
14
14
|
"author": "Ivan Zakharchanka",
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"devDependencies": {
|
|
17
|
+
"@swc/jest": "^0.2.37",
|
|
18
|
+
"@types/jest": "^29.5.14",
|
|
17
19
|
"@types/node": "^22.10.0",
|
|
20
|
+
"@typescript-eslint/eslint-plugin": "^8.16.0",
|
|
21
|
+
"@typescript-eslint/parser": "^8.16.0",
|
|
22
|
+
"@typescript-eslint/typescript-estree": "^8.16.0",
|
|
23
|
+
"eslint": "^9.15.0",
|
|
24
|
+
"eslint-config-airbnb-base": "^15.0.0",
|
|
25
|
+
"eslint-config-prettier": "^9.1.0",
|
|
26
|
+
"eslint-plugin-import": "^2.31.0",
|
|
27
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
28
|
+
"husky": "^9.1.7",
|
|
18
29
|
"inop": "^0.7.4",
|
|
30
|
+
"jest": "^29.7.0",
|
|
31
|
+
"prettier": "^3.4.1",
|
|
19
32
|
"ts-node": "^10.9.2",
|
|
20
33
|
"typescript": "^5.7.2"
|
|
21
34
|
},
|
|
22
35
|
"scripts": {
|
|
23
|
-
"build": "rm -rf build && NODE_ENV=production inop src/ build -i __tests__ && tsc --declaration --emitDeclarationOnly"
|
|
36
|
+
"build": "rm -rf build && NODE_ENV=production inop src/ build -i __tests__ && tsc --declaration --emitDeclarationOnly",
|
|
37
|
+
"test": "jest",
|
|
38
|
+
"lint": "eslint src"
|
|
24
39
|
}
|
|
25
40
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,11 +2,9 @@ export type Query<T> = {
|
|
|
2
2
|
[P in keyof T]?: QueryCondition<T[P]>;
|
|
3
3
|
} & Operations<T>;
|
|
4
4
|
|
|
5
|
-
type QueryCondition<FieldType> =
|
|
6
|
-
| FieldType
|
|
7
|
-
| Conditions<FieldType>;
|
|
5
|
+
export type QueryCondition<FieldType> = FieldType | Conditions<FieldType> | Operations<QueryCondition<FieldType>>;
|
|
8
6
|
|
|
9
|
-
interface Conditions<ValueType> {
|
|
7
|
+
export interface Conditions<ValueType> {
|
|
10
8
|
$eq?: ValueType;
|
|
11
9
|
$ne?: ValueType;
|
|
12
10
|
$gt?: ValueType;
|
|
@@ -15,76 +13,134 @@ interface Conditions<ValueType> {
|
|
|
15
13
|
$lte?: ValueType;
|
|
16
14
|
$in?: ValueType[];
|
|
17
15
|
$nin?: ValueType[];
|
|
16
|
+
$sub?: ValueType[];
|
|
17
|
+
$nsub?: ValueType[];
|
|
18
|
+
$sup?: ValueType[];
|
|
19
|
+
$nsup?: ValueType[];
|
|
20
|
+
$con?: ValueType;
|
|
21
|
+
$ncon?: ValueType;
|
|
18
22
|
$exists?: boolean;
|
|
19
23
|
$match?: RegExp | string;
|
|
20
24
|
$nmatch?: RegExp | string;
|
|
21
25
|
$bits?: number;
|
|
22
26
|
$nbits?: number;
|
|
23
|
-
|
|
27
|
+
$typeof: string;
|
|
28
|
+
$ntypeof: string;
|
|
29
|
+
}
|
|
24
30
|
|
|
25
|
-
type ConditionKeys = keyof Conditions<unknown>;
|
|
31
|
+
export type ConditionKeys = keyof Conditions<unknown>;
|
|
26
32
|
|
|
27
|
-
interface Operations<T> {
|
|
33
|
+
export interface Operations<T> {
|
|
28
34
|
$and?: Query<T>[];
|
|
29
35
|
$or?: Query<T>[];
|
|
30
36
|
$nor?: Query<T>[];
|
|
31
37
|
$not?: Query<T>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type OperationKeys = keyof Operations<unknown>;
|
|
41
|
+
|
|
42
|
+
export const stringify = (value: unknown) => {
|
|
43
|
+
if (value === undefined) {
|
|
44
|
+
return 'undefined';
|
|
45
|
+
}
|
|
46
|
+
if (value instanceof RegExp) {
|
|
47
|
+
return `/${value.source}/${value.flags}`;
|
|
48
|
+
}
|
|
49
|
+
return JSON.stringify(value);
|
|
32
50
|
};
|
|
33
51
|
|
|
34
|
-
|
|
52
|
+
export interface Condition {
|
|
53
|
+
(valuePath: string, value: any, context: Context): string;
|
|
54
|
+
}
|
|
35
55
|
|
|
36
|
-
const conditions: Record<ConditionKeys,
|
|
56
|
+
const conditions: Record<ConditionKeys, Condition> = {
|
|
37
57
|
$eq: (valuePath: string, value: unknown) => `(${valuePath} === ${value})`,
|
|
38
58
|
$ne: (valuePath: string, value: unknown) => `(${valuePath} !== ${value})`,
|
|
39
59
|
$gt: (valuePath: string, value: unknown) => `(${valuePath} > ${value})`,
|
|
40
60
|
$gte: (valuePath: string, value: unknown) => `(${valuePath} >= ${value})`,
|
|
41
61
|
$lt: (valuePath: string, value: unknown) => `(${valuePath} < ${value})`,
|
|
42
62
|
$lte: (valuePath: string, value: unknown) => `(${valuePath} <= ${value})`,
|
|
43
|
-
$in: (valuePath: string, value: unknown) =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
$
|
|
63
|
+
$in: (valuePath: string, value: unknown, context: Context) => {
|
|
64
|
+
const name = context.register(`new Set(${value})`);
|
|
65
|
+
return `${name}.has(${valuePath})`;
|
|
66
|
+
},
|
|
67
|
+
$nin: (valuePath: string, value: unknown, context: Context) => `!${conditions.$in(valuePath, value, context)}`,
|
|
68
|
+
$sub: (valuePath: string, value: unknown, context: Context) => {
|
|
69
|
+
const name = context.register(`new Set(${value})`);
|
|
70
|
+
return `${valuePath}.every((value) => ${name}.has(value))`;
|
|
71
|
+
},
|
|
72
|
+
$nsub: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sub(valuePath, value, context)}`,
|
|
73
|
+
$sup: (valuePath: string, value: unknown, context: Context) => {
|
|
74
|
+
const name = context.register(`new Set(${valuePath})`);
|
|
75
|
+
return `${value}.every((value) => ${name}.has(value))`;
|
|
76
|
+
},
|
|
77
|
+
$nsup: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sup(valuePath, value, context)}`,
|
|
78
|
+
$con: (valuePath: string, value: unknown) => `${valuePath}.includes(${value})`,
|
|
79
|
+
$ncon: (valuePath: string, value: unknown) => `!${valuePath}.includes(${value})`,
|
|
80
|
+
$exists: (valuePath: string, value: boolean) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,
|
|
81
|
+
$match: (valuePath: string, value: RegExp) => `${value}.test('' + ${valuePath})`,
|
|
82
|
+
$nmatch: (valuePath: string, value: RegExp) => `!${value}.test('' + ${valuePath})`,
|
|
48
83
|
$bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,
|
|
49
84
|
$nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,
|
|
85
|
+
$typeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} === ${value})`,
|
|
86
|
+
$ntypeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} !== ${value})`,
|
|
50
87
|
};
|
|
51
88
|
|
|
52
89
|
const operations: Record<keyof Operations<unknown>, any> = {
|
|
53
90
|
$and: (value: unknown[]) => `(${value.join(' && ')})`,
|
|
54
91
|
$or: (value: unknown[]) => `(${value.join(' || ')})`,
|
|
55
|
-
$nor: (value: unknown[]) =>
|
|
56
|
-
$not: (value: unknown) =>
|
|
57
|
-
}
|
|
92
|
+
$nor: (value: unknown[]) => `!(${value.join(' || ')})`,
|
|
93
|
+
$not: (value: unknown[]) => `!(${value})`,
|
|
94
|
+
};
|
|
58
95
|
|
|
96
|
+
class Context {
|
|
97
|
+
private counter = 0;
|
|
98
|
+
private values: string[] = [];
|
|
99
|
+
private cacheMap = new Map<string, string>();
|
|
100
|
+
|
|
101
|
+
register(code: string) {
|
|
102
|
+
if (!this.cacheMap.has(code)) {
|
|
103
|
+
const name = `v$$${this.counter++}`;
|
|
104
|
+
this.values.push(`const ${name} = ${code};`);
|
|
105
|
+
this.cacheMap.set(code, name);
|
|
106
|
+
}
|
|
107
|
+
return this.cacheMap.get(code)!;
|
|
108
|
+
}
|
|
109
|
+
toString() {
|
|
110
|
+
return this.values.join('\n');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
59
113
|
|
|
60
|
-
const
|
|
114
|
+
const filterCodeGen = <T extends object>(query: Query<T> | undefined, valuePath: string, context: Context): string => {
|
|
61
115
|
if (typeof query === 'object' && query !== null) {
|
|
62
116
|
const condition = Object.entries(query).map(([key, value]) => {
|
|
63
117
|
if (key in conditions) {
|
|
64
|
-
return conditions[key as ConditionKeys](valuePath,
|
|
118
|
+
return conditions[key as ConditionKeys](valuePath, stringify(value), context);
|
|
65
119
|
}
|
|
66
120
|
if (key in operations) {
|
|
67
121
|
const values = Array.isArray(value) ? value : [value];
|
|
68
|
-
return operations[key as OperationKeys](values.map((
|
|
122
|
+
return operations[key as OperationKeys](values.map((v) => filterCodeGen(v, valuePath, context)));
|
|
69
123
|
}
|
|
70
|
-
const valueSubPath = `${valuePath}?.[${
|
|
124
|
+
const valueSubPath = `${valuePath}?.[${stringify(key)}]`;
|
|
71
125
|
if (value instanceof RegExp) {
|
|
72
|
-
return conditions.$match(valueSubPath, value.toString());
|
|
126
|
+
return conditions.$match(valueSubPath, value.toString(), context);
|
|
73
127
|
}
|
|
74
128
|
if (typeof value === 'object' && value !== null) {
|
|
75
|
-
return
|
|
129
|
+
return filterCodeGen(value, valueSubPath, context);
|
|
76
130
|
}
|
|
77
|
-
return conditions.$eq(valueSubPath, JSON.stringify(value));
|
|
131
|
+
return conditions.$eq(valueSubPath, JSON.stringify(value), context);
|
|
78
132
|
});
|
|
79
|
-
|
|
80
|
-
|
|
133
|
+
if (condition.length > 0) {
|
|
134
|
+
return `${condition.join(' && ')}`;
|
|
135
|
+
}
|
|
81
136
|
}
|
|
82
137
|
return 'true';
|
|
83
|
-
}
|
|
138
|
+
};
|
|
84
139
|
|
|
85
|
-
export const
|
|
86
|
-
const
|
|
87
|
-
const
|
|
140
|
+
export const createFilter = <T extends object>(query?: Query<T>) => {
|
|
141
|
+
const context = new Context();
|
|
142
|
+
const code = filterCodeGen(query, 'data', context);
|
|
143
|
+
const filter = new Function('data', `${context}\nreturn ${code};`);
|
|
88
144
|
return (data: T) => filter(data);
|
|
89
145
|
};
|
|
90
146
|
|
|
@@ -92,27 +148,29 @@ export type SortType = 'asc' | 'desc' | 1 | -1;
|
|
|
92
148
|
|
|
93
149
|
export type Sort<T> = T extends object
|
|
94
150
|
? {
|
|
95
|
-
|
|
96
|
-
|
|
151
|
+
[K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;
|
|
152
|
+
}
|
|
97
153
|
: SortType;
|
|
98
154
|
|
|
99
|
-
|
|
155
|
+
const SORT_EXPR = /^(asc|desc|1|-1)$/i;
|
|
100
156
|
|
|
101
|
-
|
|
157
|
+
const SORT_MAP: Record<SortType, 1 | -1> = {
|
|
102
158
|
asc: 1,
|
|
103
159
|
desc: -1,
|
|
104
160
|
1: 1,
|
|
105
161
|
'-1': -1,
|
|
106
162
|
};
|
|
107
163
|
|
|
108
|
-
|
|
164
|
+
const sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string]): string => {
|
|
109
165
|
if (typeof sort === 'object' && sort !== null) {
|
|
110
166
|
const condition = Object.entries(sort).map(([key, value]) => {
|
|
111
167
|
const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;
|
|
112
168
|
const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;
|
|
113
|
-
return sortCodeGen(value, [valueSubPathA, valueSubPathB]
|
|
169
|
+
return sortCodeGen(value, [valueSubPathA, valueSubPathB]);
|
|
114
170
|
});
|
|
115
|
-
|
|
171
|
+
if (condition.length > 0) {
|
|
172
|
+
return condition.join(' || ');
|
|
173
|
+
}
|
|
116
174
|
}
|
|
117
175
|
if (typeof sort === 'string' && SORT_EXPR.test(sort)) {
|
|
118
176
|
return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;
|
|
@@ -123,9 +181,8 @@ export const sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string], path:
|
|
|
123
181
|
return `0`;
|
|
124
182
|
};
|
|
125
183
|
|
|
126
|
-
export const createSort = <T>(sort: Sort<T
|
|
127
|
-
const code = sortCodeGen(sort, ['a', 'b']
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
return (data: T) => filter(data);
|
|
184
|
+
export const createSort = <T>(sort: Sort<T>) => {
|
|
185
|
+
const code = sortCodeGen(sort, ['a', 'b']);
|
|
186
|
+
const sortFn = new Function('a', 'b', `return ${code};`);
|
|
187
|
+
return (a: T, b: T) => sortFn(a, b);
|
|
131
188
|
};
|
package/.prettierrc
DELETED
package/tsconfig.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"strict": true,
|
|
5
|
-
"target": "ESNext",
|
|
6
|
-
"module": "NodeNext",
|
|
7
|
-
"moduleResolution": "NodeNext",
|
|
8
|
-
"declaration": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"lib": ["ESNext"],
|
|
11
|
-
"rootDir": "src",
|
|
12
|
-
"outDir": "build"
|
|
13
|
-
},
|
|
14
|
-
"include": [
|
|
15
|
-
"src/**/*.ts"
|
|
16
|
-
, "src/__benchmark__/index.js" ],
|
|
17
|
-
"exclude": [
|
|
18
|
-
"**/__tests__",
|
|
19
|
-
"node_modules",
|
|
20
|
-
"**/*.tmp.ts"
|
|
21
|
-
]
|
|
22
|
-
}
|