Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
014a482984 | ||
|
|
51799f7dc1 | ||
|
|
c36d2a7ba9 | ||
|
|
b79ac30d4e | ||
|
|
4cd8897c6b | ||
|
|
86593de7d0 | ||
|
|
78f68bf904 | ||
|
|
c7b0fd03b0 | ||
|
|
8d56792b37 | ||
|
|
8c1c95284e | ||
|
|
0bdbfe1078 | ||
|
|
c40672c458 | ||
|
|
64a20ee5b1 | ||
|
|
a469a65101 | ||
|
|
65c71be17e | ||
|
|
b9e3641c85 | ||
|
|
6ea7450536 | ||
|
|
692d01f165 | ||
|
|
84c27739ec | ||
|
|
c4c6e8abd8 | ||
|
|
e7357e4ff1 | ||
|
|
a673d33e89 | ||
|
|
4818e05d2d | ||
|
|
07f32b9a7d | ||
|
|
60d18f8d5c |
63
CHANGELOG.md
63
CHANGELOG.md
@@ -1,3 +1,66 @@
|
||||
## [5.3.4](https://github.com/wickedest/Mergely/compare/v5.3.3...v5.3.4) (2024-10-11)
|
||||
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "build(deps-dev): bump webpack from 5.91.0 to 5.94.0 (#203)" ([c36d2a7](https://github.com/wickedest/Mergely/commit/c36d2a7ba9ca895844126711aa00b9634814e1dc)), closes [#203](https://github.com/wickedest/Mergely/issues/203)
|
||||
|
||||
## [5.3.3](https://github.com/wickedest/Mergely/compare/v5.3.2...v5.3.3) (2024-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **#209:** Fixes issue with options not being preserved when individually set. ([#211](https://github.com/wickedest/Mergely/issues/211)) ([4cd8897](https://github.com/wickedest/Mergely/commit/4cd8897c6b157d506560bb09f508f9debe2a4211))
|
||||
|
||||
## [5.3.2](https://github.com/wickedest/Mergely/compare/v5.3.1...v5.3.2) (2024-07-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Removed debug code ([c7b0fd0](https://github.com/wickedest/Mergely/commit/c7b0fd03b01cb3267d292c2985ca8b62adda11e0))
|
||||
|
||||
## [5.3.1](https://github.com/wickedest/Mergely/compare/v5.3.0...v5.3.1) (2024-06-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed a regression rendering inline markup for numbers, punctuation, and symbols ([#199](https://github.com/wickedest/Mergely/issues/199)) ([c40672c](https://github.com/wickedest/Mergely/commit/c40672c458723bdd0d9a0062ef1457e435866765))
|
||||
|
||||
# [5.3.0](https://github.com/wickedest/Mergely/compare/v5.2.0...v5.3.0) (2024-06-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Supports unicode diacritical marks when rendering line diff (fixes [#169](https://github.com/wickedest/Mergely/issues/169)) ([#197](https://github.com/wickedest/Mergely/issues/197)) ([a469a65](https://github.com/wickedest/Mergely/commit/a469a6510122356a7cae3fb1259e999e6cc34c94))
|
||||
|
||||
# [5.2.0](https://github.com/wickedest/Mergely/compare/v5.1.4...v5.2.0) (2024-06-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Allows height to be not explicit height, e.g. 'inherit' or '100%' ([#196](https://github.com/wickedest/Mergely/issues/196)) ([b9e3641](https://github.com/wickedest/Mergely/commit/b9e3641c852a8926db5efdf33e65a607d5f2df5e))
|
||||
|
||||
## [5.1.4](https://github.com/wickedest/Mergely/compare/v5.1.3...v5.1.4) (2024-05-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* removed accidental change that enabled code display modes (e.g. javascript, python, html) ([692d01f](https://github.com/wickedest/Mergely/commit/692d01f1653ae8f1163a2c6228f457549086b75d))
|
||||
|
||||
## [5.1.3](https://github.com/wickedest/Mergely/compare/v5.1.2...v5.1.3) (2024-05-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **chore:** updated deps ([#195](https://github.com/wickedest/Mergely/issues/195)) ([c4c6e8a](https://github.com/wickedest/Mergely/commit/c4c6e8abd8f02762d5803774789673f76a95e932))
|
||||
|
||||
## [5.1.2](https://github.com/wickedest/Mergely/compare/v5.1.1...v5.1.2) (2024-05-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* trace only when debug enabled ([#194](https://github.com/wickedest/Mergely/issues/194)) ([60d18f8](https://github.com/wickedest/Mergely/commit/60d18f8d5c0df349b4806b2e8a6c0f79d9f8074e))
|
||||
|
||||
## [5.1.1](https://github.com/wickedest/Mergely/compare/v5.1.0...v5.1.1) (2024-03-11)
|
||||
|
||||
|
||||
|
||||
9203
package-lock.json
generated
9203
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mergely",
|
||||
"version": "5.1.1",
|
||||
"version": "5.3.4",
|
||||
"description": "A javascript UI for diff/merge",
|
||||
"license": "(GPL-3.0 OR LGPL-3.0 OR MPL-1.1 OR SEE LICENSE IN LICENSE)",
|
||||
"author": {
|
||||
@@ -57,7 +57,7 @@
|
||||
"karma-sourcemap-loader": "^0.4.0",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"mocha": "^9.1.4",
|
||||
"semantic-release": "^21.0.1",
|
||||
"semantic-release": "^21.1.2",
|
||||
"simple-mock": "^0.8.0",
|
||||
"standard-version": "^9.3.2",
|
||||
"style-loader": "^3.3.1",
|
||||
|
||||
185
src/diff-view.js
185
src/diff-view.js
@@ -2,11 +2,6 @@ const CodeMirror = require('codemirror');
|
||||
require('codemirror/addon/search/searchcursor.js');
|
||||
require('codemirror/addon/selection/mark-selection.js');
|
||||
require('codemirror/lib/codemirror.css');
|
||||
require('codemirror/mode/go/go.js');
|
||||
require('codemirror/mode/javascript/javascript.js');
|
||||
require('codemirror/mode/htmlmixed/htmlmixed.js');
|
||||
require('codemirror/mode/markdown/markdown.js');
|
||||
require('codemirror/mode/python/python.js');
|
||||
|
||||
const dom = require('./dom.js');
|
||||
const VDoc = require('./vdoc');
|
||||
@@ -58,9 +53,7 @@ CodeMirrorDiffView.prototype.unbind = function() {
|
||||
if (this._unbound) {
|
||||
return;
|
||||
}
|
||||
if (this.settings._debug) {
|
||||
trace('api#unbind');
|
||||
}
|
||||
this.trace('api#unbind');
|
||||
if (this._changedTimeout != null) {
|
||||
clearTimeout(this._changedTimeout);
|
||||
}
|
||||
@@ -72,7 +65,6 @@ CodeMirrorDiffView.prototype.unbind = function() {
|
||||
this.el.removeChild(this.el.lastChild);
|
||||
}
|
||||
if (this._origEl) {
|
||||
this.el.style = this._origEl.style;
|
||||
this.el.className = this._origEl.className;
|
||||
}
|
||||
this._unbound = true;
|
||||
@@ -89,9 +81,7 @@ CodeMirrorDiffView.prototype.readOnly = function(side) {
|
||||
}
|
||||
|
||||
CodeMirrorDiffView.prototype.lhs = function(text) {
|
||||
if (this.settings._debug) {
|
||||
trace('api#lhs', text && text.length);
|
||||
}
|
||||
this.trace('api#lhs', text && text.length);
|
||||
// invalidate existing changes and current position
|
||||
this.changes = [];
|
||||
this._current_diff = -1;
|
||||
@@ -100,33 +90,25 @@ CodeMirrorDiffView.prototype.lhs = function(text) {
|
||||
|
||||
CodeMirrorDiffView.prototype.rhs = function(text) {
|
||||
// invalidate existing changes and current position
|
||||
if (this.settings._debug) {
|
||||
trace('api#rhs', text && text.length);
|
||||
}
|
||||
this.trace('api#rhs', text && text.length);
|
||||
this.changes = [];
|
||||
this._current_diff = -1;
|
||||
this.editor.rhs.setValue(text);
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.update = function() {
|
||||
if (this.settings._debug) {
|
||||
trace('api#update');
|
||||
}
|
||||
this.trace('api#update');
|
||||
this.el.dispatchEvent(new Event('changed'));
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.unmarkup = function() {
|
||||
if (this.settings._debug) {
|
||||
trace('api#unmarkup');
|
||||
}
|
||||
this.trace('api#unmarkup');
|
||||
this._clear();
|
||||
this.el.dispatchEvent(new Event('updated'));
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.scrollToDiff = function(direction) {
|
||||
if (this.settings._debug) {
|
||||
trace('api#scrollToDiff', direction);
|
||||
}
|
||||
this.trace('api#scrollToDiff', direction);
|
||||
if (!this.changes.length) return;
|
||||
if (direction === 'next') {
|
||||
if (this._current_diff === this.changes.length - 1
|
||||
@@ -143,18 +125,14 @@ CodeMirrorDiffView.prototype.scrollToDiff = function(direction) {
|
||||
this._current_diff = Math.max(--this._current_diff, 0);
|
||||
}
|
||||
}
|
||||
if (this.settings._debug) {
|
||||
trace('change', 'current-diff', this._current_diff);
|
||||
}
|
||||
this.trace('change', 'current-diff', this._current_diff);
|
||||
// _current_diff changed, refresh the view
|
||||
this._scroll_to_change(this.changes[this._current_diff]);
|
||||
this.setChanges(this.changes);
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.mergeCurrentChange = function(side) {
|
||||
if (this.settings._debug) {
|
||||
trace('api#mergeCurrentChange', side);
|
||||
}
|
||||
this.trace('api#mergeCurrentChange', side);
|
||||
if (!this.changes.length) return;
|
||||
if (side == 'lhs' && !this.lhs_cmsettings.readOnly) {
|
||||
this._merge_change(this.changes[this._current_diff], 'rhs', 'lhs');
|
||||
@@ -165,9 +143,7 @@ CodeMirrorDiffView.prototype.mergeCurrentChange = function(side) {
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.scrollTo = function(side, num) {
|
||||
if (this.settings._debug) {
|
||||
trace('api#scrollTo', side, num);
|
||||
}
|
||||
this.trace('api#scrollTo', side, num);
|
||||
const ed = this.editor[side];
|
||||
ed.setCursor(num);
|
||||
ed.centerOnCursor();
|
||||
@@ -181,9 +157,7 @@ CodeMirrorDiffView.prototype.setOptions = function(opts) {
|
||||
...this.settings,
|
||||
...opts
|
||||
};
|
||||
if (this.settings._debug) {
|
||||
trace('api#setOptions', opts);
|
||||
}
|
||||
this.trace('api#setOptions', opts);
|
||||
|
||||
// if options set after init
|
||||
if (this.editor) {
|
||||
@@ -216,9 +190,7 @@ CodeMirrorDiffView.prototype.setOptions = function(opts) {
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.get = function(side) {
|
||||
if (this.settings._debug) {
|
||||
trace('api#get', side);
|
||||
}
|
||||
this.trace('api#get', side);
|
||||
const ed = this.editor[side];
|
||||
const value = ed.getValue();
|
||||
if (value === undefined) {
|
||||
@@ -228,16 +200,12 @@ CodeMirrorDiffView.prototype.get = function(side) {
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.cm = function(side) {
|
||||
if (this.settings._debug) {
|
||||
trace('api#cm', 'side');
|
||||
}
|
||||
this.trace('api#cm', 'side');
|
||||
return this.editor[side];
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.search = function(side, query, direction) {
|
||||
if (this.settings._debug) {
|
||||
trace('api#search', side, query, direction);
|
||||
}
|
||||
this.trace('api#search', side, query, direction);
|
||||
const editor = this.editor[side];
|
||||
if (!editor.getSearchCursor) {
|
||||
throw new Error('install CodeMirror search addon');
|
||||
@@ -264,9 +232,7 @@ CodeMirrorDiffView.prototype.search = function(side, query, direction) {
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.resize = function() {
|
||||
if (this.settings._debug) {
|
||||
trace('api#resize');
|
||||
}
|
||||
this.trace('api#resize');
|
||||
const parent = this.el;
|
||||
const contentHeight = parent.offsetHeight - 2;
|
||||
|
||||
@@ -289,19 +255,18 @@ CodeMirrorDiffView.prototype.resize = function() {
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.bind = function(container) {
|
||||
if (this.settings._debug) {
|
||||
trace('api#bind', container);
|
||||
}
|
||||
this._origEl = {
|
||||
style: container.style,
|
||||
className: container.className
|
||||
};
|
||||
this.trace('api#bind', container);
|
||||
const el = dom.getMergelyContainer({ clazz: container.className });
|
||||
const computedStyle = window.getComputedStyle(container);
|
||||
if (!computedStyle.height || computedStyle.height === '0px') {
|
||||
if (!el.style.height
|
||||
&& (!computedStyle.height || computedStyle.height === '0px')
|
||||
) {
|
||||
throw new Error(
|
||||
`The element "${container.id}" requires an explicit height`);
|
||||
}
|
||||
this._origEl = {
|
||||
className: container.className
|
||||
};
|
||||
this.id = `${container.id}`;
|
||||
this.lhsId = `${container.id}-lhs`;
|
||||
this.rhsId = `${container.id}-rhs`;
|
||||
@@ -413,9 +378,7 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
||||
}
|
||||
});
|
||||
this.editor.rhs.on('beforeChange', (cm, ev) => {
|
||||
if (this.settings._debug) {
|
||||
trace('event#rhs-beforeChange', ev);
|
||||
}
|
||||
this.trace('event#rhs-beforeChange', ev);
|
||||
if (ev.text.length > 1
|
||||
|| ((ev.from.line - ev.to.line) && ev.origin === '+delete')) {
|
||||
this._clear();
|
||||
@@ -423,24 +386,16 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
||||
});
|
||||
|
||||
this.editor.lhs.on('change', (instance, ev) => {
|
||||
if (this.settings._debug) {
|
||||
trace('event#lhs-change');
|
||||
}
|
||||
this.trace('event#lhs-change');
|
||||
this._changing();
|
||||
if (this.settings._debug) {
|
||||
trace('event#lhs-change [emitted]');
|
||||
}
|
||||
this.trace('event#lhs-change [emitted]');
|
||||
});
|
||||
this.editor.lhs.on('scroll', () => {
|
||||
if (this._skipscroll.lhs) {
|
||||
if (this.settings._debug) {
|
||||
trace('event#lhs-scroll (skipped)');
|
||||
}
|
||||
this.trace('event#lhs-scroll (skipped)');
|
||||
return;
|
||||
} else {
|
||||
if (this.settings._debug) {
|
||||
trace('event#lhs-scroll');
|
||||
}
|
||||
this.trace('event#lhs-scroll');
|
||||
}
|
||||
// firefox scroll-linked effect render issue
|
||||
setTimeout(() => {
|
||||
@@ -448,21 +403,15 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
||||
}, 1);
|
||||
});
|
||||
this.editor.rhs.on('change', (instance, ev) => {
|
||||
if (this.settings._debug) {
|
||||
trace('event#rhs-change', ev);
|
||||
}
|
||||
this.trace('event#rhs-change', ev);
|
||||
this._changing();
|
||||
});
|
||||
this.editor.rhs.on('scroll', () => {
|
||||
if (this._skipscroll.rhs) {
|
||||
if (this.settings._debug) {
|
||||
trace('event#rhs-scroll (skipped)');
|
||||
}
|
||||
this.trace('event#rhs-scroll (skipped)');
|
||||
return;
|
||||
} else {
|
||||
if (this.settings._debug) {
|
||||
trace('event#rhs-scroll');
|
||||
}
|
||||
this.trace('event#rhs-scroll');
|
||||
}
|
||||
// firefox scroll-linked effect render issue
|
||||
setTimeout(() => {
|
||||
@@ -475,7 +424,7 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
||||
const resize = () => {
|
||||
if (this.settings._debug) {
|
||||
traceTimeStart('event#resize');
|
||||
trace('event#resize [start]');
|
||||
this.trace('event#resize [start]');
|
||||
}
|
||||
this.resize();
|
||||
if (this.settings._debug) {
|
||||
@@ -500,7 +449,6 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
||||
ev.preventDefault();
|
||||
return;
|
||||
}
|
||||
const ed = this.editor[side];
|
||||
// See if the user clicked the line number of a difference:
|
||||
let found = false;
|
||||
for (let i = 0; i < this.changes.length; ++i) {
|
||||
@@ -532,16 +480,12 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
||||
}
|
||||
|
||||
this.editor.lhs.on('gutterClick', (cm, n, gutterClass, ev) => {
|
||||
if (this.settings._debug) {
|
||||
trace('event#gutterClick', 'lhs', n, ev);
|
||||
}
|
||||
this.trace('event#gutterClick', 'lhs', n, ev);
|
||||
gutterClicked.call(this, 'lhs', n, ev);
|
||||
});
|
||||
|
||||
this.editor.rhs.on('gutterClick', (cm, n, gutterClass, ev) => {
|
||||
if (this.settings._debug) {
|
||||
trace('event#gutterClick', 'rhs', n, ev);
|
||||
}
|
||||
this.trace('event#gutterClick', 'rhs', n, ev);
|
||||
gutterClicked.call(this, 'rhs', n, ev);
|
||||
});
|
||||
|
||||
@@ -689,18 +633,14 @@ CodeMirrorDiffView.prototype._scrolling = function({ side }) {
|
||||
const vp = this.editor[oside].getViewport();
|
||||
let scroll = true;
|
||||
if (last_change) {
|
||||
if (this.settings._debug) {
|
||||
trace('scroll#_scrolling', 'last change before midline', last_change);
|
||||
}
|
||||
this.trace('scroll#_scrolling', 'last change before midline', last_change);
|
||||
if (midline.line >= vp.from && midline <= vp.to) {
|
||||
scroll = false;
|
||||
}
|
||||
}
|
||||
if (scroll || force_scroll) {
|
||||
// scroll the other side
|
||||
if (this.settings._debug) {
|
||||
trace('scroll#_scrolling', 'other side', oside, 'pos:', top_to - top_adjust);
|
||||
}
|
||||
this.trace('scroll#_scrolling', 'other side', oside, 'pos:', top_to - top_adjust);
|
||||
|
||||
// disable linked scroll events for the opposite editor because this
|
||||
// triggers the next one explicitly, and we don't want to link the
|
||||
@@ -708,21 +648,21 @@ CodeMirrorDiffView.prototype._scrolling = function({ side }) {
|
||||
// coming in 2s, so this will "link" scrolling the other editor to
|
||||
// this editor until this editor stops scrolling and times out.
|
||||
this._skipscroll[oside] = true;
|
||||
trace('scroll#set oside skip set:', oside, this._skipscroll);
|
||||
this.trace('scroll#set oside skip set:', oside, this._skipscroll);
|
||||
if (this._linkedScrollTimeout[oside]) {
|
||||
clearTimeout(this._linkedScrollTimeout[oside]);
|
||||
trace('scroll#clearing timeout:', this._skipscroll);
|
||||
this.trace('scroll#clearing timeout:', this._skipscroll);
|
||||
}
|
||||
this._linkedScrollTimeout[oside] = setTimeout(() => {
|
||||
this._skipscroll[oside] = false;
|
||||
trace('scroll#set oside skip unset:', oside, this._skipscroll);
|
||||
this.trace('scroll#set oside skip unset:', oside, this._skipscroll);
|
||||
}, 100);
|
||||
|
||||
const top = top_to - top_adjust;
|
||||
// scroll the opposite editor
|
||||
this.editor[oside].scrollTo(left_to, top);
|
||||
} else if (this.settings._debug) {
|
||||
trace('scroll#_scrolling', 'not scrolling other side');
|
||||
} else {
|
||||
this.trace('scroll#_scrolling', 'not scrolling other side');
|
||||
}
|
||||
this._renderChanges();
|
||||
|
||||
@@ -733,23 +673,19 @@ CodeMirrorDiffView.prototype._scrolling = function({ side }) {
|
||||
|
||||
CodeMirrorDiffView.prototype._changing = function() {
|
||||
if (!this.settings.autoupdate) {
|
||||
if (this.settings._debug) {
|
||||
trace('change#_changing autoupdate is disabled');
|
||||
}
|
||||
this.trace('change#_changing autoupdate is disabled');
|
||||
return;
|
||||
}
|
||||
if (this.settings._debug) {
|
||||
traceTimeStart('change#_changing');
|
||||
trace('change#_changing [start]');
|
||||
this.trace('change#_changing [start]');
|
||||
}
|
||||
const handleChange = () => {
|
||||
this._changedTimeout = null;
|
||||
this.el.dispatchEvent(new Event('changed'));
|
||||
};
|
||||
if (this.settings.change_timeout > 0) {
|
||||
if (this.settings._debug) {
|
||||
trace('change#setting timeout', this.settings.change_timeout)
|
||||
}
|
||||
this.trace('change#setting timeout', this.settings.change_timeout)
|
||||
if (this._changedTimeout != null) {
|
||||
clearTimeout(this._changedTimeout);
|
||||
}
|
||||
@@ -763,9 +699,7 @@ CodeMirrorDiffView.prototype._changing = function() {
|
||||
};
|
||||
|
||||
CodeMirrorDiffView.prototype.setChanges = function(changes) {
|
||||
if (this.settings._debug) {
|
||||
trace('change#setChanges');
|
||||
}
|
||||
this.trace('change#setChanges');
|
||||
this._clear();
|
||||
// after clear, set the new changes
|
||||
this.changes = changes;
|
||||
@@ -775,7 +709,7 @@ CodeMirrorDiffView.prototype.setChanges = function(changes) {
|
||||
CodeMirrorDiffView.prototype._renderChanges = function() {
|
||||
if (this.settings._debug) {
|
||||
traceTimeStart('draw#_renderChanges');
|
||||
trace('draw#_renderChanges [start]', this.changes.length, 'changes');
|
||||
this.trace('draw#_renderChanges [start]', this.changes.length, 'changes');
|
||||
}
|
||||
this._clearCanvases();
|
||||
this._calculateOffsets(this.changes);
|
||||
@@ -818,6 +752,9 @@ CodeMirrorDiffView.prototype._set_top_offset = function (side) {
|
||||
// this is the distance from the top of the screen to the top of the
|
||||
// content of the first codemirror editor
|
||||
const topnode = this._queryElement('.CodeMirror-measure');
|
||||
if (!topnode.offsetParent) {
|
||||
return false;
|
||||
}
|
||||
const top_offset = topnode.offsetParent.offsetTop + 4;
|
||||
|
||||
// restore editor's scroll position
|
||||
@@ -1103,12 +1040,9 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) {
|
||||
const ctx_lhs = mcanvas_lhs.getContext('2d');
|
||||
const ctx_rhs = mcanvas_rhs.getContext('2d');
|
||||
|
||||
if (this.settings._debug
|
||||
&& this.settings._debug) {
|
||||
trace('draw#_renderDiff', 'visible page height', ex.visible_page_height);
|
||||
trace('draw#_renderDiff', 'scroller-top lhs', ex.lhs_scroller.scrollTop);
|
||||
trace('draw#_renderDiff', 'scroller-top rhs', ex.rhs_scroller.scrollTop);
|
||||
}
|
||||
this.trace('draw#_renderDiff', 'visible page height', ex.visible_page_height);
|
||||
this.trace('draw#_renderDiff', 'scroller-top lhs', ex.lhs_scroller.scrollTop);
|
||||
this.trace('draw#_renderDiff', 'scroller-top rhs', ex.rhs_scroller.scrollTop);
|
||||
|
||||
ex.lhs_margin.removeEventListener('click', this._handleLhsMarginClick);
|
||||
ex.rhs_margin.removeEventListener('click', this._handleRhsMarginClick);
|
||||
@@ -1133,17 +1067,16 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) {
|
||||
const rhs_y_end = change['rhs-y-end'] - rhsScrollTop;
|
||||
|
||||
if (Number.isNaN(lhs_y_start)) {
|
||||
trace('draw#_renderDiff', 'unexpected NaN',
|
||||
change['lhs-y-start'], change['lhs-y-end']);
|
||||
this.trace(
|
||||
'draw#_renderDiff unexpected NaN',
|
||||
change['lhs-y-start'], change['lhs-y-end']
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// draw margin indicators
|
||||
if (this.settings._debug
|
||||
&& this.settings._debug) {
|
||||
trace('draw#_renderDiff', 'draw1', 'marker',
|
||||
lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end);
|
||||
}
|
||||
this.trace('draw#_renderDiff', 'draw1', 'marker',
|
||||
lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end);
|
||||
|
||||
const mkr_lhs_y_start = change['lhs-y-start'] * lratio;
|
||||
const mkr_lhs_y_end = Math.max(change['lhs-y-end'] * lratio, 5);
|
||||
@@ -1272,4 +1205,10 @@ CodeMirrorDiffView.prototype._queryElement = function(selector) {
|
||||
return this[cacheName];
|
||||
}
|
||||
|
||||
CodeMirrorDiffView.prototype.trace = function(...args) {
|
||||
if (this.settings._debug) {
|
||||
console.log(...args);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CodeMirrorDiffView;
|
||||
|
||||
@@ -241,10 +241,13 @@ function CodeifyText(lhs, rhs, options) {
|
||||
this.ctxs = {};
|
||||
this.options = options;
|
||||
this.options.split = this.options.split || 'lines';
|
||||
const exp = /\p{Letter}\p{Mark}*|\p{Number}\p{Mark}*|\p{Punctuation}\p{Mark}*|\p{Symbol}\p{Mark}*|\p{White_Space}/gu;
|
||||
|
||||
if (typeof lhs === 'string') {
|
||||
if (this.options.split === 'chars') {
|
||||
this.lhs = lhs.split('');
|
||||
// split characters and include their diacritical marks
|
||||
this.lhs = lhs.match(exp) || [];
|
||||
// this.lhs = [...lhs];
|
||||
} else if (this.options.split === 'words') {
|
||||
this.lhs = lhs.split(/\s/);
|
||||
} else if (this.options.split === 'lines') {
|
||||
@@ -255,7 +258,9 @@ function CodeifyText(lhs, rhs, options) {
|
||||
}
|
||||
if (typeof rhs === 'string') {
|
||||
if (this.options.split === 'chars') {
|
||||
this.rhs = rhs.split('');
|
||||
// split characters and include their diacritical marks
|
||||
this.rhs = rhs.match(exp) || [];
|
||||
// this.rhs = [...rhs];
|
||||
} else if (this.options.split === 'words') {
|
||||
this.rhs = rhs.split(/\s/);
|
||||
} else if (this.options.split === 'lines') {
|
||||
|
||||
@@ -46,7 +46,9 @@ class Mergely {
|
||||
}
|
||||
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
if (!computedStyle.height || computedStyle.height === '0px') {
|
||||
if (!element.style.height
|
||||
&& (!computedStyle.height || computedStyle.height === '0px')
|
||||
) {
|
||||
throw new Error(
|
||||
`The element "${selector}" requires an explicit height`);
|
||||
}
|
||||
@@ -120,13 +122,11 @@ class Mergely {
|
||||
const colors = dom.getColors(this.el);
|
||||
this._options = {
|
||||
...defaultOptions,//lgpl
|
||||
...this._initOptions,
|
||||
...(this._options || this._initOptions),
|
||||
...options//lgpl-separate-notice
|
||||
};
|
||||
this._viewOptions = {
|
||||
...defaultOptions,
|
||||
...this._initOptions,
|
||||
...options,
|
||||
...this._options,
|
||||
_colors: colors
|
||||
};
|
||||
}
|
||||
|
||||
22
src/vdoc.js
22
src/vdoc.js
@@ -2,6 +2,10 @@ const diff = require('./diff');
|
||||
|
||||
const trace = console.log;
|
||||
|
||||
const expLetters = new RegExp(
|
||||
/\p{Letter}\p{Mark}*|\p{Number}\p{Mark}*|\p{Punctuation}\p{Mark}*|\p{Symbol}\p{Mark}*|\p{White_Space}/gu
|
||||
);
|
||||
|
||||
class VDoc {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
@@ -275,15 +279,18 @@ class VLine {
|
||||
editor.setGutterMarker(this.id, name, item);
|
||||
}
|
||||
if (this.markup.length) {
|
||||
// while Mergely diffs unicode chars (letters+mark), CM is by character,
|
||||
// so diffs need to be mapped.
|
||||
const mapped = mapLettersToChars(editor.getValue());
|
||||
for (const markup of this.markup) {
|
||||
const [ charFrom, charTo, className ] = markup;
|
||||
const fromPos = { line: this.id };
|
||||
const toPos = { line: this.id };
|
||||
if (charFrom >= 0) {
|
||||
fromPos.ch = charFrom;
|
||||
fromPos.ch = mapped[charFrom];
|
||||
}
|
||||
if (charTo >= 0) {
|
||||
toPos.ch = charTo;
|
||||
toPos.ch = mapped[charTo];
|
||||
}
|
||||
this._clearMarkup.push(
|
||||
editor.markText(fromPos, toPos, { className }));
|
||||
@@ -334,4 +341,15 @@ function getExtents(side, change) {
|
||||
};
|
||||
}
|
||||
|
||||
function mapLettersToChars(text) {
|
||||
let match;
|
||||
let mapped = {};
|
||||
let index = 0;
|
||||
expLetters.lastIndex = 0;
|
||||
while ((match = expLetters.exec(text)) !== null) {
|
||||
mapped[index++] = match.index;
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
|
||||
module.exports = VDoc;
|
||||
|
||||
@@ -42,6 +42,7 @@ describe('markup', () => {
|
||||
|
||||
const LHS_CHANGE_START = '.mergely.lhs.c.CodeMirror-linebackground.start';
|
||||
const LHS_CHANGE_END = '.mergely.lhs.c.CodeMirror-linebackground.end';
|
||||
const LHS_CHANGE_START_AND_END = '.mergely.lhs.c.CodeMirror-linebackground.start.end';
|
||||
const RHS_CHANGE_START = '.mergely.rhs.c.CodeMirror-linebackground.start';
|
||||
const RHS_CHANGE_END = '.mergely.rhs.c.CodeMirror-linebackground.end';
|
||||
|
||||
@@ -233,7 +234,130 @@ describe('markup', () => {
|
||||
expect(rhs_spans[1].innerText).to.equal('h');
|
||||
expect(rhs_spans[2].innerText).to.equal('ir');
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'single word single diacritic non-spacing marks',
|
||||
lhs: 'كلمة',
|
||||
rhs: 'كَلمة',
|
||||
check: (editor) => {
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||
expect(lhs_spans).to.have.length(1);
|
||||
expect(lhs_spans[0].innerText).to.equal('ك');
|
||||
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||
expect(rhs_spans).to.have.length(1);
|
||||
expect(rhs_spans[0].innerText).to.equal('كَ');
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'single word multiple diacritic non-spacing marks',
|
||||
lhs: ['\u006E', '\u0061', '\u0314', '\u0065'].join(''), // na̔e
|
||||
rhs: ['\u006E', '\u0061', '\u0314', '\u034A', '\u0065'].join(''), // na̔͊e
|
||||
check: (editor) => {
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||
expect(lhs_spans).to.have.length(1);
|
||||
expect(lhs_spans[0].innerText).to.equal(['\u0061', '\u0314'].join(''));
|
||||
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||
expect(rhs_spans).to.have.length(1);
|
||||
expect(rhs_spans[0].innerText).to.equal('a̔͊');
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'multiple words diacritic non-spacing marks',
|
||||
lhs: 'كلمة اخرى',
|
||||
rhs: 'كْلمة اخرى',
|
||||
check: (editor) => {
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||
expect(lhs_spans).to.have.length(1);
|
||||
expect(lhs_spans[0].innerText).to.equal('ك');
|
||||
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||
expect(rhs_spans).to.have.length(1);
|
||||
expect(rhs_spans[0].innerText).to.equal('كْ');
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'nonnormalizable diacritic non-spacing marks',
|
||||
lhs: 'naeg',
|
||||
// there are 2 marks on 'e', tilde (0303) and x (0353)
|
||||
rhs: ['\u006E', '\u0061', '\u0353', '\u0065', '\u0353', '\u0303', '\u0067'].join(''),
|
||||
check: (editor) => {
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||
expect(lhs_spans).to.have.length(1);
|
||||
expect(lhs_spans[0].innerText).to.equal('ae');
|
||||
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||
expect(rhs_spans).to.have.length(1);
|
||||
expect(rhs_spans[0].innerText).to.equal(
|
||||
['\u0061', '\u0353', '\u0065', '\u0353', '\u0303'].join('')
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'nonnormalizable diacritic non-spacing marks',
|
||||
lhs: [
|
||||
'\u0065', '\u0353', '\u0303',
|
||||
'\u0065', '\u0353', '\u0303',
|
||||
'\u0065', '\u0353', '\u0303',
|
||||
'x',
|
||||
'\u0065', '\u0353', '\u0303',
|
||||
].join(''),
|
||||
// there are 2 marks on 'e', tilde (0303) and x (0353)
|
||||
rhs: [
|
||||
'\u0065', '\u0353', '\u0303',
|
||||
'\u0065', '\u0353', '\u0303',
|
||||
'\u0065', '\u0353', '\u0303',
|
||||
'y',
|
||||
'\u0065', '\u0353', '\u0303',
|
||||
].join(''),
|
||||
check: (editor) => {
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||
expect(lhs_spans).to.have.length(1);
|
||||
expect(lhs_spans[0].innerText).to.equal('x');
|
||||
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||
expect(rhs_spans).to.have.length(1);
|
||||
expect(rhs_spans[0].innerText).to.equal('y');
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Changes with non-letter chars',
|
||||
lhs: '~# 00 == ! (dog) \n',
|
||||
rhs: '~? 11 ++ ] (fox) .\n',
|
||||
check: (editor) => {
|
||||
expect(editor.querySelectorAll(LHS_CHANGE_START_AND_END + '.cid-0')).to.have.length(1);
|
||||
expect(editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0')).to.have.length(6);
|
||||
expect(editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0')).to.have.length(7);
|
||||
const lhs_changes = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||
const rhs_changes = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||
const lhs_values = [];
|
||||
for (const value of lhs_changes.values()) {
|
||||
lhs_values.push(value.innerText);
|
||||
}
|
||||
const rhs_values = [];
|
||||
for (const value of rhs_changes.values()) {
|
||||
rhs_values.push(value.innerText);
|
||||
}
|
||||
expect(lhs_values).to.deep.equal(['#', '00', '==', '!', 'd', 'g']);
|
||||
expect(rhs_values).to.deep.equal(['?', '11', '++', ']', 'f', 'x', '.']);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
// to debug, add `only: true` to the test `opts` above, and run `npm run debug`
|
||||
|
||||
@@ -62,19 +62,17 @@ describe('mergely', function () {
|
||||
try {
|
||||
const { children } = editor.el.children[0];
|
||||
const items = Array.from(children).map(a => a.className);
|
||||
expect(items[0] == 'mergely-margin');
|
||||
expect(items[1] == 'mergely-column');
|
||||
// NOTE: if running karma debug, these tests can fail because
|
||||
// the debugger grabs the focus and the CodeMirror instance
|
||||
// loses `CodeMirror-focused`
|
||||
expect(items).to.deep.equal([
|
||||
'mergely-margin',
|
||||
'mergely-column',
|
||||
'CodeMirror cm-s-default CodeMirror-focused',
|
||||
'mergely-canvas',
|
||||
'mergely-column',
|
||||
'CodeMirror cm-s-default',
|
||||
'mergely-margin',
|
||||
'mergely-splash'
|
||||
]);
|
||||
expect(items[2].indexOf('CodeMirror cm-s-default' == 0));
|
||||
expect(items[3] == 'mergely-margin');
|
||||
expect(items[4] == 'mergely-column');
|
||||
expect(items[5] == 'CodeMirror cm-s-default');
|
||||
expect(items[6] == 'mergely-column');
|
||||
expect(items[7] == 'mergely-splash');
|
||||
expect(editor.get('lhs')).to.equal('');
|
||||
expect(editor.get('rhs')).to.equal('');
|
||||
done();
|
||||
@@ -99,15 +97,17 @@ describe('mergely', function () {
|
||||
// NOTE: if running karma debug, these tests can fail because
|
||||
// the debugger grabs the focus and the CodeMirror instance
|
||||
// loses `CodeMirror-focused`
|
||||
expect(items).to.deep.equal([
|
||||
'mergely-margin',
|
||||
'mergely-column',
|
||||
'CodeMirror cm-s-default CodeMirror-focused',
|
||||
'mergely-canvas',
|
||||
'mergely-column',
|
||||
'CodeMirror cm-s-default',
|
||||
'mergely-margin'
|
||||
]);
|
||||
expect(items[0] == 'mergely-margin');
|
||||
expect(items[1] == 'mergely-column');
|
||||
// NOTE: if running karma debug, these tests can fail because
|
||||
// the debugger grabs the focus and the CodeMirror instance
|
||||
// loses `CodeMirror-focused`
|
||||
expect(items[2].indexOf('CodeMirror cm-s-default' == 0));
|
||||
expect(items[3] == 'mergely-margin');
|
||||
expect(items[4] == 'mergely-column');
|
||||
expect(items[5] == 'CodeMirror cm-s-default');
|
||||
expect(items[6] == 'mergely-column');
|
||||
expect(items[7] == 'mergely-splash');
|
||||
expect(editor.get('lhs')).to.equal('left-hand side text');
|
||||
expect(editor.get('rhs')).to.equal('right-hand side text');
|
||||
done();
|
||||
@@ -129,18 +129,17 @@ describe('mergely', function () {
|
||||
try {
|
||||
const { children } = editor.el.children[0];
|
||||
const items = Array.from(children).map(a => a.className);
|
||||
expect(items[0] == 'mergely-margin');
|
||||
expect(items[1] == 'mergely-column');
|
||||
// NOTE: if running karma debug, these tests can fail because
|
||||
// the debugger grabs the focus and the CodeMirror instance
|
||||
// loses `CodeMirror-focused`
|
||||
expect(items).to.deep.equal([
|
||||
'mergely-margin',
|
||||
'mergely-column',
|
||||
'CodeMirror cm-s-default CodeMirror-focused',
|
||||
'mergely-canvas',
|
||||
'mergely-column',
|
||||
'CodeMirror cm-s-default',
|
||||
'mergely-margin'
|
||||
]);
|
||||
expect(items[2].indexOf('CodeMirror cm-s-default' == 0));
|
||||
expect(items[3] == 'mergely-margin');
|
||||
expect(items[4] == 'mergely-column');
|
||||
expect(items[5] == 'CodeMirror cm-s-default');
|
||||
expect(items[6] == 'mergely-column');
|
||||
expect(items[7] == 'mergely-splash');
|
||||
expect(editor.get('lhs')).to.equal('left-hand side text');
|
||||
expect(editor.get('rhs')).to.equal('right-hand side text');
|
||||
done();
|
||||
@@ -161,18 +160,17 @@ describe('mergely', function () {
|
||||
try {
|
||||
const { children } = editor.el.children[0];
|
||||
const items = Array.from(children).map(a => a.className);
|
||||
expect(items[0] == 'mergely-margin');
|
||||
expect(items[1] == 'mergely-column');
|
||||
// NOTE: if running karma debug, these tests can fail because
|
||||
// the debugger grabs the focus and the CodeMirror instance
|
||||
// loses `CodeMirror-focused`
|
||||
expect(items).to.deep.equal([
|
||||
'mergely-margin',
|
||||
'mergely-column',
|
||||
'CodeMirror cm-s-default CodeMirror-focused',
|
||||
'mergely-canvas',
|
||||
'mergely-column',
|
||||
'CodeMirror cm-s-default',
|
||||
'mergely-margin'
|
||||
]);
|
||||
expect(items[2].indexOf('CodeMirror cm-s-default' == 0));
|
||||
expect(items[3] == 'mergely-margin');
|
||||
expect(items[4] == 'mergely-column');
|
||||
expect(items[5] == 'CodeMirror cm-s-default');
|
||||
expect(items[6] == 'mergely-column');
|
||||
expect(items[7] == 'mergely-splash');
|
||||
expect(children[0].style.visibility).to.equal('hidden');
|
||||
expect(children[6].style.visibility).to.equal('hidden');
|
||||
done();
|
||||
@@ -503,6 +501,24 @@ describe('mergely', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should preserve changed option', function (done) {
|
||||
const editor = init({
|
||||
change_timeout: 0,
|
||||
license: 'lgpl-separate-notice',
|
||||
lhs: 'left-hand side text',
|
||||
rhs: 'right-hand side text'
|
||||
});
|
||||
editor.once('updated', () => {
|
||||
editor.options({ sidebar: false });
|
||||
expect(editor.options().wrap_lines).to.equal(false, 'wrap_lines');
|
||||
expect(editor.options().sidebar).to.equal(false, 'sidebar');
|
||||
editor.options({ wrap_lines: false });
|
||||
expect(editor.options().wrap_lines).to.equal(false, 'wrap_lines-2');
|
||||
expect(editor.options().sidebar).to.equal(false, 'sidebar-2');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore white-space', function (done) {
|
||||
const editor = init({
|
||||
height: 100,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const path = require('path')
|
||||
const chalk = require('chalk');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
@@ -50,8 +51,8 @@ module.exports = {
|
||||
compiler.hooks.entryOption.tap('MyPlugin', (context, entry) => {
|
||||
console.log('-'.repeat(78));
|
||||
console.log('Applications:');
|
||||
console.log('http://localhost:8080/app.html');
|
||||
console.log('http://localhost:8080/app-styles.html');
|
||||
console.log(chalk.bold(chalk.underline(chalk.cyan('http://localhost:8080/app.html'))));
|
||||
console.log(chalk.bold(chalk.underline(chalk.cyan('http://localhost:8080/app-styles.html'))));
|
||||
console.log('-'.repeat(78));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ module.exports = (mode) => {
|
||||
...webpackDevConfig.output,
|
||||
path: path.join(__dirname, 'lib'),
|
||||
filename: './[name].js',
|
||||
library: {
|
||||
name: 'mergely',
|
||||
type: 'umd',
|
||||
}
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
|
||||
Reference in New Issue
Block a user