release-please
Version:
generate release PRs based on the conventionalcommits.org spec
126 lines • 5.17 kB
JavaScript
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.replaceTomlValue = exports.parseWith = void 0;
const TOMLParser = require("@iarna/toml/lib/toml-parser");
const taggedValueMarker = Symbol('__TAGGED_VALUE');
/**
* A custom variant of `TOMLParser` that replaces all values with a tagged
* variant that includes their start and end positions, allowing them to be
* replaced.
*/
class TaggedTOMLParser extends TOMLParser {
parseValue() {
// Remember the start position of the value.
//
// Off-by-one correctness: by this point, `this.pos` points one character
// *after* the first character of the value, which is in `this.char`
this.state.__TAGGED_START = this.pos - 1;
return super.parseValue();
}
next(fn) {
const prevState = this.state;
super.next(fn); // `next` returns void
// Carry over the start position. If it wasn't set, (say, if we were parsing
// something other than a value), we're just assigning `undefined` here.
this.state.__TAGGED_START = prevState.__TAGGED_START;
}
return(value) {
const prevState = this.state;
super.return(value); // `return` returns void
if (prevState.__TAGGED_START && typeof this.state.returned !== 'object') {
// If the parser we just returned from remembered a start position,
// tag the returned value with "start" and "end".
// Note that we don't tag objects to avoid encountering multiple tagged
// values when replacing later on.
const taggedValue = {
[taggedValueMarker]: true,
start: prevState.__TAGGED_START,
end: this.pos,
value: this.state.returned,
};
this.state.returned = taggedValue;
}
}
}
/**
* Parses input as TOML with the given parser
* @param input A string
* @param parserType The TOML parser to use (might be custom)
*/
function parseWith(input, parserType = TaggedTOMLParser) {
const parser = new parserType();
parser.parse(input);
return parser.finish();
}
exports.parseWith = parseWith;
function isTaggedValue(x) {
if (!x) {
return false;
}
if (typeof x !== 'object') {
return false;
}
const ts = x;
return ts[taggedValueMarker] === true;
}
/**
* Given TOML input and a path to a value, attempt to replace
* that value without modifying the formatting.
* @param input A string that's valid TOML
* @param path Path to a value to replace. When replacing 'deps.tokio.version', pass ['deps', 'tokio', 'version']. The value must already exist.
* @param newValue The value to replace the value at `path` with. Is passed through `JSON.stringify()` when replacing: strings will end up being double-quoted strings, properly escaped. Numbers will be numbers.
*/
function replaceTomlValue(input, path, newValue) {
// our pointer into the object "tree", initially points to the root.
let current = parseWith(input, TaggedTOMLParser);
// navigate down the object tree, following the path, expecting only objects.
// Note that tagged strings (generated by `TaggedTOMLParser`) are also objects.
for (let i = 0; i < path.length; i++) {
const key = path[i];
// // We may encounter tagged values when descending through the object tree
// if (isTaggedValue(current)) {
// if (!current.value || typeof current.value !== 'object') {
// const msg = `partial path does not lead to table: ${path
// .slice(0, i)
// .join('.')}`;
// throw new Error(msg);
// }
// current = current.value as Record<string, unknown>;
// }
const next = current[key];
if (typeof next !== 'object') {
const msg = `path not found in object: ${path.slice(0, i + 1).join('.')}`;
throw new Error(msg);
}
current = next;
}
if (!isTaggedValue(current)) {
const msg = `value at path ${path.join('.')} is not tagged`;
throw new Error(msg);
}
const before = input.slice(0, current.start);
const after = input.slice(current.end);
const output = before + JSON.stringify(newValue) + after;
try {
parseWith(output, TOMLParser);
}
catch (e) {
throw new Error(`After replacing value, result is not valid TOML: ${e}`);
}
return output;
}
exports.replaceTomlValue = replaceTomlValue;
//# sourceMappingURL=toml-edit.js.map
;