@player-lang/json-language-service 0.0.2-next.0

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.
Files changed (59) hide show
  1. package/dist/cjs/index.cjs +2314 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/index.legacy-esm.js +2249 -0
  4. package/dist/index.mjs +2249 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +40 -0
  7. package/src/__tests__/__snapshots__/service.test.ts.snap +213 -0
  8. package/src/__tests__/service.test.ts +298 -0
  9. package/src/constants.ts +38 -0
  10. package/src/index.ts +490 -0
  11. package/src/parser/__tests__/parse.test.ts +18 -0
  12. package/src/parser/document.ts +456 -0
  13. package/src/parser/edits.ts +31 -0
  14. package/src/parser/index.ts +38 -0
  15. package/src/parser/jsonParseErrors.ts +69 -0
  16. package/src/parser/types.ts +314 -0
  17. package/src/parser/utils.ts +94 -0
  18. package/src/plugins/__tests__/asset-wrapper-array-plugin.test.ts +112 -0
  19. package/src/plugins/__tests__/binding-schema-plugin.test.ts +62 -0
  20. package/src/plugins/__tests__/duplicate-id-plugin.test.ts +195 -0
  21. package/src/plugins/__tests__/missing-asset-wrapper-plugin.test.ts +190 -0
  22. package/src/plugins/__tests__/nav-state-plugin.test.ts +136 -0
  23. package/src/plugins/__tests__/view-node-plugin.test.ts +154 -0
  24. package/src/plugins/asset-wrapper-array-plugin.ts +123 -0
  25. package/src/plugins/binding-schema-plugin.ts +289 -0
  26. package/src/plugins/duplicate-id-plugin.ts +158 -0
  27. package/src/plugins/missing-asset-wrapper-plugin.ts +96 -0
  28. package/src/plugins/nav-state-plugin.ts +139 -0
  29. package/src/plugins/view-node-plugin.ts +225 -0
  30. package/src/plugins/xlr-plugin.ts +371 -0
  31. package/src/types.ts +119 -0
  32. package/src/utils.ts +143 -0
  33. package/src/xlr/__tests__/__snapshots__/transform.test.ts.snap +390 -0
  34. package/src/xlr/__tests__/transform.test.ts +108 -0
  35. package/src/xlr/index.ts +3 -0
  36. package/src/xlr/registry.ts +99 -0
  37. package/src/xlr/service.ts +190 -0
  38. package/src/xlr/transforms.ts +169 -0
  39. package/types/constants.d.ts +7 -0
  40. package/types/index.d.ts +69 -0
  41. package/types/parser/document.d.ts +25 -0
  42. package/types/parser/edits.d.ts +10 -0
  43. package/types/parser/index.d.ts +16 -0
  44. package/types/parser/jsonParseErrors.d.ts +27 -0
  45. package/types/parser/types.d.ts +188 -0
  46. package/types/parser/utils.d.ts +26 -0
  47. package/types/plugins/asset-wrapper-array-plugin.d.ts +9 -0
  48. package/types/plugins/binding-schema-plugin.d.ts +15 -0
  49. package/types/plugins/duplicate-id-plugin.d.ts +7 -0
  50. package/types/plugins/missing-asset-wrapper-plugin.d.ts +9 -0
  51. package/types/plugins/nav-state-plugin.d.ts +9 -0
  52. package/types/plugins/view-node-plugin.d.ts +9 -0
  53. package/types/plugins/xlr-plugin.d.ts +7 -0
  54. package/types/types.d.ts +81 -0
  55. package/types/utils.d.ts +24 -0
  56. package/types/xlr/index.d.ts +4 -0
  57. package/types/xlr/registry.d.ts +17 -0
  58. package/types/xlr/service.d.ts +22 -0
  59. package/types/xlr/transforms.d.ts +18 -0
