UNPKG

@amaui/diff

Version:
255 lines (254 loc) 10.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.optionsDefault = void 0; const is_1 = __importDefault(require("@amaui/utils/is")); const merge_1 = __importDefault(require("@amaui/utils/merge")); const stringify_1 = __importDefault(require("@amaui/utils/stringify")); const OPTIONS = { default: { init: { method: (value) => String(value) }, itemize: { method: (value) => value.split('').filter(Boolean) }, join: { method: (value) => value.join('') }, equal: { method: (value, value1) => value === value1 }, }, word: { init: { method: (value) => String(value) }, itemize: { method: (value) => value.split(/\s|\b/).filter(Boolean) }, join: { method: (value) => value.join(' ') }, }, line: { init: { method: (value) => String(value) }, itemize: { method: (value) => { const values = value.split(/(\n|\r\n)/); const newValues = []; for (let i = 0; i < values.length; i++) { if (i % 2) newValues[newValues.length - 1] += values[i]; else newValues.push(values[i]); } return newValues; }, }, join: { method: (value) => value.join('') }, }, sentence: { init: { method: (value) => String(value) }, itemize: { method: (value) => value.split(/(\S.*?[.!?])(?=\s +|$)/).filter(Boolean) }, join: { method: (value) => value.join('') }, }, }; exports.optionsDefault = Object.assign(Object.assign({}, OPTIONS.default), { output: { compressed: true } }); class AmauiDiff { constructor(options = exports.optionsDefault) { this.options = (0, merge_1.default)(options, exports.optionsDefault); } static get amauidiff() { return new AmauiDiff(); } static get word() { return new AmauiDiff(AmauiDiff.OPTIONS.word); } static get line() { return new AmauiDiff(AmauiDiff.OPTIONS.line); } static get sentence() { return new AmauiDiff(AmauiDiff.OPTIONS.sentence); } static get json() { const options = AmauiDiff.OPTIONS.line; options.init.method = (value) => (0, stringify_1.default)(value); return new AmauiDiff(options); } static get OPTIONS() { return OPTIONS; } static updateGroups(diff) { const updateGroups = []; let previousGroup = []; let lastGroupIsRemove = false; const items = []; for (let i = 0; i < diff.items.length;) { if (['r', 'remove'].indexOf(diff.items[i]) > -1) { items.push(diff.items.slice(i, i + 2)); i += 2; } else { items.push(diff.items.slice(i, i + 3)); i += 3; } } diff.items = items; diff.items.forEach((item, index) => { const itemFollowing = diff.items[index + 1]; const action = item[0]; const actionFollow = itemFollowing && itemFollowing[0]; if (['r', 'remove'].indexOf(action) > -1 && ['r', 'remove'].indexOf(actionFollow) > -1 && !lastGroupIsRemove) { if (previousGroup.length) updateGroups.push(previousGroup); lastGroupIsRemove = true; previousGroup = []; } if (['a', 'add'].indexOf(action) > -1 && lastGroupIsRemove) { if (previousGroup.length) updateGroups.push(previousGroup); lastGroupIsRemove = false; previousGroup = []; } previousGroup.push(item); }); if (previousGroup.length) updateGroups.push(previousGroup); return updateGroups; } diff(value_, value1_, options_ = {}) { const options = (0, merge_1.default)(options_, this.options); const value = options.init.method(value_); const value1 = options.init.method(value1_); if ((0, is_1.default)('string', value) && (0, is_1.default)('string', value1)) { const valueArrayi = options.itemize.method(value1); const valueArrayj = options.itemize.method(value); const lengthi = valueArrayi.length; const lengthj = valueArrayj.length; const lengthMax = Math.max(lengthi, lengthj); const matrix = []; const lcs = []; const findItem = (item) => { if (item) { let [i, j] = item.slice(1); let left = (matrix[i] && matrix[i][j - 1]); let top = (matrix[i - 1] && matrix[i - 1][j]); let topLeft = (matrix[i - 1] && matrix[i - 1][j - 1]); if ((left && top) && (left[0] !== item[0] && top[0] !== item[0])) { // Enter the new upper left matrix part [i, j] = topLeft.slice(1); } let item_; while (!item_) { left = (matrix[i] && matrix[i][j - 1]); top = (matrix[i - 1] && matrix[i - 1][j]); topLeft = (matrix[i - 1] && matrix[i - 1][j - 1]); if (left && (left[0] === matrix[i][j][0])) { [i, j] = left.slice(1); } else if (top && (top[0] === matrix[i][j][0])) { [i, j] = top.slice(1); } else { item_ = matrix[i][j]; break; } } return item_; } }; const addItem = (i, j) => { const left = (matrix[i] && matrix[i][j - 1]); const top = (matrix[i - 1] && matrix[i - 1][j]); const topLeft = (matrix[i - 1] && matrix[i - 1][j - 1]); const match = options.equal.method(valueArrayi[i], valueArrayj[j]); let item = 0; if (match) item = (topLeft ? topLeft[0] : 0) + 1; else item = Math.max((top ? top[0] : 0), (left ? left[0] : 0)); const item_ = [item, i, j]; return item_; }; // Make a matrix for (let i = 0; i < lengthi; i++) { matrix.push([]); for (let j = 0; j < lengthj; j++) { matrix[i][j] = addItem(i, j); } } // Make a lcs const matrixItem = matrix[lengthi - 1][lengthj - 1]; if (matrixItem && matrixItem[0]) { let item = matrixItem; let i = lengthi - 1; let j = lengthj - 1; const left = (matrix[i] && matrix[i][j - 1]); const top = (matrix[i - 1] && matrix[i - 1][j]); const topLeft = (matrix[i - 1] && matrix[i - 1][j - 1]); if ((left && top) && (left[0] !== item[0] && top[0] !== item[0])) { lcs.push(item); // Enter the new upper left matrix part [i, j] = topLeft.slice(1); } while (item && (item[1] !== 0 && item[2] !== 0)) { item = findItem(item); lcs.push(item); } } // Sort based on value lcs lcs.sort((a, b) => a[0] - b[0]); // Make a diff items const diff = { items: [], }; for (let i = 0; i < lengthMax; i++) { const iItem = lcs.find(item => item[1] === i); const jItem = lcs.find(item => item[2] === i); if (!jItem && i < lengthj) diff.items.push(this.action('remove', i, undefined, options)); if (!iItem && i < lengthi) diff.items.push(this.action('add', i, valueArrayi[i], options)); } function sortMethod(a, b) { if (a[0] < b[0]) return 1; if (a[0] > b[0]) return -1; return 0; } diff.items.sort(sortMethod); diff.items = diff.items.flat(); return diff; } } update(value__, diff, options_ = {}) { var _a; const options = (0, merge_1.default)(options_, this.options); const value_ = options.init.method(value__); if ((0, is_1.default)('string', value_) && diff !== undefined) { const value = options.itemize.method(value_); if ((_a = diff.items) === null || _a === void 0 ? void 0 : _a.length) { const updateGroups = AmauiDiff.updateGroups(diff); updateGroups.forEach(group => { const removeGroup = group.every((item) => ['r', 'remove'].indexOf(item[0]) > -1); if (removeGroup) { group.sort((a, b) => b[1] - a[1]); group.forEach((item) => value.splice(item[1], 1)); } else { group.forEach((item) => { const action = item[0]; const index = item[1]; const valueItem = item[2]; switch (action) { case 'a': case 'add': value.splice(index, 0, valueItem); break; case 'r': case 'remove': value.splice(index, 1); break; default: break; } }); } }); } return options.join.method(value); } } action(action, index, value, options) { var _a; const compressed = (_a = options.output) === null || _a === void 0 ? void 0 : _a.compressed; return [compressed ? action.charAt(0) : action, index, value].filter(item => item !== undefined); } } exports.default = AmauiDiff;