@player-tools/metrics-output-plugin 0.12.1-next.1 → 0.12.1-next.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/dist/cjs/index.cjs +64 -32
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.legacy-esm.js +64 -32
- package/dist/index.mjs +64 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/__tests__/metrics-output-plugin.test.ts +608 -0
- package/src/metrics-output.ts +98 -66
- package/src/types.ts +21 -0
- package/types/metrics-output.d.ts +6 -15
- package/types/types.d.ts +13 -0
|
@@ -778,4 +778,612 @@ describe("WriteMetricsPlugin", () => {
|
|
|
778
778
|
// Clean up
|
|
779
779
|
fs.unlinkSync(outputPath);
|
|
780
780
|
});
|
|
781
|
+
|
|
782
|
+
test("rootProperties function error is handled and recorded", async () => {
|
|
783
|
+
const service = new PlayerLanguageService();
|
|
784
|
+
|
|
785
|
+
service.addLSPPlugin(
|
|
786
|
+
new MetricsOutput({
|
|
787
|
+
outputDir: TEST_DIR,
|
|
788
|
+
fileName: "rootprops_error",
|
|
789
|
+
rootProperties: () => {
|
|
790
|
+
throw new Error("boom");
|
|
791
|
+
},
|
|
792
|
+
stats: { ok: 1 },
|
|
793
|
+
}),
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
await service.setAssetTypesFromModule([
|
|
797
|
+
Types,
|
|
798
|
+
ReferenceAssetsWebPluginManifest,
|
|
799
|
+
]);
|
|
800
|
+
|
|
801
|
+
const doc = TextDocument.create("file:///err.json", "json", 1, "{}");
|
|
802
|
+
await service.validateTextDocument(doc);
|
|
803
|
+
|
|
804
|
+
const out = path.join(TEST_DIR, "rootprops_error.json");
|
|
805
|
+
expect(fs.existsSync(out)).toBe(true);
|
|
806
|
+
|
|
807
|
+
const json = JSON.parse(fs.readFileSync(out, "utf-8"));
|
|
808
|
+
// The catch assigns an error object at the root
|
|
809
|
+
expect(json).toHaveProperty("error");
|
|
810
|
+
expect(json.content).toHaveProperty("/err.json");
|
|
811
|
+
|
|
812
|
+
fs.unlinkSync(out);
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
test("defaults outputDir to process.cwd()", async () => {
|
|
816
|
+
const service = new PlayerLanguageService();
|
|
817
|
+
const tempDir = path.resolve("target_default");
|
|
818
|
+
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true });
|
|
819
|
+
|
|
820
|
+
const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(tempDir);
|
|
821
|
+
|
|
822
|
+
service.addLSPPlugin(
|
|
823
|
+
new MetricsOutput({
|
|
824
|
+
// no outputDir on purpose
|
|
825
|
+
fileName: "default_dir",
|
|
826
|
+
stats: { a: 1 },
|
|
827
|
+
}),
|
|
828
|
+
);
|
|
829
|
+
|
|
830
|
+
await service.setAssetTypesFromModule([
|
|
831
|
+
Types,
|
|
832
|
+
ReferenceAssetsWebPluginManifest,
|
|
833
|
+
]);
|
|
834
|
+
|
|
835
|
+
const doc = TextDocument.create("file:///default.json", "json", 1, "{}");
|
|
836
|
+
await service.validateTextDocument(doc);
|
|
837
|
+
|
|
838
|
+
const out = path.join(tempDir, "default_dir.json");
|
|
839
|
+
expect(fs.existsSync(out)).toBe(true);
|
|
840
|
+
|
|
841
|
+
// cleanup
|
|
842
|
+
fs.unlinkSync(out);
|
|
843
|
+
fs.rmdirSync(tempDir);
|
|
844
|
+
cwdSpy.mockRestore();
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
test("defaults fileName to 'metrics' when omitted", async () => {
|
|
848
|
+
const service = new PlayerLanguageService();
|
|
849
|
+
const tempDir = path.resolve("target_default_name");
|
|
850
|
+
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true });
|
|
851
|
+
|
|
852
|
+
service.addLSPPlugin(
|
|
853
|
+
new MetricsOutput({
|
|
854
|
+
outputDir: tempDir, // no fileName on purpose
|
|
855
|
+
stats: { a: 1 },
|
|
856
|
+
}),
|
|
857
|
+
);
|
|
858
|
+
|
|
859
|
+
await service.setAssetTypesFromModule([
|
|
860
|
+
Types,
|
|
861
|
+
ReferenceAssetsWebPluginManifest,
|
|
862
|
+
]);
|
|
863
|
+
|
|
864
|
+
const doc = TextDocument.create(
|
|
865
|
+
"file:///default-name.json",
|
|
866
|
+
"json",
|
|
867
|
+
1,
|
|
868
|
+
"{}",
|
|
869
|
+
);
|
|
870
|
+
await service.validateTextDocument(doc);
|
|
871
|
+
|
|
872
|
+
expect(fs.existsSync(path.join(tempDir, "metrics.json"))).toBe(true);
|
|
873
|
+
|
|
874
|
+
fs.unlinkSync(path.join(tempDir, "metrics.json"));
|
|
875
|
+
fs.rmdirSync(tempDir);
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
test("treats valid non-object existing metrics as empty", async () => {
|
|
879
|
+
const dir = path.resolve("target_non_object");
|
|
880
|
+
const name = "preexisting_non_object";
|
|
881
|
+
const outPath = path.join(dir, `${name}.json`);
|
|
882
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
883
|
+
|
|
884
|
+
// Seed a valid JSON that is NOT an object → hits the else branch (': {}')
|
|
885
|
+
fs.writeFileSync(outPath, "123", "utf-8"); // could also be "true", "\"str\"", or "null"
|
|
886
|
+
|
|
887
|
+
const service = new PlayerLanguageService();
|
|
888
|
+
service.addLSPPlugin(
|
|
889
|
+
new MetricsOutput({
|
|
890
|
+
outputDir: dir,
|
|
891
|
+
fileName: name,
|
|
892
|
+
stats: { metric: 42 },
|
|
893
|
+
}),
|
|
894
|
+
);
|
|
895
|
+
await service.setAssetTypesFromModule([
|
|
896
|
+
Types,
|
|
897
|
+
ReferenceAssetsWebPluginManifest,
|
|
898
|
+
]);
|
|
899
|
+
|
|
900
|
+
const doc = TextDocument.create("file:///x.json", "json", 1, "{}");
|
|
901
|
+
await service.validateTextDocument(doc);
|
|
902
|
+
|
|
903
|
+
const json = JSON.parse(fs.readFileSync(outPath, "utf-8"));
|
|
904
|
+
expect(json.content["/x.json"].stats.metric).toBe(42);
|
|
905
|
+
// No root pulled from preexisting since it wasn't an object
|
|
906
|
+
fs.unlinkSync(outPath);
|
|
907
|
+
fs.rmdirSync(dir);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
describe("Re-running validation", () => {
|
|
911
|
+
const MULTI_TEST_DIR = path.resolve("target_multi");
|
|
912
|
+
const MULTI_TEST_FILE = "multi_metrics.json";
|
|
913
|
+
const MULTI_TEST_PATH = path.join(MULTI_TEST_DIR, MULTI_TEST_FILE);
|
|
914
|
+
|
|
915
|
+
beforeEach(() => {
|
|
916
|
+
// Create test directory for multi-validation tests
|
|
917
|
+
if (!fs.existsSync(MULTI_TEST_DIR)) {
|
|
918
|
+
fs.mkdirSync(MULTI_TEST_DIR, { recursive: true });
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
afterEach(() => {
|
|
923
|
+
// Clean up test files
|
|
924
|
+
try {
|
|
925
|
+
if (fs.existsSync(MULTI_TEST_PATH)) {
|
|
926
|
+
fs.unlinkSync(MULTI_TEST_PATH);
|
|
927
|
+
}
|
|
928
|
+
if (fs.existsSync(MULTI_TEST_DIR)) {
|
|
929
|
+
fs.rmdirSync(MULTI_TEST_DIR);
|
|
930
|
+
}
|
|
931
|
+
} catch (e) {
|
|
932
|
+
console.debug("Test cleanup failed, but tests may still be valid:", e);
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
test("Auto-append: creates new file if none exists", async () => {
|
|
937
|
+
// Ensure no existing file
|
|
938
|
+
if (fs.existsSync(MULTI_TEST_PATH)) {
|
|
939
|
+
fs.unlinkSync(MULTI_TEST_PATH);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// Create service
|
|
943
|
+
const service = new PlayerLanguageService();
|
|
944
|
+
service.addLSPPlugin(
|
|
945
|
+
new MetricsOutput({
|
|
946
|
+
outputDir: MULTI_TEST_DIR,
|
|
947
|
+
fileName: MULTI_TEST_FILE.replace(".json", ""),
|
|
948
|
+
stats: {
|
|
949
|
+
complexity: () => 20,
|
|
950
|
+
},
|
|
951
|
+
}),
|
|
952
|
+
);
|
|
953
|
+
|
|
954
|
+
await service.setAssetTypesFromModule([
|
|
955
|
+
Types,
|
|
956
|
+
ReferenceAssetsWebPluginManifest,
|
|
957
|
+
]);
|
|
958
|
+
|
|
959
|
+
// Validate a document
|
|
960
|
+
const doc = TextDocument.create("file:///new.json", "json", 1, "{}");
|
|
961
|
+
await service.validateTextDocument(doc);
|
|
962
|
+
|
|
963
|
+
// Check that new file was created
|
|
964
|
+
const result = JSON.parse(fs.readFileSync(MULTI_TEST_PATH, "utf-8"));
|
|
965
|
+
expect(result.content).toHaveProperty("/new.json");
|
|
966
|
+
expect(result.content["/new.json"].stats.complexity).toBe(20);
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
test("Auto-append: if metrics.json exists, new file entries are appended and existing ones are preserved", async () => {
|
|
970
|
+
// Seed an existing metrics file (what already exists on disk)
|
|
971
|
+
const existingMetrics = {
|
|
972
|
+
content: {
|
|
973
|
+
"/stage1.json": {
|
|
974
|
+
stats: { complexity: 10 },
|
|
975
|
+
features: { stable: true },
|
|
976
|
+
},
|
|
977
|
+
},
|
|
978
|
+
timestamp: "stage1-timestamp",
|
|
979
|
+
};
|
|
980
|
+
fs.writeFileSync(MULTI_TEST_PATH, JSON.stringify(existingMetrics));
|
|
981
|
+
|
|
982
|
+
// Create service that will add a new file entry
|
|
983
|
+
const service = new PlayerLanguageService();
|
|
984
|
+
service.addLSPPlugin(
|
|
985
|
+
new MetricsOutput({
|
|
986
|
+
outputDir: MULTI_TEST_DIR,
|
|
987
|
+
fileName: MULTI_TEST_FILE.replace(".json", ""),
|
|
988
|
+
stats: {
|
|
989
|
+
complexity: () => 20,
|
|
990
|
+
},
|
|
991
|
+
features: {
|
|
992
|
+
stable: () => false,
|
|
993
|
+
},
|
|
994
|
+
}),
|
|
995
|
+
);
|
|
996
|
+
|
|
997
|
+
await service.setAssetTypesFromModule([
|
|
998
|
+
Types,
|
|
999
|
+
ReferenceAssetsWebPluginManifest,
|
|
1000
|
+
]);
|
|
1001
|
+
|
|
1002
|
+
// Validate a different document to trigger append
|
|
1003
|
+
const doc = TextDocument.create("file:///stage2.json", "json", 1, "{}");
|
|
1004
|
+
await service.validateTextDocument(doc);
|
|
1005
|
+
|
|
1006
|
+
// The resulting content should have the union of file entries with their respective data
|
|
1007
|
+
const result = JSON.parse(fs.readFileSync(MULTI_TEST_PATH, "utf-8"));
|
|
1008
|
+
expect(result.content).toEqual({
|
|
1009
|
+
"/stage1.json": {
|
|
1010
|
+
stats: { complexity: 10 },
|
|
1011
|
+
features: { stable: true },
|
|
1012
|
+
},
|
|
1013
|
+
"/stage2.json": {
|
|
1014
|
+
stats: { complexity: 20 },
|
|
1015
|
+
features: { stable: false },
|
|
1016
|
+
},
|
|
1017
|
+
});
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
test("Auto-append: root properties are preserved and new ones appended when file exists", async () => {
|
|
1021
|
+
// Existing file with root metadata
|
|
1022
|
+
const existingMetrics = {
|
|
1023
|
+
content: {
|
|
1024
|
+
"/stage1.json": { stats: { metric: 1 } },
|
|
1025
|
+
},
|
|
1026
|
+
root: {
|
|
1027
|
+
metadata: {
|
|
1028
|
+
project: { name: "metrics-service" },
|
|
1029
|
+
},
|
|
1030
|
+
},
|
|
1031
|
+
};
|
|
1032
|
+
fs.writeFileSync(MULTI_TEST_PATH, JSON.stringify(existingMetrics));
|
|
1033
|
+
|
|
1034
|
+
const service = new PlayerLanguageService();
|
|
1035
|
+
service.addLSPPlugin(
|
|
1036
|
+
new MetricsOutput({
|
|
1037
|
+
outputDir: MULTI_TEST_DIR,
|
|
1038
|
+
fileName: MULTI_TEST_FILE.replace(".json", ""),
|
|
1039
|
+
rootProperties: {
|
|
1040
|
+
root: {
|
|
1041
|
+
metadata: { project: { version: "1.0.0" } },
|
|
1042
|
+
build: { ci: true },
|
|
1043
|
+
},
|
|
1044
|
+
},
|
|
1045
|
+
stats: { metric: () => 42 },
|
|
1046
|
+
}),
|
|
1047
|
+
);
|
|
1048
|
+
|
|
1049
|
+
await service.setAssetTypesFromModule([
|
|
1050
|
+
Types,
|
|
1051
|
+
ReferenceAssetsWebPluginManifest,
|
|
1052
|
+
]);
|
|
1053
|
+
|
|
1054
|
+
// Validate a document to trigger write
|
|
1055
|
+
const doc = TextDocument.create("file:///stage2.json", "json", 1, "{}");
|
|
1056
|
+
await service.validateTextDocument(doc);
|
|
1057
|
+
|
|
1058
|
+
const result = JSON.parse(fs.readFileSync(MULTI_TEST_PATH, "utf-8"));
|
|
1059
|
+
|
|
1060
|
+
// Root properties merge (preserve existing, append new)
|
|
1061
|
+
expect(result.root.metadata.project).toEqual(
|
|
1062
|
+
expect.objectContaining({ name: "metrics-service", version: "1.0.0" }),
|
|
1063
|
+
);
|
|
1064
|
+
expect(result.root.build).toEqual(expect.objectContaining({ ci: true }));
|
|
1065
|
+
|
|
1066
|
+
// Content includes both files with their own stats
|
|
1067
|
+
expect(result.content).toHaveProperty("/stage1.json");
|
|
1068
|
+
expect(result.content["/stage1.json"].stats.metric).toBe(1);
|
|
1069
|
+
expect(result.content).toHaveProperty("/stage2.json");
|
|
1070
|
+
expect(result.content["/stage2.json"].stats.metric).toBe(42);
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
test("Auto-append:loads existing metrics and merges", async () => {
|
|
1074
|
+
const service = new PlayerLanguageService();
|
|
1075
|
+
const dir = path.resolve("target_existing_ok");
|
|
1076
|
+
const name = "preexisting_ok";
|
|
1077
|
+
const outPath = path.join(dir, `${name}.json`);
|
|
1078
|
+
|
|
1079
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1080
|
+
// Seed a valid JSON object so parsed && typeof parsed === "object" is true
|
|
1081
|
+
fs.writeFileSync(
|
|
1082
|
+
outPath,
|
|
1083
|
+
JSON.stringify(
|
|
1084
|
+
{
|
|
1085
|
+
root: true,
|
|
1086
|
+
content: {
|
|
1087
|
+
"/seed.json": { stats: { seeded: 1 } },
|
|
1088
|
+
},
|
|
1089
|
+
},
|
|
1090
|
+
null,
|
|
1091
|
+
2,
|
|
1092
|
+
),
|
|
1093
|
+
"utf-8",
|
|
1094
|
+
);
|
|
1095
|
+
|
|
1096
|
+
service.addLSPPlugin(
|
|
1097
|
+
new MetricsOutput({
|
|
1098
|
+
outputDir: dir,
|
|
1099
|
+
fileName: name, // matches the seeded file
|
|
1100
|
+
stats: { added: 2 },
|
|
1101
|
+
}),
|
|
1102
|
+
);
|
|
1103
|
+
|
|
1104
|
+
await service.setAssetTypesFromModule([
|
|
1105
|
+
Types,
|
|
1106
|
+
ReferenceAssetsWebPluginManifest,
|
|
1107
|
+
]);
|
|
1108
|
+
|
|
1109
|
+
const doc = TextDocument.create("file:///seed.json", "json", 1, "{}");
|
|
1110
|
+
await service.validateTextDocument(doc);
|
|
1111
|
+
|
|
1112
|
+
const json = JSON.parse(fs.readFileSync(outPath, "utf-8"));
|
|
1113
|
+
expect(json.root).toBe(true); // came from existing file
|
|
1114
|
+
expect(json.content["/seed.json"].stats).toMatchObject({
|
|
1115
|
+
seeded: 1,
|
|
1116
|
+
added: 2,
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
fs.unlinkSync(outPath);
|
|
1120
|
+
fs.rmdirSync(dir);
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
test("Deep merge: nested objects are merged (preserve existing, extend new) across runs", async () => {
|
|
1124
|
+
// Seed an existing metrics file with nested content and realistic root metadata
|
|
1125
|
+
const existingMetrics = {
|
|
1126
|
+
content: {
|
|
1127
|
+
"/nested.json": {
|
|
1128
|
+
stats: {
|
|
1129
|
+
metrics: { warnings: 1, byType: { deprecations: 1 } },
|
|
1130
|
+
nested: { x: 10 },
|
|
1131
|
+
},
|
|
1132
|
+
},
|
|
1133
|
+
},
|
|
1134
|
+
root: {
|
|
1135
|
+
metadata: {
|
|
1136
|
+
project: {
|
|
1137
|
+
name: "metrics-service",
|
|
1138
|
+
info: { repo: "tools" },
|
|
1139
|
+
},
|
|
1140
|
+
},
|
|
1141
|
+
},
|
|
1142
|
+
};
|
|
1143
|
+
fs.writeFileSync(MULTI_TEST_PATH, JSON.stringify(existingMetrics));
|
|
1144
|
+
|
|
1145
|
+
// Create service which writes overlapping nested values
|
|
1146
|
+
const service = new PlayerLanguageService();
|
|
1147
|
+
service.addLSPPlugin(
|
|
1148
|
+
new MetricsOutput({
|
|
1149
|
+
outputDir: MULTI_TEST_DIR,
|
|
1150
|
+
fileName: MULTI_TEST_FILE.replace(".json", ""),
|
|
1151
|
+
rootProperties: {
|
|
1152
|
+
root: {
|
|
1153
|
+
metadata: {
|
|
1154
|
+
project: { info: { branch: "main" } },
|
|
1155
|
+
build: { ci: true },
|
|
1156
|
+
},
|
|
1157
|
+
},
|
|
1158
|
+
},
|
|
1159
|
+
stats: {
|
|
1160
|
+
metrics: () => ({ warnings: 2, byType: { errors: 1 } }), // overwrite primitive and extend nested
|
|
1161
|
+
nested: () => ({ y: 20 }), // extend nested
|
|
1162
|
+
},
|
|
1163
|
+
}),
|
|
1164
|
+
);
|
|
1165
|
+
|
|
1166
|
+
await service.setAssetTypesFromModule([
|
|
1167
|
+
Types,
|
|
1168
|
+
ReferenceAssetsWebPluginManifest,
|
|
1169
|
+
]);
|
|
1170
|
+
|
|
1171
|
+
// Validate a document to trigger write for the same file path
|
|
1172
|
+
const doc = TextDocument.create("file:///nested.json", "json", 1, "{}");
|
|
1173
|
+
await service.validateTextDocument(doc);
|
|
1174
|
+
|
|
1175
|
+
// Verify deep merge result
|
|
1176
|
+
const result = JSON.parse(fs.readFileSync(MULTI_TEST_PATH, "utf-8"));
|
|
1177
|
+
expect(result.content["/nested.json"].stats).toEqual(
|
|
1178
|
+
expect.objectContaining({
|
|
1179
|
+
metrics: expect.objectContaining({
|
|
1180
|
+
warnings: 2,
|
|
1181
|
+
byType: expect.objectContaining({ deprecations: 1, errors: 1 }),
|
|
1182
|
+
}),
|
|
1183
|
+
nested: expect.objectContaining({ x: 10, y: 20 }),
|
|
1184
|
+
}),
|
|
1185
|
+
);
|
|
1186
|
+
expect(result.root.metadata.project.name).toBe("metrics-service");
|
|
1187
|
+
expect(result.root.metadata.project.info).toEqual(
|
|
1188
|
+
expect.objectContaining({ repo: "tools", branch: "main" }),
|
|
1189
|
+
);
|
|
1190
|
+
expect(result.root.metadata.build).toEqual(
|
|
1191
|
+
expect.objectContaining({ ci: true }),
|
|
1192
|
+
);
|
|
1193
|
+
});
|
|
1194
|
+
|
|
1195
|
+
test("New root property on second run keeps content as last key", async () => {
|
|
1196
|
+
// First run writes initial root and first file entry
|
|
1197
|
+
const service1 = new PlayerLanguageService();
|
|
1198
|
+
service1.addLSPPlugin(
|
|
1199
|
+
new MetricsOutput({
|
|
1200
|
+
outputDir: MULTI_TEST_DIR,
|
|
1201
|
+
fileName: MULTI_TEST_FILE.replace(".json", ""),
|
|
1202
|
+
rootProperties: { initialRoot: true },
|
|
1203
|
+
stats: { metric: () => 1 },
|
|
1204
|
+
}),
|
|
1205
|
+
);
|
|
1206
|
+
|
|
1207
|
+
await service1.setAssetTypesFromModule([
|
|
1208
|
+
Types,
|
|
1209
|
+
ReferenceAssetsWebPluginManifest,
|
|
1210
|
+
]);
|
|
1211
|
+
|
|
1212
|
+
const doc1 = TextDocument.create("file:///stage1.json", "json", 1, "{}");
|
|
1213
|
+
await service1.validateTextDocument(doc1);
|
|
1214
|
+
|
|
1215
|
+
// Second run adds a new root property and a different file entry
|
|
1216
|
+
const service2 = new PlayerLanguageService();
|
|
1217
|
+
service2.addLSPPlugin(
|
|
1218
|
+
new MetricsOutput({
|
|
1219
|
+
outputDir: MULTI_TEST_DIR,
|
|
1220
|
+
fileName: MULTI_TEST_FILE.replace(".json", ""),
|
|
1221
|
+
rootProperties: { newRoot: true },
|
|
1222
|
+
stats: { metric: () => 2 },
|
|
1223
|
+
}),
|
|
1224
|
+
);
|
|
1225
|
+
|
|
1226
|
+
await service2.setAssetTypesFromModule([
|
|
1227
|
+
Types,
|
|
1228
|
+
ReferenceAssetsWebPluginManifest,
|
|
1229
|
+
]);
|
|
1230
|
+
|
|
1231
|
+
const doc2 = TextDocument.create("file:///stage2.json", "json", 1, "{}");
|
|
1232
|
+
await service2.validateTextDocument(doc2);
|
|
1233
|
+
|
|
1234
|
+
// Verify only that the last top-level key is "content"
|
|
1235
|
+
const parsed = JSON.parse(fs.readFileSync(MULTI_TEST_PATH, "utf-8"));
|
|
1236
|
+
const keys = Object.keys(parsed);
|
|
1237
|
+
expect(keys[keys.length - 1]).toBe("content");
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
test("merges with existing metrics file (root and content)", async () => {
|
|
1241
|
+
const service = new PlayerLanguageService();
|
|
1242
|
+
|
|
1243
|
+
const fileName = "preexisting_merge";
|
|
1244
|
+
const outputPath = path.join(TEST_DIR, `${fileName}.json`);
|
|
1245
|
+
|
|
1246
|
+
// Seed an existing metrics file with root and nested content
|
|
1247
|
+
fs.writeFileSync(
|
|
1248
|
+
outputPath,
|
|
1249
|
+
JSON.stringify(
|
|
1250
|
+
{
|
|
1251
|
+
rootKey: "keep-me",
|
|
1252
|
+
content: {
|
|
1253
|
+
"existing.json": {
|
|
1254
|
+
stats: { existingStat: 1 },
|
|
1255
|
+
},
|
|
1256
|
+
},
|
|
1257
|
+
},
|
|
1258
|
+
null,
|
|
1259
|
+
2,
|
|
1260
|
+
),
|
|
1261
|
+
"utf-8",
|
|
1262
|
+
);
|
|
1263
|
+
|
|
1264
|
+
service.addLSPPlugin(
|
|
1265
|
+
new MetricsOutput({
|
|
1266
|
+
outputDir: TEST_DIR,
|
|
1267
|
+
fileName,
|
|
1268
|
+
rootProperties: { anotherRoot: true },
|
|
1269
|
+
stats: { newStat: 2 },
|
|
1270
|
+
}),
|
|
1271
|
+
);
|
|
1272
|
+
|
|
1273
|
+
await service.setAssetTypesFromModule([
|
|
1274
|
+
Types,
|
|
1275
|
+
ReferenceAssetsWebPluginManifest,
|
|
1276
|
+
]);
|
|
1277
|
+
|
|
1278
|
+
const doc = TextDocument.create(
|
|
1279
|
+
"existing.json",
|
|
1280
|
+
"json",
|
|
1281
|
+
1,
|
|
1282
|
+
JSON.stringify({ id: "ok" }),
|
|
1283
|
+
);
|
|
1284
|
+
await service.validateTextDocument(doc);
|
|
1285
|
+
|
|
1286
|
+
expect(fs.existsSync(outputPath)).toBe(true);
|
|
1287
|
+
const parsed = JSON.parse(fs.readFileSync(outputPath, "utf-8"));
|
|
1288
|
+
|
|
1289
|
+
// Root deep-merged
|
|
1290
|
+
expect(parsed.rootKey).toBe("keep-me");
|
|
1291
|
+
expect(parsed.anotherRoot).toBe(true);
|
|
1292
|
+
|
|
1293
|
+
// Content deep-merged for same file
|
|
1294
|
+
expect(parsed.content["existing.json"].stats).toMatchObject({
|
|
1295
|
+
existingStat: 1,
|
|
1296
|
+
newStat: 2,
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
fs.unlinkSync(outputPath);
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
test("Gracefully handles malformed existing metrics file and logs warning", async () => {
|
|
1303
|
+
// Seed an invalid JSON metrics file to trigger the parse error path
|
|
1304
|
+
fs.writeFileSync(MULTI_TEST_PATH, "{ invalid-json ");
|
|
1305
|
+
|
|
1306
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
1307
|
+
|
|
1308
|
+
const service = new PlayerLanguageService();
|
|
1309
|
+
service.addLSPPlugin(
|
|
1310
|
+
new MetricsOutput({
|
|
1311
|
+
outputDir: MULTI_TEST_DIR,
|
|
1312
|
+
fileName: MULTI_TEST_FILE.replace(".json", ""),
|
|
1313
|
+
stats: { metric: () => 99 },
|
|
1314
|
+
}),
|
|
1315
|
+
);
|
|
1316
|
+
|
|
1317
|
+
await service.setAssetTypesFromModule([
|
|
1318
|
+
Types,
|
|
1319
|
+
ReferenceAssetsWebPluginManifest,
|
|
1320
|
+
]);
|
|
1321
|
+
|
|
1322
|
+
const doc = TextDocument.create(
|
|
1323
|
+
"file:///malformed.json",
|
|
1324
|
+
"json",
|
|
1325
|
+
1,
|
|
1326
|
+
"{}",
|
|
1327
|
+
);
|
|
1328
|
+
await service.validateTextDocument(doc);
|
|
1329
|
+
|
|
1330
|
+
// Should log a parse warning and still produce a valid metrics file
|
|
1331
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
1332
|
+
const args = warnSpy.mock.calls[0][0] as string;
|
|
1333
|
+
expect(args).toContain("Could not parse existing metrics file");
|
|
1334
|
+
|
|
1335
|
+
const parsed = JSON.parse(fs.readFileSync(MULTI_TEST_PATH, "utf-8"));
|
|
1336
|
+
// normalizePath removes file:// and backslashes; our test doc uses file:///malformed.json
|
|
1337
|
+
expect(parsed.content).toHaveProperty("/malformed.json");
|
|
1338
|
+
expect(parsed.content["/malformed.json"].stats.metric).toBe(99);
|
|
1339
|
+
|
|
1340
|
+
warnSpy.mockRestore();
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
test("loads and merges an existing metrics file", async () => {
|
|
1344
|
+
const service = new PlayerLanguageService();
|
|
1345
|
+
const fileName = "pre_merge";
|
|
1346
|
+
const outPath = path.join(TEST_DIR, `${fileName}.json`);
|
|
1347
|
+
|
|
1348
|
+
// Seed a valid existing metrics file
|
|
1349
|
+
fs.writeFileSync(
|
|
1350
|
+
outPath,
|
|
1351
|
+
JSON.stringify(
|
|
1352
|
+
{
|
|
1353
|
+
rootKey: "keep",
|
|
1354
|
+
content: {
|
|
1355
|
+
"/pre.json": { stats: { preStat: 1 } },
|
|
1356
|
+
},
|
|
1357
|
+
},
|
|
1358
|
+
null,
|
|
1359
|
+
2,
|
|
1360
|
+
),
|
|
1361
|
+
"utf-8",
|
|
1362
|
+
);
|
|
1363
|
+
|
|
1364
|
+
service.addLSPPlugin(
|
|
1365
|
+
new MetricsOutput({
|
|
1366
|
+
outputDir: TEST_DIR,
|
|
1367
|
+
fileName,
|
|
1368
|
+
stats: { newStat: 2 },
|
|
1369
|
+
}),
|
|
1370
|
+
);
|
|
1371
|
+
|
|
1372
|
+
await service.setAssetTypesFromModule([
|
|
1373
|
+
Types,
|
|
1374
|
+
ReferenceAssetsWebPluginManifest,
|
|
1375
|
+
]);
|
|
1376
|
+
const doc = TextDocument.create("file:///pre.json", "json", 1, "{}");
|
|
1377
|
+
await service.validateTextDocument(doc);
|
|
1378
|
+
|
|
1379
|
+
const json = JSON.parse(fs.readFileSync(outPath, "utf-8"));
|
|
1380
|
+
expect(json.rootKey).toBe("keep"); // came from existing file
|
|
1381
|
+
expect(json.content["/pre.json"].stats).toMatchObject({
|
|
1382
|
+
preStat: 1,
|
|
1383
|
+
newStat: 2,
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1386
|
+
fs.unlinkSync(outPath);
|
|
1387
|
+
});
|
|
1388
|
+
});
|
|
781
1389
|
});
|