@@ -0,0 +1,190 @@
1
+ import { XLRSDK } from "@xlr-lib/xlr-sdk";
2
+ import type { ArrayType, NodeType, ObjectType } from "@xlr-lib/xlr";
3
+ import type { ASTNode } from "../parser";
4
+ import { mapFlowStateToType } from "../utils";
5
+ import { PlayerXLRRegistry } from "./registry";
6
+
7
+ export interface XLRContext {
8
+ /** The name of the XLR at the provided position */
9
+ name: string;
10
+
11
+ /** The position(s) in the XLR at the specified node */
12
+ nodes: Array<NodeType>;
13
+
14
+ /** The nearest objects(s) to the specified node */
15
+ nearestObjects: Array<ObjectType>;
16
+ }
17
+
18
+ /**
19
+ * XLRs Manager for
20
+ */
21
+ export class XLRService {
22
+ private baseTypes = [
23
+ "asset",
24
+ "view",
25
+ "flow",
26
+ "content",
27
+ "navigation",
28
+ "state",
29
+ ];
30
+
31
+ public XLRSDK: XLRSDK;
32
+
33
+ constructor() {
34
+ this.XLRSDK = new XLRSDK(new PlayerXLRRegistry());
35
+ }
36
+
37
+ private walker(
38
+ n: ASTNode,
39
+ path: ASTNode[],
40
+ ):
41
+ | {
42
+ /** name of type */
43
+ name: string;
44
+
45
+ /** path to get there from n */
46
+ path: ASTNode[];
47
+ }
48
+ | undefined {
49
+ if (this.baseTypes.indexOf(n.type) > -1) {
50
+ if (n.type === "asset") {
51
+ const name = this.XLRSDK.hasType(n.assetType?.valueNode?.value ?? "")
52
+ ? (n.assetType?.valueNode?.value as string)
53
+ : "Asset";
54
+ return {
55
+ name,
56
+ path,
57
+ };
58
+ }
59
+
60
+ if (n.type === "view") {
61
+ const name = this.XLRSDK.hasType(n.viewType?.valueNode?.value ?? "")
62
+ ? (n.viewType?.valueNode?.value as string)
63
+ : "View";
64
+ return { name, path };
65
+ }
66
+
67
+ if (n.type === "state") {
68
+ if (n.stateType?.valueNode?.value) {
69
+ const flowStateType = mapFlowStateToType(
70
+ n.stateType?.valueNode?.value,
71
+ );
72
+ if (flowStateType) {
73
+ return { name: flowStateType, path };
74
+ }
75
+ }
76
+ }
77
+
78
+ if (n.type === "content") {
79
+ return { name: "Flow", path };
80
+ }
81
+
82
+ if (n.type === "flow") {
83
+ return { name: "NavigationFlow", path };
84
+ }
85
+
86
+ if (n.type === "navigation") {
87
+ return { name: "Navigation", path };
88
+ }
89
+ }
90
+
91
+ if (n.parent) {
92
+ path.push(n.parent);
93
+ return this.walker(n.parent, path);
94
+ }
95
+
96
+ return undefined;
97
+ }
98
+
99
+ public getTypeInfoAtPosition(
100
+ node: ASTNode | undefined,
101
+ ): XLRContext | undefined {
102
+ if (!node) return;
103
+
104
+ const pointer = node;
105
+ const xlrInfo = this.walker(pointer, []);
106
+
107
+ // bail if we can"t figure out the type or don"t have it
108
+ if (!xlrInfo) return;
109
+
110
+ const activeNode = this.XLRSDK.getType(xlrInfo.name);
111
+ if (!activeNode) return;
112
+
113
+ let nearestObjectTypes = [activeNode as ObjectType];
114
+ let pointers = [];
115
+
116
+ if (activeNode.type === "and") {
117
+ pointers = activeNode.and;
118
+ } else if (activeNode.type === "or") {
119
+ pointers = activeNode.or;
120
+ } else {
121
+ pointers = [activeNode];
122
+ }
123
+
124
+ for (const pathSegment of xlrInfo.path.reverse()) {
125
+ const newPointers: NodeType[] = [];
126
+
127
+ for (let nodePointer of pointers) {
128
+ let newNode;
129
+
130
+ if (nodePointer.type === "ref") {
131
+ if (this.XLRSDK.hasType(nodePointer.ref)) {
132
+ nodePointer = this.XLRSDK.getType(nodePointer.ref) as ObjectType;
133
+ } else {
134
+ continue;
135
+ }
136
+ }
137
+
138
+ if (pathSegment.type === "property" && nodePointer.type === "object") {
139
+ if (nodePointer?.properties[pathSegment.keyNode.value]) {
140
+ newNode = nodePointer?.properties[pathSegment.keyNode.value]?.node;
141
+ } else if (nodePointer?.additionalProperties) {
142
+ // search through additional properties types
143
+ const adNode = nodePointer?.additionalProperties;
144
+ if (typeof adNode !== "boolean") {
145
+ newNode = adNode;
146
+ }
147
+ }
148
+ } else if (
149
+ pathSegment.type === "object" ||
150
+ this.baseTypes.indexOf(pathSegment.type) !== -1
151
+ ) {
152
+ newNode = nodePointer;
153
+ } else if (pathSegment.type === "array") {
154
+ newNode = (nodePointer as ArrayType).elementType;
155
+ }
156
+
157
+ if (!newNode) {
158
+ continue;
159
+ } else if (newNode.type === "or") {
160
+ newPointers.push(...newNode.or);
161
+ } else if (newNode.type === "and") {
162
+ newPointers.push(...newNode.and);
163
+ } else if (newNode.type === "ref" && this.XLRSDK.hasType(newNode.ref)) {
164
+ newPointers.push(this.XLRSDK.getType(newNode.ref) as ObjectType);
165
+ } else {
166
+ newPointers.push(newNode);
167
+ }
168
+ }
169
+
170
+ if (newPointers.filter((n) => n).length === 0) {
171
+ break;
172
+ }
173
+
174
+ const newObjectTypes = newPointers.filter(
175
+ (n) => n.type === "object",
176
+ ) as ObjectType[];
177
+ if (newObjectTypes.length > 0) {
178
+ nearestObjectTypes = newObjectTypes;
179
+ }
180
+
181
+ pointers = newPointers;
182
+ }
183
+
184
+ return {
185
+ name: xlrInfo.name,
186
+ nodes: pointers,
187
+ nearestObjects: nearestObjectTypes,
188
+ };
189
+ }
190
+ }
@@ -0,0 +1,169 @@
1
+ import type {
2
+ ArrayType,
3
+ OrType,
4
+ RefType,
5
+ TransformFunction,
6
+ } from "@xlr-lib/xlr";
7
+ import { simpleTransformGenerator } from "@xlr-lib/xlr-sdk";
8
+ import { isPrimitiveTypeNode } from "@xlr-lib/xlr-utils";
9
+
10
+ /**
11
+ * Adds applicability and _comment properties to Assets
12
+ */
13
+
14
+ export const applyCommonProps: TransformFunction = (node, capability) => {
15
+ return simpleTransformGenerator("object", ["Assets", "Views"], (xlrNode) => {
16
+ if (!xlrNode.properties.applicability) {
17
+ xlrNode.properties.applicability = {
18
+ required: false,
19
+ node: {
20
+ type: "or",
21
+ name: "Applicability",
22
+ description:
23
+ "Evaluate the given expression (or boolean) and if falsy, remove this node from the tree. This is re-computed for each change in the data-model",
24
+ or: [
25
+ {
26
+ type: "boolean",
27
+ },
28
+ {
29
+ type: "ref",
30
+ ref: "Expression",
31
+ },
32
+ ],
33
+ },
34
+ };
35
+ }
36
+
37
+ if (!xlrNode.properties._comment) {
38
+ xlrNode.properties._comment = {
39
+ required: false,
40
+ node: {
41
+ description: "Adds a comment for the given node",
42
+ type: "string",
43
+ },
44
+ };
45
+ }
46
+
47
+ return xlrNode;
48
+ })(node, capability);
49
+ };
50
+
51
+ /**
52
+ * Replaces AssetWrapper References with AssetWrapperOrSwitch
53
+ */
54
+ export const applyAssetWrapperOrSwitch: TransformFunction = (
55
+ node,
56
+ capability,
57
+ ) => {
58
+ return simpleTransformGenerator("ref", ["Assets", "Views"], (xlrNode) => {
59
+ if (xlrNode.ref.includes("AssetWrapper")) {
60
+ return {
61
+ ...xlrNode,
62
+ ref: xlrNode.ref.replace("AssetWrapper", "AssetWrapperOrSwitch"),
63
+ };
64
+ }
65
+
66
+ return xlrNode;
67
+ })(node, capability);
68
+ };
69
+
70
+ /**
71
+ * Modifies any primitive type property node (except id/type) to be Bindings or Expressions
72
+ */
73
+ export const applyValueRefs: TransformFunction = (node, capability) => {
74
+ return simpleTransformGenerator(
75
+ "object",
76
+ ["Assets", "Views"],
77
+ (inputNode) => {
78
+ const xlrNode = { ...inputNode };
79
+ for (const key in xlrNode.properties) {
80
+ if (key === "id") {
81
+ continue;
82
+ }
83
+
84
+ const value = xlrNode.properties[key];
85
+ if (value.node.type === "or") {
86
+ value.node.or.push({
87
+ type: "ref",
88
+ ref: "ExpressionRef",
89
+ });
90
+ value.node.or.push({
91
+ type: "ref",
92
+ ref: "BindingRef",
93
+ });
94
+ } else if (isPrimitiveTypeNode(value.node)) {
95
+ const newUnionType: OrType = {
96
+ type: "or",
97
+ description: value.node.description,
98
+ or: [
99
+ value.node,
100
+ {
101
+ type: "ref",
102
+ ref: "ExpressionRef",
103
+ },
104
+ {
105
+ type: "ref",
106
+ ref: "BindingRef",
107
+ },
108
+ ],
109
+ };
110
+ value.node = newUnionType;
111
+ }
112
+ }
113
+
114
+ return xlrNode;
115
+ },
116
+ )(node, capability);
117
+ };
118
+
119
+ /**
120
+ * Computes possible template keys and adds them to an Asset
121
+ */
122
+ export const applyTemplateProperty: TransformFunction = (node, capability) => {
123
+ return simpleTransformGenerator(
124
+ "object",
125
+ ["Assets", "Views"],
126
+ (inputNode) => {
127
+ const templateTypes: Array<RefType> = [];
128
+ const xlrNode = { ...inputNode };
129
+ for (const key in xlrNode.properties) {
130
+ const value = xlrNode.properties[key];
131
+ if (value.node.type === "array") {
132
+ value.required = false;
133
+ templateTypes.push({
134
+ type: "ref",
135
+ ref: `Template<${
136
+ value.node.elementType.type === "ref"
137
+ ? value.node.elementType.ref
138
+ : value.node.elementType.name
139
+ }, "${key}">`,
140
+ genericArguments: [
141
+ value.node.elementType,
142
+ {
143
+ type: "string",
144
+ const: key,
145
+ },
146
+ ],
147
+ });
148
+ }
149
+ }
150
+
151
+ if (templateTypes.length > 0) {
152
+ const templateType: ArrayType = {
153
+ type: "array",
154
+ elementType:
155
+ templateTypes.length > 1
156
+ ? { type: "or", or: templateTypes }
157
+ : templateTypes[0],
158
+ description: "A list of templates to process for this node",
159
+ };
160
+ xlrNode.properties.template = {
161
+ required: false,
162
+ node: templateType,
163
+ };
164
+ }
165
+
166
+ return xlrNode;
167
+ },
168
+ )(node, capability);
169
+ };
@@ -0,0 +1,7 @@
1
+ import type { Filters } from "@xlr-lib/xlr-sdk";
2
+ import { TransformFunction } from "@xlr-lib/xlr";
3
+ import type { PlayerLanguageServicePlugin } from ".";
4
+ export declare const PLUGINS: Array<PlayerLanguageServicePlugin>;
5
+ export declare const DEFAULT_FILTERS: Filters;
6
+ export declare const TRANSFORM_FUNCTIONS: Array<TransformFunction>;
7
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1,69 @@
1
+ import type { TextDocument } from "vscode-languageserver-textdocument";
2
+ import { AsyncParallelHook, SyncBailHook, SyncHook, SyncWaterfallHook } from "tapable-ts";
3
+ import type { CodeAction, CodeActionContext, CompletionItem, Diagnostic, FormattingOptions, Hover, Position, Location } from "vscode-languageserver-types";
4
+ import { CompletionList, Range, TextEdit } from "vscode-languageserver-types";
5
+ import type { TransformFunction } from "@xlr-lib/xlr";
6
+ import type { DocumentContext, ValidationContext, CompletionContext, EnhancedDocumentContextWithPosition, Violation } from "./types";
7
+ import { XLRService } from "./xlr";
8
+ import { TSManifest } from "@xlr-lib/xlr";
9
+ export * from "./utils";
10
+ export * from "./constants";
11
+ export * from "./types";
12
+ export * from "./parser";
13
+ export * from "./xlr/index";
14
+ export interface PlayerLanguageServicePlugin {
15
+ /** The name of the plugin */
16
+ name: string;
17
+ /** the handle to get the LSP */
18
+ apply(languageService: PlayerLanguageService): void;
19
+ }
20
+ /** The thing that handles most of the LSP work */
21
+ export declare class PlayerLanguageService {
22
+ readonly XLRService: XLRService;
23
+ private parseCache;
24
+ private fixableViolationsForDocument;
25
+ readonly hooks: {
26
+ onDocumentUpdate: SyncHook<[DocumentContext], Record<string, any>>;
27
+ validate: AsyncParallelHook<[DocumentContext, ValidationContext], void>;
28
+ onValidateEnd: SyncWaterfallHook<[
29
+ Diagnostic[],
30
+ {
31
+ /** The context of the document */
32
+ documentContext: DocumentContext;
33
+ /** A callback for adding a new fixable rule */
34
+ addFixableViolation: (diag: Diagnostic, violation: Violation) => void;
35
+ }
36
+ ], Record<string, any>>;
37
+ complete: AsyncParallelHook<[
38
+ EnhancedDocumentContextWithPosition,
39
+ CompletionContext
40
+ ], void>;
41
+ hover: SyncBailHook<[
42
+ EnhancedDocumentContextWithPosition
43
+ ], Hover | undefined, Record<string, any>>;
44
+ definition: SyncBailHook<[
45
+ EnhancedDocumentContextWithPosition
46
+ ], Location | undefined, Record<string, any>>;
47
+ };
48
+ constructor(config?: {
49
+ /** A list of plugins to include */
50
+ plugins?: Array<PlayerLanguageServicePlugin>;
51
+ });
52
+ private parseTextDocument;
53
+ private updateSource;
54
+ private getJSONPositionInfo;
55
+ private updateSourceWithPosition;
56
+ onClose(document: TextDocument): void;
57
+ formatTextDocument(document: TextDocument, options: FormattingOptions, range?: Range): Promise<Array<TextEdit> | undefined>;
58
+ validateTextDocument(document: TextDocument): Promise<Array<Diagnostic> | undefined>;
59
+ getCompletionsAtPosition(document: TextDocument, position: Position): Promise<CompletionList>;
60
+ resolveCompletionItem(completionItem: CompletionItem): Promise<CompletionItem>;
61
+ getHoverInfoAtPosition(document: TextDocument, position: Position): Promise<Hover | undefined | null>;
62
+ getCodeActionsInRange(document: TextDocument, context: CodeActionContext): Promise<CodeAction[]>;
63
+ getDefinitionAtPosition(document: TextDocument, position: Position): Promise<Location | undefined | null>;
64
+ addXLRTransforms(transforms: Record<string, TransformFunction>): void;
65
+ addLSPPlugin(plugin: PlayerLanguageServicePlugin): void;
66
+ setAssetTypes(typeFiles: Array<string>): Promise<void>;
67
+ setAssetTypesFromModule(manifest: Array<TSManifest>): Promise<void>;
68
+ }
69
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,25 @@
1
+ import type { Node } from "jsonc-parser";
2
+ import type { Diagnostic } from "vscode-languageserver-types";
3
+ import type { TextDocument } from "vscode-languageserver-textdocument";
4
+ import type { ASTNode } from "./types";
5
+ /**
6
+ * The top level document
7
+ */
8
+ export declare class PlayerContent {
9
+ readonly root: ASTNode;
10
+ readonly syntaxErrors: Array<Diagnostic>;
11
+ private readonly jsonNodeToNode;
12
+ constructor(root: ASTNode, errors: Array<Diagnostic>, jsonToNodeMap: Map<Node, ASTNode>);
13
+ getNodeFromOffset(offset: number): ASTNode | undefined;
14
+ }
15
+ export declare enum ObjType {
16
+ FLOW = 0,
17
+ ASSET = 1,
18
+ ASSET_WRAPPER = 2,
19
+ UNKNOWN = 3
20
+ }
21
+ /** Try to identify any object as an Asset or Flow */
22
+ export default function identify(node: Node): ObjType;
23
+ /** parse a text document into a player one */
24
+ export declare function parse(document: TextDocument): PlayerContent;
25
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1,10 @@
1
+ import type { TextDocument } from "vscode-languageserver-textdocument";
2
+ import { TextEdit, Range } from "vscode-languageserver-types";
3
+ import type { StringASTNode, NodeEdit, ASTNode } from "./types";
4
+ /** Create a NodeEdit by replacing a string */
5
+ export declare function replaceString(node: StringASTNode, newText: string): NodeEdit;
6
+ /** Get the range for a given node */
7
+ export declare function toRange(document: TextDocument, node: ASTNode): Range;
8
+ /** Convert a NodeEdit to a TextEdit */
9
+ export declare function toTextEdit(document: TextDocument, edit: NodeEdit): TextEdit;
10
+ //# sourceMappingURL=edits.d.ts.map
@@ -0,0 +1,16 @@
1
+ import type { TextDocument } from "vscode-languageserver-textdocument";
2
+ import type { PlayerContent } from "./document";
3
+ import type { ASTNode } from "./types";
4
+ export * from "./utils";
5
+ export { parse } from "./document";
6
+ export * from "./types";
7
+ export * from "./document";
8
+ export interface PlayerContentProvider {
9
+ parse(document: TextDocument): PlayerContent;
10
+ }
11
+ /** traverse a node and call the visitor for each nested item */
12
+ export declare function walk(node: ASTNode, visitor: (n: ASTNode) => Promise<boolean>): Promise<void>;
13
+ /** Get the JSON value for a node */
14
+ export declare function getNodeValue(node: ASTNode): any;
15
+ export * from "./edits";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { Diagnostic } from "vscode-languageserver-types";
2
+ import type { ParseError } from "jsonc-parser";
3
+ import type { TextDocument } from "vscode-languageserver-textdocument";
4
+ declare enum ParseErrorCode {
5
+ InvalidSymbol = 1,
6
+ InvalidNumberFormat = 2,
7
+ PropertyNameExpected = 3,
8
+ ValueExpected = 4,
9
+ ColonExpected = 5,
10
+ CommaExpected = 6,
11
+ CloseBraceExpected = 7,
12
+ CloseBracketExpected = 8,
13
+ EndOfFileExpected = 9,
14
+ InvalidCommentToken = 10,
15
+ UnexpectedEndOfComment = 11,
16
+ UnexpectedEndOfString = 12,
17
+ UnexpectedEndOfNumber = 13,
18
+ InvalidUnicode = 14,
19
+ InvalidEscapeCharacter = 15,
20
+ InvalidCharacter = 16
21
+ }
22
+ /** Just what the function says */
23
+ export declare function prettyPrintParseErrorCode(code: ParseErrorCode): string;
24
+ /** Convert any JSON parsing errors to LSP diagnostics */
25
+ export declare function convertErrorsToDiags(document: TextDocument, errors: Array<ParseError>): Array<Diagnostic>;
26
+ export {};
27
+ //# sourceMappingURL=jsonParseErrors.d.ts.map