-
Notifications
You must be signed in to change notification settings - Fork 659
Expand file tree
/
Copy pathpathFormatter.ts
More file actions
56 lines (44 loc) · 1.57 KB
/
Copy pathpathFormatter.ts
File metadata and controls
56 lines (44 loc) · 1.57 KB
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
import { JSONHeroPath, PathComponent } from "@jsonhero/path";
export type PathFormat = "jsonpath" | "js";
// The public PathComponent interface doesn't expose `keyName`, but every
// concrete component (SimpleKeyPathComponent, StartPathComponent, ...) does.
function keyNameOf(component: PathComponent): string {
return (component as PathComponent & { keyName: string }).keyName;
}
const SAFE_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
function quoteKey(key: string): string {
const escaped = key.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
return `["${escaped}"]`;
}
/**
* Formats a JSONHeroPath string (e.g. `$.data.0.user-name`) as either a
* JSONPath expression or a JavaScript accessor.
*
* - jsonpath: `$.data[0]["user-name"]` (root -> `$`)
* - js: `data[0]["user-name"]` (root -> ``)
*/
export function formatPath(path: string, format: PathFormat): string {
const heroPath = new JSONHeroPath(path);
// Drop the leading StartPathComponent (`$`).
const components = heroPath.components.slice(1);
let result = format === "jsonpath" ? "$" : "";
components.forEach((component, index) => {
const key = keyNameOf(component);
if (component.isArray) {
result += `[${key}]`;
return;
}
if (!SAFE_IDENTIFIER.test(key)) {
result += quoteKey(key);
return;
}
// A safe identifier: use dot notation, except for the very first
// component of a JS accessor, which has no leading dot.
if (format === "js" && index === 0) {
result += key;
} else {
result += `.${key}`;
}
});
return result;
}