@valbuild/server 0.98.0 → 0.99.1
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.
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { ValidationError } from "@valbuild/core";
|
|
2
|
-
export declare function getValidationErrorFileRef(validationError: ValidationError):
|
|
2
|
+
export declare function getValidationErrorFileRef(validationError: ValidationError): any;
|
|
@@ -1187,14 +1187,24 @@ class Service {
|
|
|
1187
1187
|
const schema = this.extracted.schemas[moduleFilePath];
|
|
1188
1188
|
const serializedSchema = this.extracted.serializedSchemas[moduleFilePath];
|
|
1189
1189
|
const moduleError = this.extracted.moduleErrors.find(e => e.path === moduleFilePath);
|
|
1190
|
+
// A module whose `def()` threw is recorded WITHOUT a path: the import never
|
|
1191
|
+
// got far enough to reveal one, so it cannot be matched above. Those errors
|
|
1192
|
+
// are precisely why a module can be missing here, so report them too -
|
|
1193
|
+
// "was not found in val.modules" alone sends the reader hunting for a
|
|
1194
|
+
// registration that is already there.
|
|
1195
|
+
const unattributedModuleErrors = this.extracted.moduleErrors.filter(e => e.path === undefined);
|
|
1190
1196
|
if (source === undefined || schema === undefined || serializedSchema === undefined) {
|
|
1191
1197
|
return {
|
|
1192
1198
|
path: moduleFilePath,
|
|
1193
1199
|
errors: {
|
|
1194
1200
|
invalidModulePath: moduleFilePath,
|
|
1195
|
-
fatal: [{
|
|
1196
|
-
message:
|
|
1197
|
-
}]
|
|
1201
|
+
fatal: moduleError ? [{
|
|
1202
|
+
message: moduleError.message
|
|
1203
|
+
}] : [{
|
|
1204
|
+
message: `Module '${moduleFilePath}' was not found in val.modules`
|
|
1205
|
+
}, ...unattributedModuleErrors.map(e => ({
|
|
1206
|
+
message: e.message
|
|
1207
|
+
}))]
|
|
1198
1208
|
}
|
|
1199
1209
|
};
|
|
1200
1210
|
}
|
|
@@ -5550,6 +5560,7 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5550
5560
|
// #region sources
|
|
5551
5561
|
"/sources/~": {
|
|
5552
5562
|
PUT: async req => {
|
|
5563
|
+
var _patchOps$patches;
|
|
5553
5564
|
const query = req.query;
|
|
5554
5565
|
const cookies = req.cookies;
|
|
5555
5566
|
// TODO: filter results by moduleFilePath
|
|
@@ -5607,27 +5618,34 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5607
5618
|
const unpatchedSources = sourcesRes.sources;
|
|
5608
5619
|
// Default to true to keep the legacy contract for older clients.
|
|
5609
5620
|
// The studio client always passes false: it owns patch application
|
|
5610
|
-
// and
|
|
5621
|
+
// and validation, and treats /sources/~ as a pure un-patched read.
|
|
5611
5622
|
const applyPatches = query.apply_patches !== false;
|
|
5612
|
-
|
|
5623
|
+
// NOTE: renders are computed here even when the client applies patches
|
|
5624
|
+
// itself: the render select functions live on the Schema instances and
|
|
5625
|
+
// are not part of the serialized schema, so the client cannot derive
|
|
5626
|
+
// them. They are computed on the patched sources, so that previews
|
|
5627
|
+
// (list titles / subtitles / images) reflect the patches that apply.
|
|
5628
|
+
let patchedSources = sourcesRes.sources;
|
|
5629
|
+
if ((((_patchOps$patches = patchOps.patches) === null || _patchOps$patches === void 0 ? void 0 : _patchOps$patches.length) ?? 0) > 0) {
|
|
5613
5630
|
const onlyPatchedTreeModules = await serverOps.getSources({
|
|
5614
5631
|
...patchAnalysis,
|
|
5615
5632
|
...patchOps
|
|
5616
5633
|
});
|
|
5617
|
-
|
|
5618
|
-
sources
|
|
5619
|
-
|
|
5620
|
-
...(onlyPatchedTreeModules.sources || {})
|
|
5621
|
-
},
|
|
5622
|
-
errors: {
|
|
5623
|
-
...sourcesRes.errors,
|
|
5624
|
-
...(onlyPatchedTreeModules.errors || {})
|
|
5625
|
-
}
|
|
5634
|
+
patchedSources = {
|
|
5635
|
+
...sourcesRes.sources,
|
|
5636
|
+
...(onlyPatchedTreeModules.sources || {})
|
|
5626
5637
|
};
|
|
5638
|
+
if (applyPatches) {
|
|
5639
|
+
sourcesRes = {
|
|
5640
|
+
sources: patchedSources,
|
|
5641
|
+
errors: {
|
|
5642
|
+
...sourcesRes.errors,
|
|
5643
|
+
...(onlyPatchedTreeModules.errors || {})
|
|
5644
|
+
}
|
|
5645
|
+
};
|
|
5646
|
+
}
|
|
5627
5647
|
}
|
|
5628
|
-
const renderRes =
|
|
5629
|
-
renders: {}
|
|
5630
|
-
};
|
|
5648
|
+
const renderRes = await serverOps.getRenders(schemasRes, patchedSources);
|
|
5631
5649
|
let sourcesValidation = {
|
|
5632
5650
|
errors: {},
|
|
5633
5651
|
files: {},
|
|
@@ -7778,6 +7796,33 @@ async function downloadFileFromRemote(ref, filePath) {
|
|
|
7778
7796
|
// record-level fix expands into per-entry errors that should point at the
|
|
7779
7797
|
// individual entry (e.g. `?p="/public/val/logo.png"`) rather than the record.
|
|
7780
7798
|
|
|
7799
|
+
// Gallery entries are keyed by their file path. Remote galleries key uploaded
|
|
7800
|
+
// entries by a remote URL while keeping the file on disk at its local path, so
|
|
7801
|
+
// normalize a remote-URL key back to that local path for on-disk reads.
|
|
7802
|
+
function galleryKeyToLocalPath(key) {
|
|
7803
|
+
const res = core.Internal.remote.splitRemoteRef(key);
|
|
7804
|
+
return res.status === "success" ? `/${res.filePath}` : key;
|
|
7805
|
+
}
|
|
7806
|
+
|
|
7807
|
+
// Patch path of the record that holds a media entry. Media keys are file paths
|
|
7808
|
+
// that can contain dots, which sourceToPatchPath cannot round-trip, so strip
|
|
7809
|
+
// the (JSON-encoded) key segment off the entry source path and convert the
|
|
7810
|
+
// remaining parent path — whose ancestors are plain object keys / array
|
|
7811
|
+
// indices — instead.
|
|
7812
|
+
function parentPatchPathOfMediaEntry(sourcePath, entryKey) {
|
|
7813
|
+
const jsonKey = JSON.stringify(entryKey);
|
|
7814
|
+
let parent = sourcePath;
|
|
7815
|
+
if (parent.endsWith(jsonKey)) {
|
|
7816
|
+
parent = parent.slice(0, parent.length - jsonKey.length);
|
|
7817
|
+
}
|
|
7818
|
+
if (parent.endsWith(".")) {
|
|
7819
|
+
parent = parent.slice(0, -1);
|
|
7820
|
+
} else if (parent.endsWith(core.Internal.ModuleFilePathSep)) {
|
|
7821
|
+
parent = parent.slice(0, parent.length - core.Internal.ModuleFilePathSep.length);
|
|
7822
|
+
}
|
|
7823
|
+
return patch.sourceToPatchPath(parent);
|
|
7824
|
+
}
|
|
7825
|
+
|
|
7781
7826
|
// TODO: find a better name? transformFixesToPatch?
|
|
7782
7827
|
async function createFixPatch(config, apply, sourcePath, validationError, remoteFiles, moduleSource, moduleSchema) {
|
|
7783
7828
|
const remainingErrors = [];
|
|
@@ -7955,6 +8000,61 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
7955
8000
|
path: patch.sourceToPatchPath(sourcePath)
|
|
7956
8001
|
});
|
|
7957
8002
|
}
|
|
8003
|
+
} else if (fix === "images:upload-remote" || fix === "files:upload-remote") {
|
|
8004
|
+
// Gallery entry: the record is keyed by the file path, so uploading to
|
|
8005
|
+
// remote means renaming the key from the local path to the remote URL
|
|
8006
|
+
// (remove the old key, add the new one with the same metadata).
|
|
8007
|
+
const remoteFile = remoteFiles[sourcePath];
|
|
8008
|
+
if (!remoteFile) {
|
|
8009
|
+
remainingErrors.push({
|
|
8010
|
+
...validationError,
|
|
8011
|
+
message: "Cannot fix local to remote gallery entry: remote file was not uploaded",
|
|
8012
|
+
fixes: undefined
|
|
8013
|
+
});
|
|
8014
|
+
continue;
|
|
8015
|
+
}
|
|
8016
|
+
const metadata = remoteFile.metadata;
|
|
8017
|
+
if (!metadata) {
|
|
8018
|
+
remainingErrors.push({
|
|
8019
|
+
...validationError,
|
|
8020
|
+
message: "Failed to get metadata for remote gallery file",
|
|
8021
|
+
fixes: undefined
|
|
8022
|
+
});
|
|
8023
|
+
continue;
|
|
8024
|
+
}
|
|
8025
|
+
// The entry is keyed by its (local) file path, which can contain dots
|
|
8026
|
+
// (e.g. image.png). sourceToPatchPath can't round-trip such keys, so
|
|
8027
|
+
// build the patch path from the parent record path plus the key (which
|
|
8028
|
+
// validateMediaKey carries as the error value), like the UI does.
|
|
8029
|
+
const entryKey = validationError.value;
|
|
8030
|
+
if (typeof entryKey !== "string") {
|
|
8031
|
+
remainingErrors.push({
|
|
8032
|
+
...validationError,
|
|
8033
|
+
message: "Cannot rewrite remote gallery entry: missing entry key",
|
|
8034
|
+
fixes: undefined
|
|
8035
|
+
});
|
|
8036
|
+
continue;
|
|
8037
|
+
}
|
|
8038
|
+
const parentPath = parentPatchPathOfMediaEntry(sourcePath, entryKey);
|
|
8039
|
+
const removePath = parentPath.concat(entryKey);
|
|
8040
|
+
const addPath = parentPath.concat(remoteFile.ref);
|
|
8041
|
+
if (!patch.isNotRoot(removePath) || !patch.isNotRoot(addPath)) {
|
|
8042
|
+
remainingErrors.push({
|
|
8043
|
+
...validationError,
|
|
8044
|
+
message: "Cannot rewrite remote gallery entry at root path",
|
|
8045
|
+
fixes: undefined
|
|
8046
|
+
});
|
|
8047
|
+
continue;
|
|
8048
|
+
}
|
|
8049
|
+
patch$1.push({
|
|
8050
|
+
op: "remove",
|
|
8051
|
+
path: removePath
|
|
8052
|
+
});
|
|
8053
|
+
patch$1.push({
|
|
8054
|
+
op: "add",
|
|
8055
|
+
path: addPath,
|
|
8056
|
+
value: metadata
|
|
8057
|
+
});
|
|
7958
8058
|
} else if (fix === "image:download-remote" || fix === "file:download-remote") {
|
|
7959
8059
|
const v = getRemoteValueFromValidationError(validationError);
|
|
7960
8060
|
if (!v.success) {
|
|
@@ -8031,7 +8131,7 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
8031
8131
|
}
|
|
8032
8132
|
const gallerySource = moduleSource;
|
|
8033
8133
|
for (const [entryKey, storedEntry] of Object.entries(gallerySource)) {
|
|
8034
|
-
const filename = path__namespace["default"].join(config.projectRoot, entryKey);
|
|
8134
|
+
const filename = path__namespace["default"].join(config.projectRoot, galleryKeyToLocalPath(entryKey));
|
|
8035
8135
|
let buffer;
|
|
8036
8136
|
try {
|
|
8037
8137
|
buffer = fs__default["default"].readFileSync(filename);
|
|
@@ -8556,31 +8656,39 @@ function getModulePathRange(modulePath, modulePathMap,
|
|
|
8556
8656
|
// existing callers.
|
|
8557
8657
|
target = "key") {
|
|
8558
8658
|
var _range$children;
|
|
8559
|
-
// Handle
|
|
8560
|
-
if (
|
|
8561
|
-
return undefined;
|
|
8562
|
-
}
|
|
8563
|
-
let segments;
|
|
8564
|
-
try {
|
|
8565
|
-
// Quote-aware splitter that correctly handles keys containing dots
|
|
8566
|
-
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8567
|
-
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8568
|
-
segments = core.Internal.splitModulePath(modulePath);
|
|
8569
|
-
} catch {
|
|
8570
|
-
// Return undefined if the module path is malformed. This can happen when
|
|
8571
|
-
// there are upstream errors in schema serialization.
|
|
8659
|
+
// Handle invalid module paths gracefully
|
|
8660
|
+
if (typeof modulePath !== "string") {
|
|
8572
8661
|
return undefined;
|
|
8573
8662
|
}
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8663
|
+
let range;
|
|
8664
|
+
if (modulePath === "") {
|
|
8665
|
+
// The empty module path is the module root, stored under the "" key: the
|
|
8666
|
+
// source argument of `c.define`. This is what module-level diagnostics
|
|
8667
|
+
// (which have no module path) resolve to.
|
|
8668
|
+
range = modulePathMap[""];
|
|
8669
|
+
} else {
|
|
8670
|
+
let segments;
|
|
8671
|
+
try {
|
|
8672
|
+
// Quote-aware splitter that correctly handles keys containing dots
|
|
8673
|
+
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8674
|
+
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8675
|
+
segments = core.Internal.splitModulePath(modulePath);
|
|
8676
|
+
} catch {
|
|
8677
|
+
// Return undefined if the module path is malformed. This can happen when
|
|
8678
|
+
// there are upstream errors in schema serialization.
|
|
8679
|
+
return undefined;
|
|
8680
|
+
}
|
|
8681
|
+
if (segments.length === 0) {
|
|
8682
|
+
return undefined;
|
|
8683
|
+
}
|
|
8684
|
+
range = modulePathMap[segments[0]];
|
|
8685
|
+
for (const pathSegment of segments.slice(1)) {
|
|
8686
|
+
var _range;
|
|
8687
|
+
if (!range) {
|
|
8688
|
+
break;
|
|
8689
|
+
}
|
|
8690
|
+
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8582
8691
|
}
|
|
8583
|
-
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8584
8692
|
}
|
|
8585
8693
|
if (!range) {
|
|
8586
8694
|
return undefined;
|
|
@@ -8612,7 +8720,20 @@ function createModulePathMap(sourceFile) {
|
|
|
8612
8720
|
if (ts__default["default"].isExportAssignment(child)) {
|
|
8613
8721
|
const contentNode = child.expression && ts__default["default"].isCallExpression(child.expression) && child.expression.arguments[2];
|
|
8614
8722
|
if (contentNode) {
|
|
8615
|
-
|
|
8723
|
+
const map = traverse(contentNode, sourceFile) ?? {};
|
|
8724
|
+
// Always expose the module root under the "" key so diagnostics without
|
|
8725
|
+
// a module path still resolve to a location. `traverse` already emits ""
|
|
8726
|
+
// for primitive roots (with a tighter range), so keep that if present.
|
|
8727
|
+
// Object/record keys are never "" (see traverseObjectLiteral), so this
|
|
8728
|
+
// cannot collide with a real key.
|
|
8729
|
+
return {
|
|
8730
|
+
...map,
|
|
8731
|
+
"": map[""] ?? {
|
|
8732
|
+
children: {},
|
|
8733
|
+
start: sourceFile.getLineAndCharacterOfPosition(contentNode.getStart(sourceFile)),
|
|
8734
|
+
end: sourceFile.getLineAndCharacterOfPosition(contentNode.getEnd())
|
|
8735
|
+
}
|
|
8736
|
+
};
|
|
8616
8737
|
}
|
|
8617
8738
|
}
|
|
8618
8739
|
}
|
|
@@ -1187,14 +1187,24 @@ class Service {
|
|
|
1187
1187
|
const schema = this.extracted.schemas[moduleFilePath];
|
|
1188
1188
|
const serializedSchema = this.extracted.serializedSchemas[moduleFilePath];
|
|
1189
1189
|
const moduleError = this.extracted.moduleErrors.find(e => e.path === moduleFilePath);
|
|
1190
|
+
// A module whose `def()` threw is recorded WITHOUT a path: the import never
|
|
1191
|
+
// got far enough to reveal one, so it cannot be matched above. Those errors
|
|
1192
|
+
// are precisely why a module can be missing here, so report them too -
|
|
1193
|
+
// "was not found in val.modules" alone sends the reader hunting for a
|
|
1194
|
+
// registration that is already there.
|
|
1195
|
+
const unattributedModuleErrors = this.extracted.moduleErrors.filter(e => e.path === undefined);
|
|
1190
1196
|
if (source === undefined || schema === undefined || serializedSchema === undefined) {
|
|
1191
1197
|
return {
|
|
1192
1198
|
path: moduleFilePath,
|
|
1193
1199
|
errors: {
|
|
1194
1200
|
invalidModulePath: moduleFilePath,
|
|
1195
|
-
fatal: [{
|
|
1196
|
-
message:
|
|
1197
|
-
}]
|
|
1201
|
+
fatal: moduleError ? [{
|
|
1202
|
+
message: moduleError.message
|
|
1203
|
+
}] : [{
|
|
1204
|
+
message: `Module '${moduleFilePath}' was not found in val.modules`
|
|
1205
|
+
}, ...unattributedModuleErrors.map(e => ({
|
|
1206
|
+
message: e.message
|
|
1207
|
+
}))]
|
|
1198
1208
|
}
|
|
1199
1209
|
};
|
|
1200
1210
|
}
|
|
@@ -5550,6 +5560,7 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5550
5560
|
// #region sources
|
|
5551
5561
|
"/sources/~": {
|
|
5552
5562
|
PUT: async req => {
|
|
5563
|
+
var _patchOps$patches;
|
|
5553
5564
|
const query = req.query;
|
|
5554
5565
|
const cookies = req.cookies;
|
|
5555
5566
|
// TODO: filter results by moduleFilePath
|
|
@@ -5607,27 +5618,34 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5607
5618
|
const unpatchedSources = sourcesRes.sources;
|
|
5608
5619
|
// Default to true to keep the legacy contract for older clients.
|
|
5609
5620
|
// The studio client always passes false: it owns patch application
|
|
5610
|
-
// and
|
|
5621
|
+
// and validation, and treats /sources/~ as a pure un-patched read.
|
|
5611
5622
|
const applyPatches = query.apply_patches !== false;
|
|
5612
|
-
|
|
5623
|
+
// NOTE: renders are computed here even when the client applies patches
|
|
5624
|
+
// itself: the render select functions live on the Schema instances and
|
|
5625
|
+
// are not part of the serialized schema, so the client cannot derive
|
|
5626
|
+
// them. They are computed on the patched sources, so that previews
|
|
5627
|
+
// (list titles / subtitles / images) reflect the patches that apply.
|
|
5628
|
+
let patchedSources = sourcesRes.sources;
|
|
5629
|
+
if ((((_patchOps$patches = patchOps.patches) === null || _patchOps$patches === void 0 ? void 0 : _patchOps$patches.length) ?? 0) > 0) {
|
|
5613
5630
|
const onlyPatchedTreeModules = await serverOps.getSources({
|
|
5614
5631
|
...patchAnalysis,
|
|
5615
5632
|
...patchOps
|
|
5616
5633
|
});
|
|
5617
|
-
|
|
5618
|
-
sources
|
|
5619
|
-
|
|
5620
|
-
...(onlyPatchedTreeModules.sources || {})
|
|
5621
|
-
},
|
|
5622
|
-
errors: {
|
|
5623
|
-
...sourcesRes.errors,
|
|
5624
|
-
...(onlyPatchedTreeModules.errors || {})
|
|
5625
|
-
}
|
|
5634
|
+
patchedSources = {
|
|
5635
|
+
...sourcesRes.sources,
|
|
5636
|
+
...(onlyPatchedTreeModules.sources || {})
|
|
5626
5637
|
};
|
|
5638
|
+
if (applyPatches) {
|
|
5639
|
+
sourcesRes = {
|
|
5640
|
+
sources: patchedSources,
|
|
5641
|
+
errors: {
|
|
5642
|
+
...sourcesRes.errors,
|
|
5643
|
+
...(onlyPatchedTreeModules.errors || {})
|
|
5644
|
+
}
|
|
5645
|
+
};
|
|
5646
|
+
}
|
|
5627
5647
|
}
|
|
5628
|
-
const renderRes =
|
|
5629
|
-
renders: {}
|
|
5630
|
-
};
|
|
5648
|
+
const renderRes = await serverOps.getRenders(schemasRes, patchedSources);
|
|
5631
5649
|
let sourcesValidation = {
|
|
5632
5650
|
errors: {},
|
|
5633
5651
|
files: {},
|
|
@@ -7778,6 +7796,33 @@ async function downloadFileFromRemote(ref, filePath) {
|
|
|
7778
7796
|
// record-level fix expands into per-entry errors that should point at the
|
|
7779
7797
|
// individual entry (e.g. `?p="/public/val/logo.png"`) rather than the record.
|
|
7780
7798
|
|
|
7799
|
+
// Gallery entries are keyed by their file path. Remote galleries key uploaded
|
|
7800
|
+
// entries by a remote URL while keeping the file on disk at its local path, so
|
|
7801
|
+
// normalize a remote-URL key back to that local path for on-disk reads.
|
|
7802
|
+
function galleryKeyToLocalPath(key) {
|
|
7803
|
+
const res = core.Internal.remote.splitRemoteRef(key);
|
|
7804
|
+
return res.status === "success" ? `/${res.filePath}` : key;
|
|
7805
|
+
}
|
|
7806
|
+
|
|
7807
|
+
// Patch path of the record that holds a media entry. Media keys are file paths
|
|
7808
|
+
// that can contain dots, which sourceToPatchPath cannot round-trip, so strip
|
|
7809
|
+
// the (JSON-encoded) key segment off the entry source path and convert the
|
|
7810
|
+
// remaining parent path — whose ancestors are plain object keys / array
|
|
7811
|
+
// indices — instead.
|
|
7812
|
+
function parentPatchPathOfMediaEntry(sourcePath, entryKey) {
|
|
7813
|
+
const jsonKey = JSON.stringify(entryKey);
|
|
7814
|
+
let parent = sourcePath;
|
|
7815
|
+
if (parent.endsWith(jsonKey)) {
|
|
7816
|
+
parent = parent.slice(0, parent.length - jsonKey.length);
|
|
7817
|
+
}
|
|
7818
|
+
if (parent.endsWith(".")) {
|
|
7819
|
+
parent = parent.slice(0, -1);
|
|
7820
|
+
} else if (parent.endsWith(core.Internal.ModuleFilePathSep)) {
|
|
7821
|
+
parent = parent.slice(0, parent.length - core.Internal.ModuleFilePathSep.length);
|
|
7822
|
+
}
|
|
7823
|
+
return patch.sourceToPatchPath(parent);
|
|
7824
|
+
}
|
|
7825
|
+
|
|
7781
7826
|
// TODO: find a better name? transformFixesToPatch?
|
|
7782
7827
|
async function createFixPatch(config, apply, sourcePath, validationError, remoteFiles, moduleSource, moduleSchema) {
|
|
7783
7828
|
const remainingErrors = [];
|
|
@@ -7955,6 +8000,61 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
7955
8000
|
path: patch.sourceToPatchPath(sourcePath)
|
|
7956
8001
|
});
|
|
7957
8002
|
}
|
|
8003
|
+
} else if (fix === "images:upload-remote" || fix === "files:upload-remote") {
|
|
8004
|
+
// Gallery entry: the record is keyed by the file path, so uploading to
|
|
8005
|
+
// remote means renaming the key from the local path to the remote URL
|
|
8006
|
+
// (remove the old key, add the new one with the same metadata).
|
|
8007
|
+
const remoteFile = remoteFiles[sourcePath];
|
|
8008
|
+
if (!remoteFile) {
|
|
8009
|
+
remainingErrors.push({
|
|
8010
|
+
...validationError,
|
|
8011
|
+
message: "Cannot fix local to remote gallery entry: remote file was not uploaded",
|
|
8012
|
+
fixes: undefined
|
|
8013
|
+
});
|
|
8014
|
+
continue;
|
|
8015
|
+
}
|
|
8016
|
+
const metadata = remoteFile.metadata;
|
|
8017
|
+
if (!metadata) {
|
|
8018
|
+
remainingErrors.push({
|
|
8019
|
+
...validationError,
|
|
8020
|
+
message: "Failed to get metadata for remote gallery file",
|
|
8021
|
+
fixes: undefined
|
|
8022
|
+
});
|
|
8023
|
+
continue;
|
|
8024
|
+
}
|
|
8025
|
+
// The entry is keyed by its (local) file path, which can contain dots
|
|
8026
|
+
// (e.g. image.png). sourceToPatchPath can't round-trip such keys, so
|
|
8027
|
+
// build the patch path from the parent record path plus the key (which
|
|
8028
|
+
// validateMediaKey carries as the error value), like the UI does.
|
|
8029
|
+
const entryKey = validationError.value;
|
|
8030
|
+
if (typeof entryKey !== "string") {
|
|
8031
|
+
remainingErrors.push({
|
|
8032
|
+
...validationError,
|
|
8033
|
+
message: "Cannot rewrite remote gallery entry: missing entry key",
|
|
8034
|
+
fixes: undefined
|
|
8035
|
+
});
|
|
8036
|
+
continue;
|
|
8037
|
+
}
|
|
8038
|
+
const parentPath = parentPatchPathOfMediaEntry(sourcePath, entryKey);
|
|
8039
|
+
const removePath = parentPath.concat(entryKey);
|
|
8040
|
+
const addPath = parentPath.concat(remoteFile.ref);
|
|
8041
|
+
if (!patch.isNotRoot(removePath) || !patch.isNotRoot(addPath)) {
|
|
8042
|
+
remainingErrors.push({
|
|
8043
|
+
...validationError,
|
|
8044
|
+
message: "Cannot rewrite remote gallery entry at root path",
|
|
8045
|
+
fixes: undefined
|
|
8046
|
+
});
|
|
8047
|
+
continue;
|
|
8048
|
+
}
|
|
8049
|
+
patch$1.push({
|
|
8050
|
+
op: "remove",
|
|
8051
|
+
path: removePath
|
|
8052
|
+
});
|
|
8053
|
+
patch$1.push({
|
|
8054
|
+
op: "add",
|
|
8055
|
+
path: addPath,
|
|
8056
|
+
value: metadata
|
|
8057
|
+
});
|
|
7958
8058
|
} else if (fix === "image:download-remote" || fix === "file:download-remote") {
|
|
7959
8059
|
const v = getRemoteValueFromValidationError(validationError);
|
|
7960
8060
|
if (!v.success) {
|
|
@@ -8031,7 +8131,7 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
8031
8131
|
}
|
|
8032
8132
|
const gallerySource = moduleSource;
|
|
8033
8133
|
for (const [entryKey, storedEntry] of Object.entries(gallerySource)) {
|
|
8034
|
-
const filename = path__namespace["default"].join(config.projectRoot, entryKey);
|
|
8134
|
+
const filename = path__namespace["default"].join(config.projectRoot, galleryKeyToLocalPath(entryKey));
|
|
8035
8135
|
let buffer;
|
|
8036
8136
|
try {
|
|
8037
8137
|
buffer = fs__default["default"].readFileSync(filename);
|
|
@@ -8556,31 +8656,39 @@ function getModulePathRange(modulePath, modulePathMap,
|
|
|
8556
8656
|
// existing callers.
|
|
8557
8657
|
target = "key") {
|
|
8558
8658
|
var _range$children;
|
|
8559
|
-
// Handle
|
|
8560
|
-
if (
|
|
8561
|
-
return undefined;
|
|
8562
|
-
}
|
|
8563
|
-
let segments;
|
|
8564
|
-
try {
|
|
8565
|
-
// Quote-aware splitter that correctly handles keys containing dots
|
|
8566
|
-
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8567
|
-
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8568
|
-
segments = core.Internal.splitModulePath(modulePath);
|
|
8569
|
-
} catch {
|
|
8570
|
-
// Return undefined if the module path is malformed. This can happen when
|
|
8571
|
-
// there are upstream errors in schema serialization.
|
|
8659
|
+
// Handle invalid module paths gracefully
|
|
8660
|
+
if (typeof modulePath !== "string") {
|
|
8572
8661
|
return undefined;
|
|
8573
8662
|
}
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8663
|
+
let range;
|
|
8664
|
+
if (modulePath === "") {
|
|
8665
|
+
// The empty module path is the module root, stored under the "" key: the
|
|
8666
|
+
// source argument of `c.define`. This is what module-level diagnostics
|
|
8667
|
+
// (which have no module path) resolve to.
|
|
8668
|
+
range = modulePathMap[""];
|
|
8669
|
+
} else {
|
|
8670
|
+
let segments;
|
|
8671
|
+
try {
|
|
8672
|
+
// Quote-aware splitter that correctly handles keys containing dots
|
|
8673
|
+
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8674
|
+
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8675
|
+
segments = core.Internal.splitModulePath(modulePath);
|
|
8676
|
+
} catch {
|
|
8677
|
+
// Return undefined if the module path is malformed. This can happen when
|
|
8678
|
+
// there are upstream errors in schema serialization.
|
|
8679
|
+
return undefined;
|
|
8680
|
+
}
|
|
8681
|
+
if (segments.length === 0) {
|
|
8682
|
+
return undefined;
|
|
8683
|
+
}
|
|
8684
|
+
range = modulePathMap[segments[0]];
|
|
8685
|
+
for (const pathSegment of segments.slice(1)) {
|
|
8686
|
+
var _range;
|
|
8687
|
+
if (!range) {
|
|
8688
|
+
break;
|
|
8689
|
+
}
|
|
8690
|
+
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8582
8691
|
}
|
|
8583
|
-
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8584
8692
|
}
|
|
8585
8693
|
if (!range) {
|
|
8586
8694
|
return undefined;
|
|
@@ -8612,7 +8720,20 @@ function createModulePathMap(sourceFile) {
|
|
|
8612
8720
|
if (ts__default["default"].isExportAssignment(child)) {
|
|
8613
8721
|
const contentNode = child.expression && ts__default["default"].isCallExpression(child.expression) && child.expression.arguments[2];
|
|
8614
8722
|
if (contentNode) {
|
|
8615
|
-
|
|
8723
|
+
const map = traverse(contentNode, sourceFile) ?? {};
|
|
8724
|
+
// Always expose the module root under the "" key so diagnostics without
|
|
8725
|
+
// a module path still resolve to a location. `traverse` already emits ""
|
|
8726
|
+
// for primitive roots (with a tighter range), so keep that if present.
|
|
8727
|
+
// Object/record keys are never "" (see traverseObjectLiteral), so this
|
|
8728
|
+
// cannot collide with a real key.
|
|
8729
|
+
return {
|
|
8730
|
+
...map,
|
|
8731
|
+
"": map[""] ?? {
|
|
8732
|
+
children: {},
|
|
8733
|
+
start: sourceFile.getLineAndCharacterOfPosition(contentNode.getStart(sourceFile)),
|
|
8734
|
+
end: sourceFile.getLineAndCharacterOfPosition(contentNode.getEnd())
|
|
8735
|
+
}
|
|
8736
|
+
};
|
|
8616
8737
|
}
|
|
8617
8738
|
}
|
|
8618
8739
|
}
|
|
@@ -1153,14 +1153,24 @@ class Service {
|
|
|
1153
1153
|
const schema = this.extracted.schemas[moduleFilePath];
|
|
1154
1154
|
const serializedSchema = this.extracted.serializedSchemas[moduleFilePath];
|
|
1155
1155
|
const moduleError = this.extracted.moduleErrors.find(e => e.path === moduleFilePath);
|
|
1156
|
+
// A module whose `def()` threw is recorded WITHOUT a path: the import never
|
|
1157
|
+
// got far enough to reveal one, so it cannot be matched above. Those errors
|
|
1158
|
+
// are precisely why a module can be missing here, so report them too -
|
|
1159
|
+
// "was not found in val.modules" alone sends the reader hunting for a
|
|
1160
|
+
// registration that is already there.
|
|
1161
|
+
const unattributedModuleErrors = this.extracted.moduleErrors.filter(e => e.path === undefined);
|
|
1156
1162
|
if (source === undefined || schema === undefined || serializedSchema === undefined) {
|
|
1157
1163
|
return {
|
|
1158
1164
|
path: moduleFilePath,
|
|
1159
1165
|
errors: {
|
|
1160
1166
|
invalidModulePath: moduleFilePath,
|
|
1161
|
-
fatal: [{
|
|
1162
|
-
message:
|
|
1163
|
-
}]
|
|
1167
|
+
fatal: moduleError ? [{
|
|
1168
|
+
message: moduleError.message
|
|
1169
|
+
}] : [{
|
|
1170
|
+
message: `Module '${moduleFilePath}' was not found in val.modules`
|
|
1171
|
+
}, ...unattributedModuleErrors.map(e => ({
|
|
1172
|
+
message: e.message
|
|
1173
|
+
}))]
|
|
1164
1174
|
}
|
|
1165
1175
|
};
|
|
1166
1176
|
}
|
|
@@ -5516,6 +5526,7 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5516
5526
|
// #region sources
|
|
5517
5527
|
"/sources/~": {
|
|
5518
5528
|
PUT: async req => {
|
|
5529
|
+
var _patchOps$patches;
|
|
5519
5530
|
const query = req.query;
|
|
5520
5531
|
const cookies = req.cookies;
|
|
5521
5532
|
// TODO: filter results by moduleFilePath
|
|
@@ -5573,27 +5584,34 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5573
5584
|
const unpatchedSources = sourcesRes.sources;
|
|
5574
5585
|
// Default to true to keep the legacy contract for older clients.
|
|
5575
5586
|
// The studio client always passes false: it owns patch application
|
|
5576
|
-
// and
|
|
5587
|
+
// and validation, and treats /sources/~ as a pure un-patched read.
|
|
5577
5588
|
const applyPatches = query.apply_patches !== false;
|
|
5578
|
-
|
|
5589
|
+
// NOTE: renders are computed here even when the client applies patches
|
|
5590
|
+
// itself: the render select functions live on the Schema instances and
|
|
5591
|
+
// are not part of the serialized schema, so the client cannot derive
|
|
5592
|
+
// them. They are computed on the patched sources, so that previews
|
|
5593
|
+
// (list titles / subtitles / images) reflect the patches that apply.
|
|
5594
|
+
let patchedSources = sourcesRes.sources;
|
|
5595
|
+
if ((((_patchOps$patches = patchOps.patches) === null || _patchOps$patches === void 0 ? void 0 : _patchOps$patches.length) ?? 0) > 0) {
|
|
5579
5596
|
const onlyPatchedTreeModules = await serverOps.getSources({
|
|
5580
5597
|
...patchAnalysis,
|
|
5581
5598
|
...patchOps
|
|
5582
5599
|
});
|
|
5583
|
-
|
|
5584
|
-
sources
|
|
5585
|
-
|
|
5586
|
-
...(onlyPatchedTreeModules.sources || {})
|
|
5587
|
-
},
|
|
5588
|
-
errors: {
|
|
5589
|
-
...sourcesRes.errors,
|
|
5590
|
-
...(onlyPatchedTreeModules.errors || {})
|
|
5591
|
-
}
|
|
5600
|
+
patchedSources = {
|
|
5601
|
+
...sourcesRes.sources,
|
|
5602
|
+
...(onlyPatchedTreeModules.sources || {})
|
|
5592
5603
|
};
|
|
5604
|
+
if (applyPatches) {
|
|
5605
|
+
sourcesRes = {
|
|
5606
|
+
sources: patchedSources,
|
|
5607
|
+
errors: {
|
|
5608
|
+
...sourcesRes.errors,
|
|
5609
|
+
...(onlyPatchedTreeModules.errors || {})
|
|
5610
|
+
}
|
|
5611
|
+
};
|
|
5612
|
+
}
|
|
5593
5613
|
}
|
|
5594
|
-
const renderRes =
|
|
5595
|
-
renders: {}
|
|
5596
|
-
};
|
|
5614
|
+
const renderRes = await serverOps.getRenders(schemasRes, patchedSources);
|
|
5597
5615
|
let sourcesValidation = {
|
|
5598
5616
|
errors: {},
|
|
5599
5617
|
files: {},
|
|
@@ -7744,6 +7762,33 @@ async function downloadFileFromRemote(ref, filePath) {
|
|
|
7744
7762
|
// record-level fix expands into per-entry errors that should point at the
|
|
7745
7763
|
// individual entry (e.g. `?p="/public/val/logo.png"`) rather than the record.
|
|
7746
7764
|
|
|
7765
|
+
// Gallery entries are keyed by their file path. Remote galleries key uploaded
|
|
7766
|
+
// entries by a remote URL while keeping the file on disk at its local path, so
|
|
7767
|
+
// normalize a remote-URL key back to that local path for on-disk reads.
|
|
7768
|
+
function galleryKeyToLocalPath(key) {
|
|
7769
|
+
const res = Internal.remote.splitRemoteRef(key);
|
|
7770
|
+
return res.status === "success" ? `/${res.filePath}` : key;
|
|
7771
|
+
}
|
|
7772
|
+
|
|
7773
|
+
// Patch path of the record that holds a media entry. Media keys are file paths
|
|
7774
|
+
// that can contain dots, which sourceToPatchPath cannot round-trip, so strip
|
|
7775
|
+
// the (JSON-encoded) key segment off the entry source path and convert the
|
|
7776
|
+
// remaining parent path — whose ancestors are plain object keys / array
|
|
7777
|
+
// indices — instead.
|
|
7778
|
+
function parentPatchPathOfMediaEntry(sourcePath, entryKey) {
|
|
7779
|
+
const jsonKey = JSON.stringify(entryKey);
|
|
7780
|
+
let parent = sourcePath;
|
|
7781
|
+
if (parent.endsWith(jsonKey)) {
|
|
7782
|
+
parent = parent.slice(0, parent.length - jsonKey.length);
|
|
7783
|
+
}
|
|
7784
|
+
if (parent.endsWith(".")) {
|
|
7785
|
+
parent = parent.slice(0, -1);
|
|
7786
|
+
} else if (parent.endsWith(Internal.ModuleFilePathSep)) {
|
|
7787
|
+
parent = parent.slice(0, parent.length - Internal.ModuleFilePathSep.length);
|
|
7788
|
+
}
|
|
7789
|
+
return sourceToPatchPath(parent);
|
|
7790
|
+
}
|
|
7791
|
+
|
|
7747
7792
|
// TODO: find a better name? transformFixesToPatch?
|
|
7748
7793
|
async function createFixPatch(config, apply, sourcePath, validationError, remoteFiles, moduleSource, moduleSchema) {
|
|
7749
7794
|
const remainingErrors = [];
|
|
@@ -7921,6 +7966,61 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
7921
7966
|
path: sourceToPatchPath(sourcePath)
|
|
7922
7967
|
});
|
|
7923
7968
|
}
|
|
7969
|
+
} else if (fix === "images:upload-remote" || fix === "files:upload-remote") {
|
|
7970
|
+
// Gallery entry: the record is keyed by the file path, so uploading to
|
|
7971
|
+
// remote means renaming the key from the local path to the remote URL
|
|
7972
|
+
// (remove the old key, add the new one with the same metadata).
|
|
7973
|
+
const remoteFile = remoteFiles[sourcePath];
|
|
7974
|
+
if (!remoteFile) {
|
|
7975
|
+
remainingErrors.push({
|
|
7976
|
+
...validationError,
|
|
7977
|
+
message: "Cannot fix local to remote gallery entry: remote file was not uploaded",
|
|
7978
|
+
fixes: undefined
|
|
7979
|
+
});
|
|
7980
|
+
continue;
|
|
7981
|
+
}
|
|
7982
|
+
const metadata = remoteFile.metadata;
|
|
7983
|
+
if (!metadata) {
|
|
7984
|
+
remainingErrors.push({
|
|
7985
|
+
...validationError,
|
|
7986
|
+
message: "Failed to get metadata for remote gallery file",
|
|
7987
|
+
fixes: undefined
|
|
7988
|
+
});
|
|
7989
|
+
continue;
|
|
7990
|
+
}
|
|
7991
|
+
// The entry is keyed by its (local) file path, which can contain dots
|
|
7992
|
+
// (e.g. image.png). sourceToPatchPath can't round-trip such keys, so
|
|
7993
|
+
// build the patch path from the parent record path plus the key (which
|
|
7994
|
+
// validateMediaKey carries as the error value), like the UI does.
|
|
7995
|
+
const entryKey = validationError.value;
|
|
7996
|
+
if (typeof entryKey !== "string") {
|
|
7997
|
+
remainingErrors.push({
|
|
7998
|
+
...validationError,
|
|
7999
|
+
message: "Cannot rewrite remote gallery entry: missing entry key",
|
|
8000
|
+
fixes: undefined
|
|
8001
|
+
});
|
|
8002
|
+
continue;
|
|
8003
|
+
}
|
|
8004
|
+
const parentPath = parentPatchPathOfMediaEntry(sourcePath, entryKey);
|
|
8005
|
+
const removePath = parentPath.concat(entryKey);
|
|
8006
|
+
const addPath = parentPath.concat(remoteFile.ref);
|
|
8007
|
+
if (!isNotRoot(removePath) || !isNotRoot(addPath)) {
|
|
8008
|
+
remainingErrors.push({
|
|
8009
|
+
...validationError,
|
|
8010
|
+
message: "Cannot rewrite remote gallery entry at root path",
|
|
8011
|
+
fixes: undefined
|
|
8012
|
+
});
|
|
8013
|
+
continue;
|
|
8014
|
+
}
|
|
8015
|
+
patch.push({
|
|
8016
|
+
op: "remove",
|
|
8017
|
+
path: removePath
|
|
8018
|
+
});
|
|
8019
|
+
patch.push({
|
|
8020
|
+
op: "add",
|
|
8021
|
+
path: addPath,
|
|
8022
|
+
value: metadata
|
|
8023
|
+
});
|
|
7924
8024
|
} else if (fix === "image:download-remote" || fix === "file:download-remote") {
|
|
7925
8025
|
const v = getRemoteValueFromValidationError(validationError);
|
|
7926
8026
|
if (!v.success) {
|
|
@@ -7997,7 +8097,7 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
|
7997
8097
|
}
|
|
7998
8098
|
const gallerySource = moduleSource;
|
|
7999
8099
|
for (const [entryKey, storedEntry] of Object.entries(gallerySource)) {
|
|
8000
|
-
const filename = path__default.join(config.projectRoot, entryKey);
|
|
8100
|
+
const filename = path__default.join(config.projectRoot, galleryKeyToLocalPath(entryKey));
|
|
8001
8101
|
let buffer;
|
|
8002
8102
|
try {
|
|
8003
8103
|
buffer = fs.readFileSync(filename);
|
|
@@ -8522,31 +8622,39 @@ function getModulePathRange(modulePath, modulePathMap,
|
|
|
8522
8622
|
// existing callers.
|
|
8523
8623
|
target = "key") {
|
|
8524
8624
|
var _range$children;
|
|
8525
|
-
// Handle
|
|
8526
|
-
if (
|
|
8527
|
-
return undefined;
|
|
8528
|
-
}
|
|
8529
|
-
let segments;
|
|
8530
|
-
try {
|
|
8531
|
-
// Quote-aware splitter that correctly handles keys containing dots
|
|
8532
|
-
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8533
|
-
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8534
|
-
segments = Internal.splitModulePath(modulePath);
|
|
8535
|
-
} catch {
|
|
8536
|
-
// Return undefined if the module path is malformed. This can happen when
|
|
8537
|
-
// there are upstream errors in schema serialization.
|
|
8625
|
+
// Handle invalid module paths gracefully
|
|
8626
|
+
if (typeof modulePath !== "string") {
|
|
8538
8627
|
return undefined;
|
|
8539
8628
|
}
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8629
|
+
let range;
|
|
8630
|
+
if (modulePath === "") {
|
|
8631
|
+
// The empty module path is the module root, stored under the "" key: the
|
|
8632
|
+
// source argument of `c.define`. This is what module-level diagnostics
|
|
8633
|
+
// (which have no module path) resolve to.
|
|
8634
|
+
range = modulePathMap[""];
|
|
8635
|
+
} else {
|
|
8636
|
+
let segments;
|
|
8637
|
+
try {
|
|
8638
|
+
// Quote-aware splitter that correctly handles keys containing dots
|
|
8639
|
+
// (e.g. file refs like `"/public/val/images/logo.png"`), unlike a naive
|
|
8640
|
+
// split on ".". Throws on malformed input (e.g. unbalanced quotes).
|
|
8641
|
+
segments = Internal.splitModulePath(modulePath);
|
|
8642
|
+
} catch {
|
|
8643
|
+
// Return undefined if the module path is malformed. This can happen when
|
|
8644
|
+
// there are upstream errors in schema serialization.
|
|
8645
|
+
return undefined;
|
|
8646
|
+
}
|
|
8647
|
+
if (segments.length === 0) {
|
|
8648
|
+
return undefined;
|
|
8649
|
+
}
|
|
8650
|
+
range = modulePathMap[segments[0]];
|
|
8651
|
+
for (const pathSegment of segments.slice(1)) {
|
|
8652
|
+
var _range;
|
|
8653
|
+
if (!range) {
|
|
8654
|
+
break;
|
|
8655
|
+
}
|
|
8656
|
+
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8548
8657
|
}
|
|
8549
|
-
range = (_range = range) === null || _range === void 0 || (_range = _range.children) === null || _range === void 0 ? void 0 : _range[pathSegment];
|
|
8550
8658
|
}
|
|
8551
8659
|
if (!range) {
|
|
8552
8660
|
return undefined;
|
|
@@ -8578,7 +8686,20 @@ function createModulePathMap(sourceFile) {
|
|
|
8578
8686
|
if (ts.isExportAssignment(child)) {
|
|
8579
8687
|
const contentNode = child.expression && ts.isCallExpression(child.expression) && child.expression.arguments[2];
|
|
8580
8688
|
if (contentNode) {
|
|
8581
|
-
|
|
8689
|
+
const map = traverse(contentNode, sourceFile) ?? {};
|
|
8690
|
+
// Always expose the module root under the "" key so diagnostics without
|
|
8691
|
+
// a module path still resolve to a location. `traverse` already emits ""
|
|
8692
|
+
// for primitive roots (with a tighter range), so keep that if present.
|
|
8693
|
+
// Object/record keys are never "" (see traverseObjectLiteral), so this
|
|
8694
|
+
// cannot collide with a real key.
|
|
8695
|
+
return {
|
|
8696
|
+
...map,
|
|
8697
|
+
"": map[""] ?? {
|
|
8698
|
+
children: {},
|
|
8699
|
+
start: sourceFile.getLineAndCharacterOfPosition(contentNode.getStart(sourceFile)),
|
|
8700
|
+
end: sourceFile.getLineAndCharacterOfPosition(contentNode.getEnd())
|
|
8701
|
+
}
|
|
8702
|
+
};
|
|
8582
8703
|
}
|
|
8583
8704
|
}
|
|
8584
8705
|
}
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
18
|
"types": "dist/valbuild-server.cjs.d.ts",
|
|
19
|
-
"version": "0.
|
|
19
|
+
"version": "0.99.1",
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@prettier/sync": "^0.6.1",
|
|
22
22
|
"@types/jest": "^30.0.0"
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"typescript": "^5.9.3",
|
|
30
30
|
"zod": "^4.3.5",
|
|
31
31
|
"zod-validation-error": "^5.0.0",
|
|
32
|
-
"@valbuild/core": "0.
|
|
33
|
-
"@valbuild/shared": "0.
|
|
34
|
-
"@valbuild/ui": "0.
|
|
32
|
+
"@valbuild/core": "0.99.1",
|
|
33
|
+
"@valbuild/shared": "0.99.1",
|
|
34
|
+
"@valbuild/ui": "0.99.1"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
37
|
"node": ">=18.17.0"
|