@coat/cli
Version:
TODO: See #3
88 lines (85 loc) • 3.05 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getLocationInJSONAst = getLocationInJSONAst;
var _types = require("@babel/types");
/**
* Adjusts a source location to correctly display the
* helper characters for @babel/codeframe in a JSON AST.
*
* @param location The source location property of the AST or node
* @returns The adjusted source location
*/
function shiftLocation(location) {
const singleLine = location.start.line === location.end.line;
return {
start: {
...location.start,
column: location.start.column + 1
},
end: {
...location.end,
column: singleLine ? location.end.column + 1 : location.end.column
}
};
}
/**
* Returns the source location of a property path inside a JSON AST.
*
* @param ast The JSON AST parsed by @babel/parser
* @param path The property path within the JSON object
* @returns The source location
*/
function getLocationInJSONAst(ast, path) {
const [head, ...tail] = path;
if (typeof head === "undefined") {
// The property path is empty, which means that the location
// should describe the whole JSON value
//
if (!ast.loc) {
throw new Error("AST does not have loc information.");
}
return shiftLocation(ast.loc);
} else if (typeof head === "string") {
if (!(0, _types.isObjectExpression)(ast)) {
throw new Error(`Expected ast root node to be ObjectExpression for current path: ${path.join(".")}`);
}
// Find the desired property within the AST
const desiredProperty = ast.properties.filter(prop => (0, _types.isObjectProperty)(prop))
// Reverse the AST to prefer property declarations that follow later,
// since JSON properties can be written multiple times and the
// most recent declaration overwrites previous ones.
.reverse().find(prop =>
// Typical JSON string literal, e.g.:
// { "a": 1 }
(0, _types.isStringLiteral)(prop.key) && prop.key.value === head ||
// JSON5 non-string identifier, e.g.:
// { a: 1 }
(0, _types.isIdentifier)(prop.key) && prop.key.name);
if (!desiredProperty) {
throw new Error(`Could not find property for current path: ${path.join(".")}`);
}
if (tail.length) {
return getLocationInJSONAst(desiredProperty.value, tail);
}
if (!desiredProperty.loc) {
throw new Error(`Property does not have loc information for current path: ${path.join(".")}`);
}
return shiftLocation(desiredProperty.loc);
}
if (!(0, _types.isArrayExpression)(ast)) {
throw new Error(`Expected ast root node to be ArrayExpression for current path: ${path.join(".")}`);
}
const element = ast.elements[head];
if (!element) {
throw new Error(`Array element is not defined for current path: ${path.join(".")}`);
}
if (tail.length) {
return getLocationInJSONAst(element, tail);
}
if (!element.loc) {
throw new Error(`Element does not have loc information for current path: ${path.join(".")}`);
}
return shiftLocation(element.loc);
}