From 33a618447cde1fc797e81043c79dc9108dfefb3e Mon Sep 17 00:00:00 2001 From: Ketan Reddy Date: Tue, 24 Feb 2026 20:25:55 -0800 Subject: [PATCH 1/4] Add static XLRs for Data Types --- .../src/datatypes/xlr/BooleanType.json | 72 ++++++++++++++++++ .../src/datatypes/xlr/CollectionType.json | 39 ++++++++++ .../src/datatypes/xlr/DateType.json | 55 ++++++++++++++ .../src/datatypes/xlr/IntegerNNType.json | 75 +++++++++++++++++++ .../src/datatypes/xlr/IntegerPosType.json | 75 +++++++++++++++++++ .../src/datatypes/xlr/IntegerType.json | 55 ++++++++++++++ .../src/datatypes/xlr/PhoneType.json | 55 ++++++++++++++ .../src/datatypes/xlr/StringType.json | 62 +++++++++++++++ .../src/datatypes/xlr/manifest.d.ts | 3 + .../static-xlrs/src/datatypes/xlr/manifest.js | 21 ++++++ .../src/datatypes/xlr/manifest.json | 26 +++++++ common/static-xlrs/src/index.d.ts | 2 + common/static-xlrs/src/index.js | 2 + 13 files changed, 542 insertions(+) create mode 100755 common/static-xlrs/src/datatypes/xlr/BooleanType.json create mode 100755 common/static-xlrs/src/datatypes/xlr/CollectionType.json create mode 100755 common/static-xlrs/src/datatypes/xlr/DateType.json create mode 100755 common/static-xlrs/src/datatypes/xlr/IntegerNNType.json create mode 100755 common/static-xlrs/src/datatypes/xlr/IntegerPosType.json create mode 100755 common/static-xlrs/src/datatypes/xlr/IntegerType.json create mode 100755 common/static-xlrs/src/datatypes/xlr/PhoneType.json create mode 100755 common/static-xlrs/src/datatypes/xlr/StringType.json create mode 100644 common/static-xlrs/src/datatypes/xlr/manifest.d.ts create mode 100755 common/static-xlrs/src/datatypes/xlr/manifest.js create mode 100755 common/static-xlrs/src/datatypes/xlr/manifest.json diff --git a/common/static-xlrs/src/datatypes/xlr/BooleanType.json b/common/static-xlrs/src/datatypes/xlr/BooleanType.json new file mode 100755 index 0000000..e38dbbf --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/BooleanType.json @@ -0,0 +1,72 @@ +{ + "source": "/private/var/tmp/_bazel_kreddy8/6fc13ccb395252816f0c23d8394e8532/sandbox/darwin-sandbox/1788/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/plugins/common-types/core/src/data-types/types.ts", + "name": "BooleanType", + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "BooleanType" + } + }, + "default": { + "required": true, + "node": { + "type": "boolean", + "const": false + } + }, + "validation": { + "required": true, + "node": { + "type": "array", + "elementType": { + "type": "any" + }, + "const": [ + { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "oneOf" + } + }, + "message": { + "required": true, + "node": { + "type": "string", + "const": "Value must be true or false" + } + }, + "options": { + "required": true, + "node": { + "type": "array", + "elementType": { + "type": "any" + }, + "const": [ + { + "type": "boolean", + "const": true + }, + { + "type": "boolean", + "const": false + } + ] + } + } + }, + "additionalProperties": false + } + ] + } + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/common/static-xlrs/src/datatypes/xlr/CollectionType.json b/common/static-xlrs/src/datatypes/xlr/CollectionType.json new file mode 100755 index 0000000..405bf6d --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/CollectionType.json @@ -0,0 +1,39 @@ +{ + "source": "/private/var/tmp/_bazel_kreddy8/6fc13ccb395252816f0c23d8394e8532/sandbox/darwin-sandbox/1788/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/plugins/common-types/core/src/data-types/types.ts", + "name": "CollectionType", + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "CollectionType" + } + }, + "validation": { + "required": true, + "node": { + "type": "array", + "elementType": { + "type": "any" + }, + "const": [ + { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "collection" + } + } + }, + "additionalProperties": false + } + ] + } + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/common/static-xlrs/src/datatypes/xlr/DateType.json b/common/static-xlrs/src/datatypes/xlr/DateType.json new file mode 100755 index 0000000..3259bc9 --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/DateType.json @@ -0,0 +1,55 @@ +{ + "source": "/private/var/tmp/_bazel_kreddy8/6fc13ccb395252816f0c23d8394e8532/sandbox/darwin-sandbox/1788/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/plugins/common-types/core/src/data-types/types.ts", + "name": "DateType", + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "DateType" + } + }, + "validation": { + "required": true, + "node": { + "type": "array", + "elementType": { + "type": "any" + }, + "const": [ + { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "string" + } + } + }, + "additionalProperties": false + } + ] + } + }, + "format": { + "required": true, + "node": { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "date" + } + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/common/static-xlrs/src/datatypes/xlr/IntegerNNType.json b/common/static-xlrs/src/datatypes/xlr/IntegerNNType.json new file mode 100755 index 0000000..9feaf0f --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/IntegerNNType.json @@ -0,0 +1,75 @@ +{ + "source": "/private/var/tmp/_bazel_kreddy8/6fc13ccb395252816f0c23d8394e8532/sandbox/darwin-sandbox/1788/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/plugins/common-types/core/src/data-types/types.ts", + "name": "IntegerNNType", + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "IntegerNNType" + } + }, + "validation": { + "required": true, + "node": { + "type": "array", + "elementType": { + "type": "any" + }, + "const": [ + { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "integer" + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "min" + } + }, + "value": { + "required": true, + "node": { + "type": "number", + "const": 0 + } + } + }, + "additionalProperties": false + } + ] + } + }, + "format": { + "required": true, + "node": { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "integer" + } + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/common/static-xlrs/src/datatypes/xlr/IntegerPosType.json b/common/static-xlrs/src/datatypes/xlr/IntegerPosType.json new file mode 100755 index 0000000..7ecd001 --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/IntegerPosType.json @@ -0,0 +1,75 @@ +{ + "source": "/private/var/tmp/_bazel_kreddy8/6fc13ccb395252816f0c23d8394e8532/sandbox/darwin-sandbox/1788/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/plugins/common-types/core/src/data-types/types.ts", + "name": "IntegerPosType", + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "IntegerPosType" + } + }, + "validation": { + "required": true, + "node": { + "type": "array", + "elementType": { + "type": "any" + }, + "const": [ + { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "integer" + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "min" + } + }, + "value": { + "required": true, + "node": { + "type": "number", + "const": 1 + } + } + }, + "additionalProperties": false + } + ] + } + }, + "format": { + "required": true, + "node": { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "integer" + } + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/common/static-xlrs/src/datatypes/xlr/IntegerType.json b/common/static-xlrs/src/datatypes/xlr/IntegerType.json new file mode 100755 index 0000000..b5cc704 --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/IntegerType.json @@ -0,0 +1,55 @@ +{ + "source": "/private/var/tmp/_bazel_kreddy8/6fc13ccb395252816f0c23d8394e8532/sandbox/darwin-sandbox/1788/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/plugins/common-types/core/src/data-types/types.ts", + "name": "IntegerType", + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "IntegerType" + } + }, + "validation": { + "required": true, + "node": { + "type": "array", + "elementType": { + "type": "any" + }, + "const": [ + { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "integer" + } + } + }, + "additionalProperties": false + } + ] + } + }, + "format": { + "required": true, + "node": { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "integer" + } + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/common/static-xlrs/src/datatypes/xlr/PhoneType.json b/common/static-xlrs/src/datatypes/xlr/PhoneType.json new file mode 100755 index 0000000..9e1623a --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/PhoneType.json @@ -0,0 +1,55 @@ +{ + "source": "/private/var/tmp/_bazel_kreddy8/6fc13ccb395252816f0c23d8394e8532/sandbox/darwin-sandbox/1788/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/plugins/common-types/core/src/data-types/types.ts", + "name": "PhoneType", + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "PhoneType" + } + }, + "validation": { + "required": true, + "node": { + "type": "array", + "elementType": { + "type": "any" + }, + "const": [ + { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "phone" + } + } + }, + "additionalProperties": false + } + ] + } + }, + "format": { + "required": true, + "node": { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "phone" + } + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/common/static-xlrs/src/datatypes/xlr/StringType.json b/common/static-xlrs/src/datatypes/xlr/StringType.json new file mode 100755 index 0000000..014768b --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/StringType.json @@ -0,0 +1,62 @@ +{ + "source": "/private/var/tmp/_bazel_kreddy8/6fc13ccb395252816f0c23d8394e8532/sandbox/darwin-sandbox/1788/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/plugins/common-types/core/src/data-types/types.ts", + "name": "StringType", + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "StringType" + } + }, + "default": { + "required": true, + "node": { + "type": "string", + "const": "" + } + }, + "validation": { + "required": true, + "node": { + "type": "array", + "elementType": { + "type": "any" + }, + "const": [ + { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "string" + } + } + }, + "additionalProperties": false + } + ] + } + }, + "format": { + "required": true, + "node": { + "type": "object", + "properties": { + "type": { + "required": true, + "node": { + "type": "string", + "const": "string" + } + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/common/static-xlrs/src/datatypes/xlr/manifest.d.ts b/common/static-xlrs/src/datatypes/xlr/manifest.d.ts new file mode 100644 index 0000000..c8ad3f1 --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/manifest.d.ts @@ -0,0 +1,3 @@ +import type { TSManifest } from "@player-tools/xlr"; + +export type CommonType = TSManifest; diff --git a/common/static-xlrs/src/datatypes/xlr/manifest.js b/common/static-xlrs/src/datatypes/xlr/manifest.js new file mode 100755 index 0000000..0f28f68 --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/manifest.js @@ -0,0 +1,21 @@ +const BooleanType = require("./BooleanType.json") +const IntegerType = require("./IntegerType.json") +const IntegerPosType = require("./IntegerPosType.json") +const IntegerNNType = require("./IntegerNNType.json") +const StringType = require("./StringType.json") +const CollectionType = require("./CollectionType.json") +const DateType = require("./DateType.json") +const PhoneType = require("./PhoneType.json") + + module.exports = { + "pluginName": "CommonTypes", + "capabilities": { + "Assets":[], + "Views":[], + "Expressions":[], + "DataTypes":[BooleanType,IntegerType,IntegerPosType,IntegerNNType,StringType,CollectionType,DateType,PhoneType], + }, + "customPrimitives": [ + "Expression","Asset","Binding","AssetWrapper","Schema.DataType","ExpressionHandler" + ] + } diff --git a/common/static-xlrs/src/datatypes/xlr/manifest.json b/common/static-xlrs/src/datatypes/xlr/manifest.json new file mode 100755 index 0000000..b27e3ee --- /dev/null +++ b/common/static-xlrs/src/datatypes/xlr/manifest.json @@ -0,0 +1,26 @@ +{ + "pluginName": "CommonTypes", + "capabilities": { + "Assets": [], + "Views": [], + "Expressions": [], + "DataTypes": [ + "BooleanType", + "IntegerType", + "IntegerPosType", + "IntegerNNType", + "StringType", + "CollectionType", + "DateType", + "PhoneType" + ] + }, + "customPrimitives": [ + "Expression", + "Asset", + "Binding", + "AssetWrapper", + "Schema.DataType", + "ExpressionHandler" + ] +} \ No newline at end of file diff --git a/common/static-xlrs/src/index.d.ts b/common/static-xlrs/src/index.d.ts index 2232e8a..3ebc1e8 100644 --- a/common/static-xlrs/src/index.d.ts +++ b/common/static-xlrs/src/index.d.ts @@ -1,7 +1,9 @@ import type { Types } from "./core/xlr/manifest"; import type { CommonExpressions } from "./expression/xlr/manifest"; +import type { CommonTypes } from "./datatypes/xlr/manifest"; import type { ReferenceAssetsWebPluginManifest } from "./plugin/xlr/manifest"; export const Types: Types; export const CommonExpressions: CommonExpressions; +export const CommonTypes: CommonTypes; export const ReferenceAssetsWebPluginManifest: ReferenceAssetsWebPluginManifest; diff --git a/common/static-xlrs/src/index.js b/common/static-xlrs/src/index.js index 530e449..f5822a4 100644 --- a/common/static-xlrs/src/index.js +++ b/common/static-xlrs/src/index.js @@ -1,9 +1,11 @@ const Types = require("./core/xlr/manifest"); const CommonExpressions = require("./expression/xlr/manifest"); +const CommonTypes = require("./datatypes/xlr/manifest"); const ReferenceAssetsWebPluginManifest = require("./plugin/xlr/manifest"); module.exports = { Types, CommonExpressions, + CommonTypes, ReferenceAssetsWebPluginManifest, }; From bf9d79e6b2ae527f8cce2ec6e24b521b1c86def6 Mon Sep 17 00:00:00 2001 From: Ketan Reddy Date: Tue, 24 Feb 2026 20:52:10 -0800 Subject: [PATCH 2/4] First pass at adding schema validation to the LSP --- .../__snapshots__/service.test.ts.snap | 14 - .../src/__tests__/service.test.ts | 32 +- lsp/json-language-service/src/constants.ts | 2 + .../schema-validation-plugin.test.ts | 972 ++++++++++++++++++ .../src/plugins/schema-validation-plugin.ts | 249 +++++ .../src/plugins/xlr-plugin.ts | 21 +- lsp/json-language-service/src/utils.ts | 19 + 7 files changed, 1244 insertions(+), 65 deletions(-) create mode 100644 lsp/json-language-service/src/plugins/__tests__/schema-validation-plugin.test.ts create mode 100644 lsp/json-language-service/src/plugins/schema-validation-plugin.ts diff --git a/lsp/json-language-service/src/__tests__/__snapshots__/service.test.ts.snap b/lsp/json-language-service/src/__tests__/__snapshots__/service.test.ts.snap index 3a334d3..56c0ac2 100644 --- a/lsp/json-language-service/src/__tests__/__snapshots__/service.test.ts.snap +++ b/lsp/json-language-service/src/__tests__/__snapshots__/service.test.ts.snap @@ -111,20 +111,6 @@ exports[`player language service > hover > basic hover docs 3`] = ` exports[`player language service > validation > throws AssetWrapper errors 1`] = ` [ - { - "message": "Content Validation Error - type: Expected type "string" but got "null"", - "range": { - "end": { - "character": 28, - "line": 20, - }, - "start": { - "character": 16, - "line": 20, - }, - }, - "severity": 1, - }, { "message": "Content Validation Error - missing: Property "BEGIN" missing from type "Navigation"", "range": { diff --git a/lsp/json-language-service/src/__tests__/service.test.ts b/lsp/json-language-service/src/__tests__/service.test.ts index 9a21ea2..26ef41c 100644 --- a/lsp/json-language-service/src/__tests__/service.test.ts +++ b/lsp/json-language-service/src/__tests__/service.test.ts @@ -69,37 +69,7 @@ describe("player language service", () => { } ], "data": {}, - "navigation": {}, - "schema": { - "ROOT": { - "application": { - "type": null - }, - "foo": { - "type": "fooType" - } - }, - "fooType": { - "bar": { - "type": "barType", - "validation": [ - { - "type": "required", - "severity": "error" - } - ] - }, - "baz": { - "type": "bazType", - "validation": [ - { - "type": "required", - "message": "this is my own message" - } - ] - } - } - } + "navigation": {} } `, ); diff --git a/lsp/json-language-service/src/constants.ts b/lsp/json-language-service/src/constants.ts index 9580ff0..32d0ca0 100644 --- a/lsp/json-language-service/src/constants.ts +++ b/lsp/json-language-service/src/constants.ts @@ -4,6 +4,7 @@ import { TransformFunction } from "@xlr-lib/xlr"; import type { PlayerLanguageServicePlugin } from "."; import { AssetWrapperArrayPlugin } from "./plugins/asset-wrapper-array-plugin"; import { SchemaInfoPlugin } from "./plugins/binding-schema-plugin"; +import { SchemaValidationPlugin } from "./plugins/schema-validation-plugin"; import { XLRPlugin } from "./plugins/xlr-plugin"; import { DuplicateIDPlugin } from "./plugins/duplicate-id-plugin"; import { MissingAssetWrapperPlugin } from "./plugins/missing-asset-wrapper-plugin"; @@ -20,6 +21,7 @@ export const PLUGINS: Array = [ new DuplicateIDPlugin(), new ViewNodePlugin(), new SchemaInfoPlugin(), + new SchemaValidationPlugin(), new AssetWrapperArrayPlugin(), new NavStatePlugin(), new MissingAssetWrapperPlugin(), diff --git a/lsp/json-language-service/src/plugins/__tests__/schema-validation-plugin.test.ts b/lsp/json-language-service/src/plugins/__tests__/schema-validation-plugin.test.ts new file mode 100644 index 0000000..4586b8d --- /dev/null +++ b/lsp/json-language-service/src/plugins/__tests__/schema-validation-plugin.test.ts @@ -0,0 +1,972 @@ +import { test, expect, describe, beforeEach } from "vitest"; +import { CommonTypes, Types } from "@player-lang/static-xlrs"; +import { PlayerLanguageService } from "../.."; +import { toTextDocument } from "../../utils"; + +describe("SchemaValidationPlugin", () => { + let service: PlayerLanguageService; + + beforeEach(async () => { + service = new PlayerLanguageService(); + await service.setAssetTypesFromModule([Types, CommonTypes]); + }); + + describe("schema structure validation", () => { + test("reports error when schema is missing ROOT", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + SomeType: { + foo: { type: "SomeType" }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Content Validation Error - missing: Property "ROOT" missing from type "Schema"", + "range": { + "end": { + "character": 3, + "line": 12, + }, + "start": { + "character": 12, + "line": 6, + }, + }, + "severity": 1, + }, + { + "message": "Schema must have a "ROOT" key.", + "range": { + "end": { + "character": 3, + "line": 12, + }, + "start": { + "character": 12, + "line": 6, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("reports error when DataType is missing type property", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + application: { + validation: [], + }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Content Validation Error - missing: Property "type" missing from type "DataType"", + "range": { + "end": { + "character": 7, + "line": 10, + }, + "start": { + "character": 21, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema.DataType must have a "type" property (reference to schema or XLR type).", + "range": { + "end": { + "character": 7, + "line": 10, + }, + "start": { + "character": 21, + "line": 8, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("reports error when type is not a string", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + application: { + type: 123, + }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Content Validation Error - type: Expected type "string" but got "number"", + "range": { + "end": { + "character": 19, + "line": 9, + }, + "start": { + "character": 8, + "line": 9, + }, + }, + "severity": 1, + }, + { + "message": "Schema "type" must be a string (schema type name or XLR type name).", + "range": { + "end": { + "character": 19, + "line": 9, + }, + "start": { + "character": 16, + "line": 9, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("reports error when isArray is not a boolean", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + items: { + type: "StringType", + isArray: "yes", + }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Content Validation Error - type: Expected type "boolean" but got "string"", + "range": { + "end": { + "character": 24, + "line": 10, + }, + "start": { + "character": 8, + "line": 10, + }, + }, + "severity": 1, + }, + { + "message": "Schema.DataType "isArray" must be a boolean.", + "range": { + "end": { + "character": 24, + "line": 10, + }, + "start": { + "character": 19, + "line": 10, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "default" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 11, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "validation" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 11, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "format" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 11, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - value: Unexpected properties on "StringType": isArray", + "range": { + "end": { + "character": 7, + "line": 11, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("reports error when validation is not an array", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + field: { + type: "TypeA", + validation: "not-an-array", + }, + }, + TypeA: { + nested: { type: "StringType" }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Content Validation Error - type: Expected an array but got an "string"", + "range": { + "end": { + "character": 36, + "line": 10, + }, + "start": { + "character": 22, + "line": 10, + }, + }, + "severity": 1, + }, + { + "message": "Schema.DataType "validation" must be an array.", + "range": { + "end": { + "character": 36, + "line": 10, + }, + "start": { + "character": 22, + "line": 10, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "default" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 16, + }, + "start": { + "character": 16, + "line": 14, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "validation" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 16, + }, + "start": { + "character": 16, + "line": 14, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "format" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 16, + }, + "start": { + "character": 16, + "line": 14, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("reports error when format is not an object", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + field: { + type: "TypeA", + format: "not-an-object", + }, + }, + TypeA: { + nested: { type: "StringType" }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Content Validation Error - type: Expected an object but got an "string"", + "range": { + "end": { + "character": 33, + "line": 10, + }, + "start": { + "character": 18, + "line": 10, + }, + }, + "severity": 1, + }, + { + "message": "Schema.DataType "format" must be an object.", + "range": { + "end": { + "character": 33, + "line": 10, + }, + "start": { + "character": 18, + "line": 10, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "default" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 16, + }, + "start": { + "character": 16, + "line": 14, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "validation" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 16, + }, + "start": { + "character": 16, + "line": 14, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "format" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 16, + }, + "start": { + "character": 16, + "line": 14, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("reports error when isRecord is not a boolean", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + field: { + type: "StringType", + isRecord: "yes", + }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Schema.DataType "isRecord" must be a boolean.", + "range": { + "end": { + "character": 25, + "line": 10, + }, + "start": { + "character": 20, + "line": 10, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "default" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 11, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "validation" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 11, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "format" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 11, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - value: Unexpected properties on "StringType": isRecord", + "range": { + "end": { + "character": 7, + "line": 11, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("reports error when both isArray and isRecord are true", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + field: { + type: "StringType", + isArray: true, + isRecord: true, + }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Schema.DataType cannot have both "isArray" and "isRecord" true.", + "range": { + "end": { + "character": 7, + "line": 12, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "default" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 12, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "validation" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 12, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "format" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 12, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - value: Unexpected properties on "StringType": isArray, isRecord", + "range": { + "end": { + "character": 7, + "line": 12, + }, + "start": { + "character": 15, + "line": 8, + }, + }, + "severity": 1, + }, + ] + `); + }); + }); + + describe("schema type reference validation", () => { + test("reports error for unknown type reference (not in schema, not in XLR)", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + foo: { + type: "NonExistentXLRType", + }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Unknown schema type "NonExistentXLRType". Type must be a schema type (key in this schema) or an XLR type loaded in the SDK.", + "range": { + "end": { + "character": 36, + "line": 9, + }, + "start": { + "character": 16, + "line": 9, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("accepts type reference to XLR-loaded type when that type is in the SDK", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + name: { type: "StringType" }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Schema DataType "StringType" - missing: Property "default" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 10, + }, + "start": { + "character": 14, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "validation" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 10, + }, + "start": { + "character": 14, + "line": 8, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "StringType" - missing: Property "format" missing from type "StringType"", + "range": { + "end": { + "character": 7, + "line": 10, + }, + "start": { + "character": 14, + "line": 8, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("reports errors for multiple unknown type references", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + a: { type: "UnknownA" }, + b: { type: "UnknownB" }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Unknown schema type "UnknownA". Type must be a schema type (key in this schema) or an XLR type loaded in the SDK.", + "range": { + "end": { + "character": 26, + "line": 9, + }, + "start": { + "character": 16, + "line": 9, + }, + }, + "severity": 1, + }, + { + "message": "Unknown schema type "UnknownB". Type must be a schema type (key in this schema) or an XLR type loaded in the SDK.", + "range": { + "end": { + "character": 26, + "line": 12, + }, + "start": { + "character": 16, + "line": 12, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("reports no Schema DataType errors when DataType conforms to XLR (StringType)", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + name: { + type: "StringType", + default: "", + validation: [], + format: { type: "string" }, + }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Content Validation Error - type: Expected an object but got an "string"", + "range": { + "end": { + "character": 21, + "line": 10, + }, + "start": { + "character": 19, + "line": 10, + }, + }, + "severity": 1, + }, + ] + `); + }); + + test("reports Schema DataType error when BooleanType payload has wrong type for property", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + schema: { + ROOT: { + flag: { + type: "BooleanType", + default: "not-a-boolean", + }, + }, + }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "message": "Content Validation Error - type: Expected an object but got an "string"", + "range": { + "end": { + "character": 34, + "line": 10, + }, + "start": { + "character": 19, + "line": 10, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "BooleanType" - type: Expected type "boolean" but got "string"", + "range": { + "end": { + "character": 34, + "line": 10, + }, + "start": { + "character": 8, + "line": 10, + }, + }, + "severity": 1, + }, + { + "message": "Schema DataType "BooleanType" - missing: Property "validation" missing from type "BooleanType"", + "range": { + "end": { + "character": 7, + "line": 11, + }, + "start": { + "character": 14, + "line": 8, + }, + }, + "severity": 1, + }, + ] + `); + }); + }); + + describe("flow without schema", () => { + test("does not add schema errors when schema is absent", async () => { + const document = toTextDocument( + JSON.stringify( + { + id: "foo", + views: [], + navigation: { BEGIN: "FLOW1" }, + }, + null, + 2, + ), + ); + const diagnostics = await service.validateTextDocument(document); + expect(diagnostics).toMatchInlineSnapshot(`[]`); + }); + }); +}); diff --git a/lsp/json-language-service/src/plugins/schema-validation-plugin.ts b/lsp/json-language-service/src/plugins/schema-validation-plugin.ts new file mode 100644 index 0000000..6c6d19a --- /dev/null +++ b/lsp/json-language-service/src/plugins/schema-validation-plugin.ts @@ -0,0 +1,249 @@ +import type { ValidationMessage } from "@xlr-lib/xlr-sdk"; +import { DiagnosticSeverity } from "vscode-languageserver-types"; +import type { PlayerLanguageService, PlayerLanguageServicePlugin } from ".."; +import type { ValidationContext, ASTVisitor } from "../types"; +import type { ContentASTNode, ObjectASTNode, StringASTNode } from "../parser"; +import { findErrorNode, getProperty } from "../utils"; +import type { XLRSDK } from "@xlr-lib/xlr-sdk"; + +/** + * Collects all type names defined at the top level of the schema (ROOT and + * any custom type names). Used to validate that "type" references either + * point to a schema type or an XLR-loaded type. + */ +function getSchemaTypeNames(schemaObj: ObjectASTNode): Set { + const names = new Set(); + for (const prop of schemaObj.properties) { + const key = prop.keyNode?.value; + if (typeof key === "string") { + names.add(key); + } + } + return names; +} + +/** + * Validates that a Schema.DataType object has the proper structure + */ +function validateDataTypeStructure( + dataTypeNode: ObjectASTNode, + validationContext: ValidationContext, +): void { + const validationProp = getProperty(dataTypeNode, "validation"); + if (validationProp?.valueNode && validationProp.valueNode.type !== "array") { + validationContext.addViolation({ + node: validationProp.valueNode, + message: `Schema.DataType "validation" must be an array.`, + severity: DiagnosticSeverity.Error, + }); + } + + const formatProp = getProperty(dataTypeNode, "format"); + if (formatProp?.valueNode && formatProp.valueNode.type !== "object") { + validationContext.addViolation({ + node: formatProp.valueNode, + message: `Schema.DataType "format" must be an object.`, + severity: DiagnosticSeverity.Error, + }); + } + + const isArrayProp = getProperty(dataTypeNode, "isArray"); + const isRecordProp = getProperty(dataTypeNode, "isRecord"); + if (isArrayProp?.valueNode && isArrayProp.valueNode.type !== "boolean") { + validationContext.addViolation({ + node: isArrayProp.valueNode, + message: `Schema.DataType "isArray" must be a boolean.`, + severity: DiagnosticSeverity.Error, + }); + } + if (isRecordProp?.valueNode && isRecordProp.valueNode.type !== "boolean") { + validationContext.addViolation({ + node: isRecordProp.valueNode, + message: `Schema.DataType "isRecord" must be a boolean.`, + severity: DiagnosticSeverity.Error, + }); + } + if ( + isArrayProp?.valueNode && + isRecordProp?.valueNode && + (isArrayProp.valueNode as { value?: boolean }).value === true && + (isRecordProp.valueNode as { value?: boolean }).value === true + ) { + validationContext.addViolation({ + node: dataTypeNode, + message: `Schema.DataType cannot have both "isArray" and "isRecord" true.`, + severity: DiagnosticSeverity.Error, + }); + } +} + +/** + * Validates a single schema node (e.g. ROOT or a custom type): each property + * must be an object with a "type" field (Schema.DataType), full structure + * validation, known type reference (schema or XLR), and when the type is an + * XLR type, validates the DataType object against the XLR definition via the SDK. + */ +function validateSchemaNode( + node: ObjectASTNode, + schemaTypeNames: Set, + sdk: XLRSDK, + validationContext: ValidationContext, +): void { + for (const prop of node.properties) { + const valueNode = prop.valueNode; + if (!(valueNode && valueNode.type === "object")) { + if (valueNode) { + validationContext.addViolation({ + node: valueNode, + message: `Schema property "${prop.keyNode.value}" must be an object (Schema.DataType) with a "type" field.`, + severity: DiagnosticSeverity.Error, + }); + } + continue; + } + + const dataTypeNode = valueNode as ObjectASTNode; + const typeProp = getProperty(dataTypeNode, "type"); + if (!typeProp) { + validationContext.addViolation({ + node: valueNode, + message: `Schema.DataType must have a "type" property (reference to schema or XLR type).`, + severity: DiagnosticSeverity.Error, + }); + continue; + } + + const typeValueNode = typeProp.valueNode; + if (!typeValueNode || typeValueNode.type !== "string") { + validationContext.addViolation({ + node: typeValueNode ?? typeProp, + message: `Schema "type" must be a string (schema type name or XLR type name).`, + severity: DiagnosticSeverity.Error, + }); + continue; + } + + const typeName = (typeValueNode as StringASTNode).value; + const isSchemaType = schemaTypeNames.has(typeName); + const isXLRType = sdk.hasType(typeName); + + if (!isSchemaType && !isXLRType) { + validationContext.addViolation({ + node: typeValueNode, + message: `Unknown schema type "${typeName}". Type must be a schema type (key in this schema) or an XLR type loaded in the SDK.`, + severity: DiagnosticSeverity.Error, + }); + } + + /** Full DataType structure per @player-ui/types */ + validateDataTypeStructure(dataTypeNode, validationContext); + + /** When type is an XLR type, validate the DataType object against the XLR definition */ + if (isXLRType) { + const issues: ValidationMessage[] = sdk.validateByName( + typeName, + dataTypeNode.jsonNode, + ); + issues.forEach((issue) => { + validationContext.addViolation({ + node: findErrorNode(dataTypeNode, issue.node), + message: + issue.severity === DiagnosticSeverity.Error + ? `Schema DataType "${typeName}" - ${issue.type}: ${issue.message}` + : issue.message, + severity: issue.severity as DiagnosticSeverity, + }); + }); + } + } +} + +/** + * Validates the Flow's schema property: structure per Schema.Schema, + * type references, full DataType structure, and XLR shape when type is an XLR type. + */ +function validateFlowSchema( + contentNode: ContentASTNode, + sdk: XLRSDK, + validationContext: ValidationContext, +): void { + const schemaProp = getProperty(contentNode, "schema"); + if (!schemaProp?.valueNode) { + return; + } + + const schemaValue = schemaProp.valueNode; + if (schemaValue.type !== "object") { + validationContext.addViolation({ + node: schemaValue, + message: `Flow "schema" must be an object with at least a "ROOT" key.`, + severity: DiagnosticSeverity.Error, + }); + return; + } + + const schemaObj = schemaValue as ObjectASTNode; + const hasRoot = schemaObj.properties.some((p) => p.keyNode.value === "ROOT"); + + if (!hasRoot) { + validationContext.addViolation({ + node: schemaValue, + message: `Schema must have a "ROOT" key.`, + severity: DiagnosticSeverity.Error, + }); + } + + const schemaTypeNames = getSchemaTypeNames(schemaObj); + + for (const prop of schemaObj.properties) { + const nodeValue = prop.valueNode; + if (!nodeValue || nodeValue.type !== "object") { + if (nodeValue) { + validationContext.addViolation({ + node: nodeValue, + message: `Schema node "${prop.keyNode.value}" must be an object.`, + severity: DiagnosticSeverity.Error, + }); + } + continue; + } + + validateSchemaNode( + nodeValue as ObjectASTNode, + schemaTypeNames, + sdk, + validationContext, + ); + } +} + +/** + * Plugin that registers schema validation with the Player Language Service. + */ +export class SchemaValidationPlugin implements PlayerLanguageServicePlugin { + name = "schema-validation"; + + /** Resolved when CommonTypes have been loaded into the XLR SDK (once per plugin apply) */ + private commonTypesLoaded: Promise | null = null; + + apply(service: PlayerLanguageService): void { + service.hooks.validate.tap(this.name, async (_ctx, validationContext) => { + await this.commonTypesLoaded; + validationContext.useASTVisitor( + this.createValidationVisitor(service, validationContext), + ); + }); + } + + private createValidationVisitor( + service: PlayerLanguageService, + validationContext: ValidationContext, + ): ASTVisitor { + const sdk = service.XLRService.XLRSDK; + return { + ContentNode: (contentNode) => { + validateFlowSchema(contentNode, sdk, validationContext); + }, + }; + } +} diff --git a/lsp/json-language-service/src/plugins/xlr-plugin.ts b/lsp/json-language-service/src/plugins/xlr-plugin.ts index 6e346ae..744f7ed 100644 --- a/lsp/json-language-service/src/plugins/xlr-plugin.ts +++ b/lsp/json-language-service/src/plugins/xlr-plugin.ts @@ -14,7 +14,7 @@ import type { PlayerLanguageServicePlugin, ValidationContext, } from ".."; -import { mapFlowStateToType } from "../utils"; +import { findErrorNode, mapFlowStateToType } from "../utils"; import type { ASTNode, ObjectASTNode } from "../parser"; import type { EnhancedDocumentContextWithPosition } from "../types"; @@ -22,25 +22,6 @@ function isError(issue: ValidationMessage): boolean { return issue.severity === DiagnosticSeverity.Error; } -/** BFS search to find a JSONC node in children of some AST Node */ -const findErrorNode = (rootNode: ASTNode, nodeToFind: Node): ASTNode => { - const children: Array = [rootNode]; - - while (children.length > 0) { - const child = children.pop() as ASTNode; - if (child.jsonNode === nodeToFind) { - return child; - } - - if (child.children) { - children.push(...child.children); - } - } - - // if the node can't be found return the original - return rootNode; -}; - /** * Translates an SDK severity level to an LSP severity level * Relies on both levels having the values associated to the underlying levels diff --git a/lsp/json-language-service/src/utils.ts b/lsp/json-language-service/src/utils.ts index 9d33ac3..0b87161 100644 --- a/lsp/json-language-service/src/utils.ts +++ b/lsp/json-language-service/src/utils.ts @@ -1,6 +1,7 @@ import { Range, Location } from "vscode-languageserver-types"; import { TextDocument } from "vscode-languageserver-textdocument"; import detectIndent from "detect-indent"; +import type { Node } from "jsonc-parser"; import type { ASTNode, PlayerContent, @@ -141,3 +142,21 @@ export function mapFlowStateToType( return flowXLR; } +/** BFS search to find a JSONC node in children of some AST Node */ +export const findErrorNode = (rootNode: ASTNode, nodeToFind: Node): ASTNode => { + const children: Array = [rootNode]; + + while (children.length > 0) { + const child = children.pop() as ASTNode; + if (child.jsonNode === nodeToFind) { + return child; + } + + if (child.children) { + children.push(...child.children); + } + } + + // if the node can't be found return the original + return rootNode; +}; From ca7f7c85bd727761e4dc8483239d5f2cb6e8b094 Mon Sep 17 00:00:00 2001 From: Ketan Reddy Date: Wed, 25 Feb 2026 15:48:42 -0800 Subject: [PATCH 3/4] cleanup build files --- dsl/javascript/BUILD | 4 ---- dsl/react/BUILD | 4 ---- generators/javascript/BUILD | 3 +-- lsp/json-language-server/BUILD | 4 ---- 4 files changed, 1 insertion(+), 14 deletions(-) diff --git a/dsl/javascript/BUILD b/dsl/javascript/BUILD index 606eba3..f815bc1 100644 --- a/dsl/javascript/BUILD +++ b/dsl/javascript/BUILD @@ -11,10 +11,6 @@ vitest_config(name = "vitest_config") js_pipeline( package_name = "@player-lang/functional-dsl", srcs = glob(["src/**/*"]), - test_deps = [ - "//:node_modules", - "//:vitest_config", - ], deps = [ "//:node_modules/@player-ui/types", "//:node_modules/dequal", diff --git a/dsl/react/BUILD b/dsl/react/BUILD index b0ae309..8b27b8c 100644 --- a/dsl/react/BUILD +++ b/dsl/react/BUILD @@ -14,10 +14,6 @@ js_pipeline( "//:node_modules/react", "//:node_modules/@types/react", ], - test_deps = [ - "//:node_modules", - "//:vitest_config", - ], deps = [ "//:node_modules/@player-ui/player", "//:node_modules/@player-ui/types", diff --git a/generators/javascript/BUILD b/generators/javascript/BUILD index 691a956..f86f8fd 100644 --- a/generators/javascript/BUILD +++ b/generators/javascript/BUILD @@ -11,8 +11,6 @@ vitest_config(name = "vitest_config") js_pipeline( package_name = "@player-lang/functional-dsl-generator", test_deps = [ - "//:node_modules", - "//:vitest_config", ":node_modules/@player-lang/test-utils", "//:node_modules/@xlr-lib/xlr-converters", ":node_modules/@player-lang/functional-dsl", @@ -21,6 +19,7 @@ js_pipeline( "//:node_modules/@xlr-lib/xlr", "//:node_modules/@xlr-lib/xlr-utils", "//:node_modules/ts-morph", + "//:node_modules/typescript", ], peer_deps = [ "//:node_modules/@player-ui/types", diff --git a/lsp/json-language-server/BUILD b/lsp/json-language-server/BUILD index 9c62933..99fcd04 100644 --- a/lsp/json-language-server/BUILD +++ b/lsp/json-language-server/BUILD @@ -10,10 +10,6 @@ vitest_config(name = "vitest_config") js_pipeline( package_name = "@player-lang/json-language-server", - test_deps = [ - "//:node_modules", - "//:vitest_config", - ], deps = [ ":node_modules/@player-lang/json-language-service", "//:node_modules/vscode-languageserver", From 492c272c81ee4078a5f3c51967b4cab86cf296bd Mon Sep 17 00:00:00 2001 From: Ketan Reddy Date: Wed, 25 Feb 2026 17:15:03 -0800 Subject: [PATCH 4/4] Make XLR deps peer --- generators/javascript/BUILD | 4 ++-- lsp/json-language-service/BUILD | 8 +++++--- lsp/typescript-expression-plugin/BUILD | 8 +++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/generators/javascript/BUILD b/generators/javascript/BUILD index f86f8fd..e527bcb 100644 --- a/generators/javascript/BUILD +++ b/generators/javascript/BUILD @@ -16,12 +16,12 @@ js_pipeline( ":node_modules/@player-lang/functional-dsl", ], deps = [ - "//:node_modules/@xlr-lib/xlr", - "//:node_modules/@xlr-lib/xlr-utils", "//:node_modules/ts-morph", "//:node_modules/typescript", ], peer_deps = [ + "//:node_modules/@xlr-lib/xlr", + "//:node_modules/@xlr-lib/xlr-utils", "//:node_modules/@player-ui/types", ] ) diff --git a/lsp/json-language-service/BUILD b/lsp/json-language-service/BUILD index 853d0a4..3600d93 100644 --- a/lsp/json-language-service/BUILD +++ b/lsp/json-language-service/BUILD @@ -14,9 +14,6 @@ js_pipeline( ":node_modules/@player-lang/static-xlrs", ] + COMMON_TEST_DEPS, deps = [ - "//:node_modules/@xlr-lib/xlr", - "//:node_modules/@xlr-lib/xlr-sdk", - "//:node_modules/@xlr-lib/xlr-utils", "//:node_modules/@player-ui/player", "//:node_modules/detect-indent", "//:node_modules/jsonc-parser", @@ -26,4 +23,9 @@ js_pipeline( "//:node_modules/vscode-languageserver-textdocument", "//:node_modules/vscode-languageserver-types", ], + peer_deps = [ + "//:node_modules/@xlr-lib/xlr", + "//:node_modules/@xlr-lib/xlr-sdk", + "//:node_modules/@xlr-lib/xlr-utils", + ] ) diff --git a/lsp/typescript-expression-plugin/BUILD b/lsp/typescript-expression-plugin/BUILD index 3b28681..8497c4d 100644 --- a/lsp/typescript-expression-plugin/BUILD +++ b/lsp/typescript-expression-plugin/BUILD @@ -15,13 +15,15 @@ js_pipeline( ":node_modules/@player-lang/static-xlrs", ] + COMMON_TEST_DEPS, deps = [ - "//:node_modules/@xlr-lib/xlr", - "//:node_modules/@xlr-lib/xlr-sdk", - "//:node_modules/@xlr-lib/xlr-utils", "//:node_modules/@player-ui/player", "//:node_modules/jsonc-parser", "//:node_modules/typescript-template-language-service-decorator", "//:node_modules/vscode-languageserver-types", "//:node_modules/typescript", ], + peer_deps = [ + "//:node_modules/@xlr-lib/xlr", + "//:node_modules/@xlr-lib/xlr-sdk", + "//:node_modules/@xlr-lib/xlr-utils", + ] )