@nx/devkit
Version:
110 lines (109 loc) • 3.31 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChangeType = void 0;
exports.applyChangesToString = applyChangesToString;
var ChangeType;
(function (ChangeType) {
ChangeType["Delete"] = "Delete";
ChangeType["Insert"] = "Insert";
})(ChangeType || (exports.ChangeType = ChangeType = {}));
/**
* Applies a list of changes to a string's original value.
*
* This is useful when working with ASTs.
*
* For Example, to rename a property in a method's options:
*
* ```typescript
* const code = `bootstrap({
* target: document.querySelector('#app')
* })`;
*
* const indexOfPropertyName = 13; // Usually determined by analyzing an AST.
* const updatedCode = applyChangesToString(code, [
* {
* type: ChangeType.Insert,
* index: indexOfPropertyName,
* text: 'element'
* },
* {
* type: ChangeType.Delete,
* start: indexOfPropertyName,
* length: 6
* },
* ]);
*
* bootstrap({
* element: document.querySelector('#app')
* });
* ```
*/
function applyChangesToString(text, changes) {
assertChangesValid(changes);
const sortedChanges = changes.sort((a, b) => {
const diff = getChangeIndex(a) - getChangeIndex(b);
if (diff === 0) {
if (a.type === b.type) {
return 0;
}
else {
// When at the same place, Insert before Delete
return isStringInsertion(a) ? -1 : 1;
}
}
return diff;
});
let offset = 0;
for (const change of sortedChanges) {
const index = getChangeIndex(change) + offset;
if (isStringInsertion(change)) {
text = text.slice(0, index) + change.text + text.slice(index);
offset += change.text.length;
}
else {
text = text.slice(0, index) + text.slice(index + change.length);
offset -= change.length;
}
}
return text;
}
function assertChangesValid(changes) {
for (const change of changes) {
if (isStringInsertion(change)) {
if (!Number.isInteger(change.index)) {
throw new TypeError(`${change.index} must be an integer.`);
}
if (change.index < 0) {
throw new Error(`${change.index} must be a positive integer.`);
}
if (typeof change.text !== 'string') {
throw new Error(`${change.text} must be a string.`);
}
}
else {
if (!Number.isInteger(change.start)) {
throw new TypeError(`${change.start} must be an integer.`);
}
if (change.start < 0) {
throw new Error(`${change.start} must be a positive integer.`);
}
if (!Number.isInteger(change.length)) {
throw new TypeError(`${change.length} must be an integer.`);
}
if (change.length < 0) {
throw new Error(`${change.length} must be a positive integer.`);
}
}
}
}
function getChangeIndex(change) {
if (isStringInsertion(change)) {
return change.index;
}
else {
return change.start;
}
}
function isStringInsertion(change) {
return change.type === ChangeType.Insert;
}
;