@valbuild/language-server 0.116.0 → 0.117.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.
- package/CHANGELOG.md +61 -0
- package/dist/declarations/src/diagnostics.d.ts +11 -1
- package/dist/declarations/src/mediaMetadataChecks.d.ts +105 -0
- package/dist/valbuild-language-server.cjs.dev.js +364 -1
- package/dist/valbuild-language-server.cjs.prod.js +364 -1
- package/dist/valbuild-language-server.esm.js +365 -2
- package/package.json +5 -4
|
@@ -857,6 +857,314 @@ function traverseObjectLiteral(node, sourceFile) {
|
|
|
857
857
|
return map;
|
|
858
858
|
}
|
|
859
859
|
|
|
860
|
+
/**
|
|
861
|
+
* Adjudicating the media metadata placeholders core emits unconditionally.
|
|
862
|
+
*
|
|
863
|
+
* `ImageSchema.validate` cannot read bytes, so it never answers "does the
|
|
864
|
+
* stored width/height/mimeType match the file". It defers instead: any
|
|
865
|
+
* `s.image()` carrying metadata gets an `image:check-metadata` error whether
|
|
866
|
+
* or not anything is wrong (`packages/core/src/schema/image.ts`), and
|
|
867
|
+
* `s.file()` does the same with `file:check-metadata`.
|
|
868
|
+
*
|
|
869
|
+
* `val validate` resolves those by running the fix handler and then
|
|
870
|
+
* `createFixPatch` in report mode, which compares each field against the file
|
|
871
|
+
* and returns one error per field that disagrees. The Studio cannot do that at
|
|
872
|
+
* all -- a browser has no filesystem -- so it drops them wholesale
|
|
873
|
+
* (`partitionValidationErrors` in `@valbuild/shared`).
|
|
874
|
+
*
|
|
875
|
+
* An editor is in the CLI's position, not the browser's, and publishing the
|
|
876
|
+
* placeholders raw put a permanent warning on every image in the project. So
|
|
877
|
+
* they are adjudicated here, by the same comparison `val validate` makes, and
|
|
878
|
+
* only a real disagreement is shown.
|
|
879
|
+
*/
|
|
880
|
+
/**
|
|
881
|
+
* The fixes that carry an unconditional metadata placeholder.
|
|
882
|
+
*
|
|
883
|
+
* `*:add-metadata` is deliberately absent: it is emitted only when every
|
|
884
|
+
* metadata field is missing, which needs no adjudication -- there is nothing
|
|
885
|
+
* stored to compare, and the error stands on its own.
|
|
886
|
+
*/
|
|
887
|
+
const METADATA_CHECK_FIXES = ["image:check-metadata", "file:check-metadata"];
|
|
888
|
+
|
|
889
|
+
/** One field of a media value that disagrees with the file behind it. */
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* The verdict on one placeholder. Empty means the metadata agrees with the
|
|
893
|
+
* file and nothing should be published.
|
|
894
|
+
*/
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Whether this error is the unconditional deferral rather than a real finding.
|
|
898
|
+
*
|
|
899
|
+
* This is the whole difficulty of the change. `image:check-metadata` is
|
|
900
|
+
* attached to FIVE different image errors, and only the last is a placeholder:
|
|
901
|
+
*
|
|
902
|
+
* 1. `Invalid mime type format` -- `accept` set, mimeType has no `/`
|
|
903
|
+
* 2. `Mime type mismatch` -- `accept` set and not satisfied
|
|
904
|
+
* 3. `Could not determine mime type from file extension`
|
|
905
|
+
* 4. `Mime type and file extension not matching`
|
|
906
|
+
* 5. the fall-through deferral
|
|
907
|
+
*
|
|
908
|
+
* The first four are real: they are about the mime type disagreeing with the
|
|
909
|
+
* schema or with the filename, neither of which reading the file can settle.
|
|
910
|
+
* Adjudicating one of them would find the stored metadata matches the bytes and
|
|
911
|
+
* drop a genuine error -- so they have to be told apart before any file is read.
|
|
912
|
+
*
|
|
913
|
+
* There is no flag on `ValidationError` saying which is which, so the four
|
|
914
|
+
* conditions are re-derived here from the same inputs core used. They are
|
|
915
|
+
* transcribed from `ImageSchema.executeValidate`, in its order, and
|
|
916
|
+
* `mediaMetadataChecks.test.ts` drives real core through all five cases to
|
|
917
|
+
* check that this agrees with it. If core grows a sixth condition, that test is
|
|
918
|
+
* what catches it.
|
|
919
|
+
*
|
|
920
|
+
* `FileSchema` needs none of this: its four equivalent errors carry no `fixes`
|
|
921
|
+
* at all, so `file:check-metadata` is unambiguous. The test pins that too.
|
|
922
|
+
*/
|
|
923
|
+
function isDeferredMediaMetadataCheck({
|
|
924
|
+
error,
|
|
925
|
+
schema
|
|
926
|
+
}) {
|
|
927
|
+
const fixes = error.fixes ?? [];
|
|
928
|
+
if (!fixes.some(fix => METADATA_CHECK_FIXES.includes(fix))) {
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
if (fixes.includes("file:check-metadata")) {
|
|
932
|
+
return true;
|
|
933
|
+
}
|
|
934
|
+
const value = mediaValueOf(error.value);
|
|
935
|
+
if (typeof value?.path !== "string") {
|
|
936
|
+
// Nothing to re-derive the conditions from. Keep the error: claiming an
|
|
937
|
+
// image is fine on no evidence is the worse failure.
|
|
938
|
+
return false;
|
|
939
|
+
}
|
|
940
|
+
const accept = acceptOf(schema);
|
|
941
|
+
// Core reads `src.mimeType ?? ""`, and every condition below is guarded on
|
|
942
|
+
// it being non-empty, so an absent mimeType passes all four.
|
|
943
|
+
const mimeType = typeof value.mimeType === "string" ? value.mimeType : "";
|
|
944
|
+
if (accept && mimeType && !mimeType.includes("/")) {
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
if (accept && mimeType && mimeType.includes("/") && !core.Internal.mimeTypeMatchesAccept(mimeType, accept)) {
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
const fileMimeType = core.Internal.filenameToMimeType(value.path);
|
|
951
|
+
if (!fileMimeType) {
|
|
952
|
+
return false;
|
|
953
|
+
}
|
|
954
|
+
if (mimeType && fileMimeType !== mimeType) {
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
return true;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* {@link isDeferredMediaMetadataCheck} for an error in a module, resolving the
|
|
962
|
+
* schema it needs.
|
|
963
|
+
*
|
|
964
|
+
* Both the adjudicator and `createValDiagnostics` have to make the same call,
|
|
965
|
+
* on the same inputs -- one to decide what to adjudicate, the other to decide
|
|
966
|
+
* what to publish -- so it lives here rather than being written out twice.
|
|
967
|
+
*/
|
|
968
|
+
function isDeferredMediaMetadataCheckAt({
|
|
969
|
+
sourcePath,
|
|
970
|
+
error,
|
|
971
|
+
content
|
|
972
|
+
}) {
|
|
973
|
+
return isDeferredMediaMetadataCheck({
|
|
974
|
+
error,
|
|
975
|
+
schema: resolveSchemaAt$1(sourcePath, content)
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Adjudicate every deferred metadata placeholder in `validation`.
|
|
981
|
+
*
|
|
982
|
+
* Async, and therefore separate from `createValDiagnostics`, which stays
|
|
983
|
+
* synchronous so it can be tested without a project -- the same split
|
|
984
|
+
* `resolveGalleryChecks` uses. The caller runs this first and passes the
|
|
985
|
+
* result in.
|
|
986
|
+
*/
|
|
987
|
+
async function resolveMediaMetadataChecks({
|
|
988
|
+
validation,
|
|
989
|
+
content,
|
|
990
|
+
valRoot,
|
|
991
|
+
remoteHost = process.env.VAL_REMOTE_HOST || core.DEFAULT_VAL_REMOTE_HOST
|
|
992
|
+
}) {
|
|
993
|
+
const verdicts = new Map();
|
|
994
|
+
for (const [sourcePath, errors] of Object.entries(validation)) {
|
|
995
|
+
for (const error of errors) {
|
|
996
|
+
if (!isDeferredMediaMetadataCheckAt({
|
|
997
|
+
sourcePath,
|
|
998
|
+
error,
|
|
999
|
+
content
|
|
1000
|
+
})) {
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
const key = mediaMetadataCheckKey(sourcePath, error);
|
|
1004
|
+
try {
|
|
1005
|
+
verdicts.set(key, await adjudicate({
|
|
1006
|
+
sourcePath,
|
|
1007
|
+
error,
|
|
1008
|
+
content,
|
|
1009
|
+
valRoot,
|
|
1010
|
+
remoteHost
|
|
1011
|
+
}));
|
|
1012
|
+
} catch {
|
|
1013
|
+
// This runs inside `validate`, which publishes NOTHING if it throws --
|
|
1014
|
+
// one bad image would silently clear every Val diagnostic in the file.
|
|
1015
|
+
// Keep the placeholder rather than claiming the metadata is fine.
|
|
1016
|
+
verdicts.set(key, keep(error));
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
return verdicts;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Key for one placeholder. A value can in principle carry more than one, so
|
|
1025
|
+
* the fix names are part of the key -- as they are for the gallery checks.
|
|
1026
|
+
*/
|
|
1027
|
+
function mediaMetadataCheckKey(sourcePath, error) {
|
|
1028
|
+
return `${sourcePath}|${(error.fixes ?? []).join(",")}`;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/** Keeping the placeholder: what to publish when we learned nothing. */
|
|
1032
|
+
function keep(error) {
|
|
1033
|
+
return [{
|
|
1034
|
+
message: error.message,
|
|
1035
|
+
...(error.fixes ? {
|
|
1036
|
+
fixes: error.fixes
|
|
1037
|
+
} : {}),
|
|
1038
|
+
...(error.value !== undefined ? {
|
|
1039
|
+
value: error.value
|
|
1040
|
+
} : {})
|
|
1041
|
+
}];
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* What `createFixPatch` says about one placeholder, in report mode.
|
|
1046
|
+
*
|
|
1047
|
+
* `val validate` runs the fix handler first and then `createFixPatch`, but for
|
|
1048
|
+
* these four fixes the handler is `handleFileMetadata`, whose whole job is the
|
|
1049
|
+
* precondition "the ref resolves and the file is on disk". That precondition is
|
|
1050
|
+
* checked directly here instead, for two reasons:
|
|
1051
|
+
*
|
|
1052
|
+
* - `createValDiagnostics` already reports a missing file as
|
|
1053
|
+
* `val/file-not-found`, which is a better diagnostic than a metadata
|
|
1054
|
+
* mismatch and is produced before this verdict is consulted.
|
|
1055
|
+
* - `handleFileMetadata` resolves the source path through the module, and that
|
|
1056
|
+
* throws outright for a `.jsonValues()` entry ("Cannot resolve path into a
|
|
1057
|
+
* jsonValues entry until its content is loaded") -- so routing through it
|
|
1058
|
+
* would leave every entry-backed image stuck on the placeholder.
|
|
1059
|
+
*
|
|
1060
|
+
* The comparison itself is still `createFixPatch`, unchanged, which is the
|
|
1061
|
+
* parity that matters: the editor's wording is the CLI's wording because it is
|
|
1062
|
+
* the CLI's code -- including for bytes it cannot measure, where it reports
|
|
1063
|
+
* what `val validate` reports rather than a second opinion of our own.
|
|
1064
|
+
*
|
|
1065
|
+
* `createFixPatch` reads and decodes the image itself, so this is one file read
|
|
1066
|
+
* per media field per validation pass, and nothing here should add another.
|
|
1067
|
+
*/
|
|
1068
|
+
async function adjudicate({
|
|
1069
|
+
sourcePath,
|
|
1070
|
+
error,
|
|
1071
|
+
content,
|
|
1072
|
+
valRoot,
|
|
1073
|
+
remoteHost
|
|
1074
|
+
}) {
|
|
1075
|
+
const ref = mediaValueOf(error.value)?.path;
|
|
1076
|
+
if (typeof ref !== "string") {
|
|
1077
|
+
return keep(error);
|
|
1078
|
+
}
|
|
1079
|
+
// A remote ref is a URL and is not expected on disk. Core does not emit these
|
|
1080
|
+
// fixes for one, but a stale ref should not be reported as a local mismatch.
|
|
1081
|
+
if (core.Internal.remote.splitRemoteRef(ref).status === "success") {
|
|
1082
|
+
return keep(error);
|
|
1083
|
+
}
|
|
1084
|
+
if (!fs__default["default"].existsSync(path__default["default"].join(valRoot, ref))) {
|
|
1085
|
+
// Reported as `val/file-not-found` instead; keeping the placeholder here
|
|
1086
|
+
// means this verdict never has the last word on a file that is not there.
|
|
1087
|
+
return keep(error);
|
|
1088
|
+
}
|
|
1089
|
+
let fixed;
|
|
1090
|
+
try {
|
|
1091
|
+
fixed = await server.createFixPatch({
|
|
1092
|
+
projectRoot: valRoot,
|
|
1093
|
+
remoteHost
|
|
1094
|
+
},
|
|
1095
|
+
// `false`: this is a question, not a fix. Asking for the patch would have
|
|
1096
|
+
// createFixPatch read and rewrite files behind the editor's back.
|
|
1097
|
+
false, sourcePath, error, {}, content.source, content.schema);
|
|
1098
|
+
} catch {
|
|
1099
|
+
return keep(error);
|
|
1100
|
+
}
|
|
1101
|
+
return (fixed?.remainingErrors ?? []).map(remaining => ({
|
|
1102
|
+
message: remaining.message,
|
|
1103
|
+
// `createFixPatch` clears `fixes` on the per-field errors it reports, but
|
|
1104
|
+
// the fix is still available and still the remedy -- it is what the
|
|
1105
|
+
// placeholder was asking for. Carrying the placeholder's own fixes is what
|
|
1106
|
+
// keeps the "update image metadata" quick fix offered, and the diagnostic
|
|
1107
|
+
// a Warning rather than an Error.
|
|
1108
|
+
...(error.fixes ? {
|
|
1109
|
+
fixes: error.fixes
|
|
1110
|
+
} : {}),
|
|
1111
|
+
...(error.value !== undefined ? {
|
|
1112
|
+
value: error.value
|
|
1113
|
+
} : {})
|
|
1114
|
+
}));
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* The `accept` a media schema declares, if any.
|
|
1119
|
+
*
|
|
1120
|
+
* Read from the serialized schema (`SerializedImageSchema.options`) rather
|
|
1121
|
+
* than from a schema instance: this runs against what `Service.get` returns.
|
|
1122
|
+
*/
|
|
1123
|
+
function acceptOf(schema) {
|
|
1124
|
+
if (typeof schema !== "object" || schema === null || !("options" in schema)) {
|
|
1125
|
+
return undefined;
|
|
1126
|
+
}
|
|
1127
|
+
const {
|
|
1128
|
+
options
|
|
1129
|
+
} = schema;
|
|
1130
|
+
if (typeof options !== "object" || options === null || !("accept" in options)) {
|
|
1131
|
+
return undefined;
|
|
1132
|
+
}
|
|
1133
|
+
const {
|
|
1134
|
+
accept
|
|
1135
|
+
} = options;
|
|
1136
|
+
return typeof accept === "string" ? accept : undefined;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/** A media value, read as a plain record. */
|
|
1140
|
+
function mediaValueOf(value) {
|
|
1141
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1142
|
+
return undefined;
|
|
1143
|
+
}
|
|
1144
|
+
return value;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* The serialized schema at a source path.
|
|
1149
|
+
*
|
|
1150
|
+
* Only the schema is taken from resolution -- `accept` is not on the value, and
|
|
1151
|
+
* the value itself comes from the error. Same call `missingFileRef` in
|
|
1152
|
+
* `diagnostics.ts` makes, and it can fail the same ways (a schema that failed
|
|
1153
|
+
* to serialize, a path that no longer resolves), so failure is not an error
|
|
1154
|
+
* here, just an absence.
|
|
1155
|
+
*/
|
|
1156
|
+
function resolveSchemaAt$1(sourcePath, content) {
|
|
1157
|
+
if (!content.source || !content.schema) {
|
|
1158
|
+
return undefined;
|
|
1159
|
+
}
|
|
1160
|
+
try {
|
|
1161
|
+
const [, modulePath] = core.Internal.splitModuleFilePathAndModulePath(sourcePath);
|
|
1162
|
+
return core.Internal.resolvePath(modulePath, content.source, content.schema).schema;
|
|
1163
|
+
} catch {
|
|
1164
|
+
return undefined;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
860
1168
|
/** Marks diagnostics as ours, so a client can filter on it. */
|
|
861
1169
|
const VAL_DIAGNOSTIC_SOURCE = "val";
|
|
862
1170
|
|
|
@@ -948,7 +1256,8 @@ function createValDiagnostics({
|
|
|
948
1256
|
text,
|
|
949
1257
|
valRoot,
|
|
950
1258
|
snapshot,
|
|
951
|
-
galleryChecks
|
|
1259
|
+
galleryChecks,
|
|
1260
|
+
mediaMetadataChecks
|
|
952
1261
|
}) {
|
|
953
1262
|
if (content.errors === false) {
|
|
954
1263
|
return [];
|
|
@@ -1020,6 +1329,33 @@ function createValDiagnostics({
|
|
|
1020
1329
|
continue;
|
|
1021
1330
|
}
|
|
1022
1331
|
|
|
1332
|
+
// An unconditional media metadata placeholder: core reports
|
|
1333
|
+
// `image:check-metadata` on every `s.image()` carrying metadata, whether
|
|
1334
|
+
// or not it disagrees with the file. Show only what the adjudication
|
|
1335
|
+
// actually found, and nothing when it found nothing. Deliberately after
|
|
1336
|
+
// the missing-file branch above, so a deleted file is still reported as
|
|
1337
|
+
// `val/file-not-found` rather than as a metadata mismatch.
|
|
1338
|
+
if (isDeferredMediaMetadataCheckAt({
|
|
1339
|
+
sourcePath,
|
|
1340
|
+
error,
|
|
1341
|
+
content
|
|
1342
|
+
})) {
|
|
1343
|
+
const verdict = mediaMetadataChecks?.get(mediaMetadataCheckKey(sourcePath, error));
|
|
1344
|
+
for (const finding of verdict ?? []) {
|
|
1345
|
+
diagnostics.push(build(rangeOf(sourcePath, modulePathMap), finding.message, {
|
|
1346
|
+
code: "val/validation",
|
|
1347
|
+
sourcePath,
|
|
1348
|
+
...(finding.fixes ? {
|
|
1349
|
+
fixes: finding.fixes
|
|
1350
|
+
} : {}),
|
|
1351
|
+
...(finding.value !== undefined ? {
|
|
1352
|
+
value: finding.value
|
|
1353
|
+
} : {})
|
|
1354
|
+
}));
|
|
1355
|
+
}
|
|
1356
|
+
continue;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1023
1359
|
// A gallery-backed field whose path the gallery does not track. Core
|
|
1024
1360
|
// reports it with no fix because the remedy is elsewhere; giving it its
|
|
1025
1361
|
// own code is what lets the editor offer the two remedies.
|
|
@@ -2110,6 +2446,17 @@ async function createValCodeActions({
|
|
|
2110
2446
|
remoteHost = process.env.VAL_REMOTE_HOST || core.DEFAULT_VAL_REMOTE_HOST
|
|
2111
2447
|
}) {
|
|
2112
2448
|
const actions = [];
|
|
2449
|
+
/**
|
|
2450
|
+
* Fixes already offered, as `<fix target>|<fix>`.
|
|
2451
|
+
*
|
|
2452
|
+
* One problem can be reported as several diagnostics that share a fix: a
|
|
2453
|
+
* stale image reports its width and its height separately, and a gallery
|
|
2454
|
+
* check reports one finding per entry. The fix is the same patch each time --
|
|
2455
|
+
* `createFixPatch` corrects every field, and the gallery branch walks every
|
|
2456
|
+
* entry -- so without this the editor offers the identical "Val: update image
|
|
2457
|
+
* metadata" twice, and recomputes it (re-reading the image) to do so.
|
|
2458
|
+
*/
|
|
2459
|
+
const offered = new Set();
|
|
2113
2460
|
for (const diagnostic of diagnostics) {
|
|
2114
2461
|
const data = diagnostic.data;
|
|
2115
2462
|
if (!data?.fixes?.length) {
|
|
@@ -2148,6 +2495,10 @@ async function createValCodeActions({
|
|
|
2148
2495
|
if (!isLocalFix(fix)) {
|
|
2149
2496
|
continue;
|
|
2150
2497
|
}
|
|
2498
|
+
const fixTarget = data.fixSourcePath ?? data.sourcePath;
|
|
2499
|
+
if (offered.has(`${fixTarget}|${fix}`)) {
|
|
2500
|
+
continue;
|
|
2501
|
+
}
|
|
2151
2502
|
const fixEdit = await computeFixEdit({
|
|
2152
2503
|
document,
|
|
2153
2504
|
// A gallery check is reported on the entry but fixed against the record
|
|
@@ -2169,6 +2520,7 @@ async function createValCodeActions({
|
|
|
2169
2520
|
if (!fixEdit) {
|
|
2170
2521
|
continue;
|
|
2171
2522
|
}
|
|
2523
|
+
offered.add(`${fixTarget}|${fix}`);
|
|
2172
2524
|
actions.push(vscodeLanguageserver.CodeAction.create(FIX_TITLES[fix] ?? `Val: ${fix}`,
|
|
2173
2525
|
// Not always `document.uri`: a fix inside a `.jsonValues()` entry
|
|
2174
2526
|
// edits the entry's own `*.val.json`, which is a different file from
|
|
@@ -3530,6 +3882,14 @@ function createValLanguageServer(connection) {
|
|
|
3530
3882
|
runFixHandler: args => activeProject.runFixHandler(args)
|
|
3531
3883
|
})
|
|
3532
3884
|
}) : undefined;
|
|
3885
|
+
// Core also defers "does this image's stored metadata match its file",
|
|
3886
|
+
// reporting it on every `s.image()` that carries any metadata. Same
|
|
3887
|
+
// treatment: adjudicate with the fix machinery before showing anything.
|
|
3888
|
+
const mediaMetadataChecks = result.content.errors !== false && result.content.errors.validation ? await resolveMediaMetadataChecks({
|
|
3889
|
+
validation: result.content.errors.validation,
|
|
3890
|
+
content: result.content,
|
|
3891
|
+
valRoot: activeProject.valRoot
|
|
3892
|
+
}) : undefined;
|
|
3533
3893
|
const diagnostics = createValDiagnostics({
|
|
3534
3894
|
moduleFilePath,
|
|
3535
3895
|
content: result.content,
|
|
@@ -3540,6 +3900,9 @@ function createValLanguageServer(connection) {
|
|
|
3540
3900
|
} : {}),
|
|
3541
3901
|
...(galleryChecks ? {
|
|
3542
3902
|
galleryChecks
|
|
3903
|
+
} : {}),
|
|
3904
|
+
...(mediaMetadataChecks ? {
|
|
3905
|
+
mediaMetadataChecks
|
|
3543
3906
|
} : {})
|
|
3544
3907
|
});
|
|
3545
3908
|
const unregistered = findMissingModuleDiagnostic(project.valRoot, moduleFilePath, readOpenDocument);
|