mirror of
https://github.com/transmission/transmission.git
synced 2025-12-20 02:18:42 +00:00
chore: bump web client deps (#1698)
* chore: bump web client dependencies
This commit is contained in:
@@ -119,7 +119,8 @@ module.exports = {
|
|||||||
"sonarjs/no-duplicate-string": "off",
|
"sonarjs/no-duplicate-string": "off",
|
||||||
"sort-keys": "error",
|
"sort-keys": "error",
|
||||||
"strict": "error",
|
"strict": "error",
|
||||||
'unicorn/consistent-function-scoping': 'off',
|
"unicorn/consistent-function-scoping": "off",
|
||||||
|
"unicorn/no-array-reduce": "off",
|
||||||
"unicorn/no-fn-reference-in-iterator": "off",
|
"unicorn/no-fn-reference-in-iterator": "off",
|
||||||
"unicorn/no-null": "off",
|
"unicorn/no-null": "off",
|
||||||
"unicorn/no-reduce": "off",
|
"unicorn/no-reduce": "off",
|
||||||
|
|||||||
@@ -18,36 +18,36 @@
|
|||||||
"lint:stylelint:fix": "stylelint --fix style/*scss"
|
"lint:stylelint:fix": "stylelint --fix style/*scss"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.11.6",
|
"@babel/core": "^7.14.3",
|
||||||
"@babel/eslint-parser": "^7.11.5",
|
"@babel/eslint-parser": "^7.14.3",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||||
"css-loader": "^4.3.0",
|
"css-loader": "^5.2.4",
|
||||||
"eslint": "^7.11.0",
|
"css-minimizer-webpack-plugin": "^3.0.0",
|
||||||
"eslint-plugin-sonarjs": "^0.5.0",
|
"eslint": "^7.26.0",
|
||||||
"eslint-plugin-unicorn": "^23.0.0",
|
"eslint-plugin-sonarjs": "^0.7.0",
|
||||||
"file-loader": "^6.1.0",
|
"eslint-plugin-unicorn": "^32.0.1",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
"img-optimize-loader": "^1.0.7",
|
"img-optimize-loader": "^1.0.7",
|
||||||
"mini-css-extract-plugin": "^0.11.3",
|
"mini-css-extract-plugin": "^1.6.0",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^6.0.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
"prettier": "^2.3.0",
|
||||||
"prettier": "^2.1.2",
|
"sass": "^1.32.13",
|
||||||
"sass": "^1.26.11",
|
"sass-loader": "^11.1.1",
|
||||||
"sass-loader": "^10.0.2",
|
"style-loader": "^2.0.0",
|
||||||
"style-loader": "^1.2.1",
|
"stylelint": "^13.13.1",
|
||||||
"stylelint": "^13.7.1",
|
|
||||||
"stylelint-config-prettier": "^8.0.2",
|
"stylelint-config-prettier": "^8.0.2",
|
||||||
"stylelint-config-primer": "^9.2.1",
|
"stylelint-config-primer": "^11.0.1",
|
||||||
"stylelint-config-sass-guidelines": "^7.1.0",
|
"stylelint-config-sass-guidelines": "^8.0.0",
|
||||||
"stylelint-config-standard": "^20.0.0",
|
"stylelint-config-standard": "^22.0.0",
|
||||||
"svgo": "^1.3.2",
|
"svgo": "^2.3.0",
|
||||||
"svgo-loader": "^2.2.1",
|
"svgo-loader": "^3.0.0",
|
||||||
"terser-webpack-plugin": "^4.2.2",
|
"terser-webpack-plugin": "^5.1.2",
|
||||||
"url-loader": "^4.1.0",
|
"url-loader": "^4.1.1",
|
||||||
"webpack": "^4.44.2",
|
"webpack": "^5.37.0",
|
||||||
"webpack-bundle-analyzer": "^3.9.0",
|
"webpack-bundle-analyzer": "^4.4.2",
|
||||||
"webpack-cli": "^3.3.12",
|
"webpack-cli": "^4.7.0",
|
||||||
"webpack-dev-server": "^3.11.0"
|
"webpack-dev-server": "^3.11.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash.isequal": "^4.5.0"
|
"lodash.isequal": "^4.5.0"
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -137,11 +137,12 @@ export class ActionManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static _recount(selected, nonselected) {
|
static _recount(selected, nonselected) {
|
||||||
const test = (tor) => tor.isStopped();
|
|
||||||
const total = selected.length + nonselected.length;
|
const total = selected.length + nonselected.length;
|
||||||
const selected_paused = selected.filter(test).length;
|
const selected_paused = selected.filter((tor) => tor.isStopped()).length;
|
||||||
const selected_active = selected.length - selected_paused;
|
const selected_active = selected.length - selected_paused;
|
||||||
const nonselected_paused = nonselected.filter(test).length;
|
const nonselected_paused = nonselected.filter((tor) =>
|
||||||
|
tor.isStopped()
|
||||||
|
).length;
|
||||||
const nonselected_active = nonselected.length - nonselected_paused;
|
const nonselected_active = nonselected.length - nonselected_paused;
|
||||||
const paused = selected_paused + nonselected_paused;
|
const paused = selected_paused + nonselected_paused;
|
||||||
const active = selected_active + nonselected_active;
|
const active = selected_active + nonselected_active;
|
||||||
|
|||||||
@@ -30,15 +30,21 @@ const fmt_MBps = new Intl.NumberFormat(current_locale, {
|
|||||||
unit: 'megabyte-per-second',
|
unit: 'megabyte-per-second',
|
||||||
});
|
});
|
||||||
|
|
||||||
export class Formatter {
|
export const Formatter = {
|
||||||
static countString(msgid, msgid_plural, n) {
|
/** Round a string of a number to a specified number of decimal places */
|
||||||
|
_toTruncFixed(number, places) {
|
||||||
|
const returnValue = Math.floor(number * 10 ** places) / 10 ** places;
|
||||||
|
return returnValue.toFixed(places);
|
||||||
|
},
|
||||||
|
|
||||||
|
countString(msgid, msgid_plural, n) {
|
||||||
return `${this.number(n)} ${this.ngettext(msgid, msgid_plural, n)}`;
|
return `${this.number(n)} ${this.ngettext(msgid, msgid_plural, n)}`;
|
||||||
}
|
},
|
||||||
|
|
||||||
// Formats the a memory size into a human-readable string
|
// Formats the a memory size into a human-readable string
|
||||||
// @param {Number} bytes the filesize in bytes
|
// @param {Number} bytes the filesize in bytes
|
||||||
// @return {String} human-readable string
|
// @return {String} human-readable string
|
||||||
static mem(bytes) {
|
mem(bytes) {
|
||||||
if (bytes < 0) {
|
if (bytes < 0) {
|
||||||
return 'Unknown';
|
return 'Unknown';
|
||||||
}
|
}
|
||||||
@@ -55,22 +61,26 @@ export class Formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return 'E2BIG';
|
return 'E2BIG';
|
||||||
}
|
},
|
||||||
|
|
||||||
static ngettext(msgid, msgid_plural, n) {
|
ngettext(msgid, msgid_plural, n) {
|
||||||
return plural_rules.select(n) === 'one' ? msgid : msgid_plural;
|
return plural_rules.select(n) === 'one' ? msgid : msgid_plural;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
number(number) {
|
||||||
|
return number_format.format(number);
|
||||||
|
},
|
||||||
|
|
||||||
// format a percentage to a string
|
// format a percentage to a string
|
||||||
static percentString(x) {
|
percentString(x) {
|
||||||
const decimal_places = x < 100 ? 1 : 0;
|
const decimal_places = x < 100 ? 1 : 0;
|
||||||
return this._toTruncFixed(x, decimal_places);
|
return this._toTruncFixed(x, decimal_places);
|
||||||
}
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Format a ratio to a string
|
* Format a ratio to a string
|
||||||
*/
|
*/
|
||||||
static ratioString(x) {
|
ratioString(x) {
|
||||||
if (x === -1) {
|
if (x === -1) {
|
||||||
return 'None';
|
return 'None';
|
||||||
}
|
}
|
||||||
@@ -78,32 +88,32 @@ export class Formatter {
|
|||||||
return '∞';
|
return '∞';
|
||||||
}
|
}
|
||||||
return this.percentString(x);
|
return this.percentString(x);
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the a disk capacity or file size into a human-readable string
|
* Formats the a disk capacity or file size into a human-readable string
|
||||||
* @param {Number} bytes the filesize in bytes
|
* @param {Number} bytes the filesize in bytes
|
||||||
* @return {String} human-readable string
|
* @return {String} human-readable string
|
||||||
*/
|
*/
|
||||||
static size(bytes) {
|
size(bytes) {
|
||||||
return this.mem(bytes);
|
return this.mem(bytes);
|
||||||
}
|
},
|
||||||
|
|
||||||
static speed(KBps) {
|
speed(KBps) {
|
||||||
return KBps < 999.95 ? fmt_kBps.format(KBps) : fmt_MBps.format(KBps / 1000);
|
return KBps < 999.95 ? fmt_kBps.format(KBps) : fmt_MBps.format(KBps / 1000);
|
||||||
}
|
},
|
||||||
|
|
||||||
static speedBps(Bps) {
|
speedBps(Bps) {
|
||||||
return this.speed(this.toKBps(Bps));
|
return this.speed(this.toKBps(Bps));
|
||||||
}
|
},
|
||||||
|
|
||||||
static timeInterval(seconds) {
|
timeInterval(seconds) {
|
||||||
const days = Math.floor(seconds / 86400);
|
const days = Math.floor(seconds / 86_400);
|
||||||
if (days) {
|
if (days) {
|
||||||
return this.countString('day', 'days', days);
|
return this.countString('day', 'days', days);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hours = Math.floor((seconds % 86400) / 3600);
|
const hours = Math.floor((seconds % 86_400) / 3600);
|
||||||
if (hours) {
|
if (hours) {
|
||||||
return this.countString('hour', 'hours', hours);
|
return this.countString('hour', 'hours', hours);
|
||||||
}
|
}
|
||||||
@@ -115,9 +125,9 @@ export class Formatter {
|
|||||||
|
|
||||||
seconds = Math.floor(seconds % 60);
|
seconds = Math.floor(seconds % 60);
|
||||||
return this.countString('second', 'seconds', seconds);
|
return this.countString('second', 'seconds', seconds);
|
||||||
}
|
},
|
||||||
|
|
||||||
static timestamp(seconds) {
|
timestamp(seconds) {
|
||||||
if (!seconds) {
|
if (!seconds) {
|
||||||
return 'N/A';
|
return 'N/A';
|
||||||
}
|
}
|
||||||
@@ -168,19 +178,9 @@ export class Formatter {
|
|||||||
time = [hours, minutes, seconds].join(':');
|
time = [hours, minutes, seconds].join(':');
|
||||||
|
|
||||||
return [date, time, period].join(' ');
|
return [date, time, period].join(' ');
|
||||||
}
|
},
|
||||||
|
|
||||||
static toKBps(Bps) {
|
toKBps(Bps) {
|
||||||
return Math.floor(Bps / kilo);
|
return Math.floor(Bps / kilo);
|
||||||
}
|
},
|
||||||
|
};
|
||||||
static number(number) {
|
|
||||||
return number_format.format(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Round a string of a number to a specified number of decimal places */
|
|
||||||
static _toTruncFixed(number, places) {
|
|
||||||
const returnValue = Math.floor(number * 10 ** places) / 10 ** places;
|
|
||||||
return returnValue.toFixed(places);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ export class Inspector extends EventTarget {
|
|||||||
const thead = document.createElement('thead');
|
const thead = document.createElement('thead');
|
||||||
const tr = document.createElement('tr');
|
const tr = document.createElement('tr');
|
||||||
const names = ['', 'Up', 'Down', 'Done', 'Status', 'Address', 'Client'];
|
const names = ['', 'Up', 'Down', 'Done', 'Status', 'Address', 'Client'];
|
||||||
names.forEach((name, index) => {
|
for (const [index, name] of names.entries()) {
|
||||||
const th = document.createElement('th');
|
const th = document.createElement('th');
|
||||||
const classname = peer_column_classes[index];
|
const classname = peer_column_classes[index];
|
||||||
if (classname === 'encryption') {
|
if (classname === 'encryption') {
|
||||||
@@ -159,7 +159,7 @@ export class Inspector extends EventTarget {
|
|||||||
th.classList.add(classname);
|
th.classList.add(classname);
|
||||||
setTextContent(th, name);
|
setTextContent(th, name);
|
||||||
tr.append(th);
|
tr.append(th);
|
||||||
});
|
}
|
||||||
const tbody = document.createElement('tbody');
|
const tbody = document.createElement('tbody');
|
||||||
thead.append(tr);
|
thead.append(tr);
|
||||||
table.append(thead);
|
table.append(thead);
|
||||||
@@ -201,9 +201,13 @@ export class Inspector extends EventTarget {
|
|||||||
// update the inspector when a selected torrent's data changes.
|
// update the inspector when a selected torrent's data changes.
|
||||||
const key = 'dataChanged';
|
const key = 'dataChanged';
|
||||||
const callback = this.torrent_listener;
|
const callback = this.torrent_listener;
|
||||||
this.torrents.forEach((t) => t.removeEventListener(key, callback));
|
for (const t of this.torrents) {
|
||||||
|
t.removeEventListener(key, callback);
|
||||||
|
}
|
||||||
this.torrents = [...torrents];
|
this.torrents = [...torrents];
|
||||||
this.torrents.forEach((t) => t.addEventListener(key, callback));
|
for (const t of this.torrents) {
|
||||||
|
t.addEventListener(key, callback);
|
||||||
|
}
|
||||||
|
|
||||||
this._refreshTorrents();
|
this._refreshTorrents();
|
||||||
this._updateCurrentPage();
|
this._updateCurrentPage();
|
||||||
@@ -228,8 +232,8 @@ export class Inspector extends EventTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_updateCurrentPage() {
|
_updateCurrentPage() {
|
||||||
const { elements } = this;
|
const { current_page, elements } = this;
|
||||||
switch (this.current_page) {
|
switch (current_page) {
|
||||||
case elements.files.root:
|
case elements.files.root:
|
||||||
this._updateFiles();
|
this._updateFiles();
|
||||||
break;
|
break;
|
||||||
@@ -244,7 +248,7 @@ export class Inspector extends EventTarget {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn('unexpected page');
|
console.warn('unexpected page');
|
||||||
console.log(this.current_page);
|
console.log(current_page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,8 +258,7 @@ export class Inspector extends EventTarget {
|
|||||||
const unknown = 'Unknown';
|
const unknown = 'Unknown';
|
||||||
const fmt = Formatter;
|
const fmt = Formatter;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const { torrents } = this;
|
const { elements: e, torrents } = this;
|
||||||
const e = this.elements;
|
|
||||||
const sizeWhenDone = torrents.reduce(
|
const sizeWhenDone = torrents.reduce(
|
||||||
(accumulator, t) => accumulator + t.getSizeWhenDone(),
|
(accumulator, t) => accumulator + t.getSizeWhenDone(),
|
||||||
0
|
0
|
||||||
@@ -502,7 +505,7 @@ export class Inspector extends EventTarget {
|
|||||||
const date = get(torrents[0]);
|
const date = get(torrents[0]);
|
||||||
const mixed_date = !torrents.every((t) => get(t) === date);
|
const mixed_date = !torrents.every((t) => get(t) === date);
|
||||||
|
|
||||||
const empty_creator = !creator || !creator.length;
|
const empty_creator = !creator || creator.length === 0;
|
||||||
const empty_date = !date;
|
const empty_date = !date;
|
||||||
if (mixed_creator || mixed_date) {
|
if (mixed_creator || mixed_date) {
|
||||||
string = mixed;
|
string = mixed;
|
||||||
@@ -557,8 +560,8 @@ export class Inspector extends EventTarget {
|
|||||||
|
|
||||||
_updatePeers() {
|
_updatePeers() {
|
||||||
const fmt = Formatter;
|
const fmt = Formatter;
|
||||||
const { torrents } = this;
|
const { elements, torrents } = this;
|
||||||
const { tbody } = this.elements.peers;
|
const { tbody } = elements.peers;
|
||||||
|
|
||||||
const cell_setters = [
|
const cell_setters = [
|
||||||
(peer, td) => {
|
(peer, td) => {
|
||||||
@@ -598,12 +601,12 @@ export class Inspector extends EventTarget {
|
|||||||
for (const peer of tor.getPeers()) {
|
for (const peer of tor.getPeers()) {
|
||||||
const tr = document.createElement('tr');
|
const tr = document.createElement('tr');
|
||||||
tr.classList.add('peer-row');
|
tr.classList.add('peer-row');
|
||||||
cell_setters.forEach((setter, index) => {
|
for (const [index, setter] of cell_setters.entries()) {
|
||||||
const td = document.createElement('td');
|
const td = document.createElement('td');
|
||||||
td.classList.add(peer_column_classes[index]);
|
td.classList.add(peer_column_classes[index]);
|
||||||
setter(peer, td);
|
setter(peer, td);
|
||||||
tr.append(td);
|
tr.append(td);
|
||||||
});
|
}
|
||||||
rows.push(tr);
|
rows.push(tr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +627,7 @@ export class Inspector extends EventTarget {
|
|||||||
case Torrent._TrackerWaiting: {
|
case Torrent._TrackerWaiting: {
|
||||||
const timeUntilAnnounce = Math.max(
|
const timeUntilAnnounce = Math.max(
|
||||||
0,
|
0,
|
||||||
tracker.nextAnnounceTime - new Date().getTime() / 1000
|
tracker.nextAnnounceTime - Date.now() / 1000
|
||||||
);
|
);
|
||||||
return `Next announce in ${Formatter.timeInterval(timeUntilAnnounce)}`;
|
return `Next announce in ${Formatter.timeInterval(timeUntilAnnounce)}`;
|
||||||
}
|
}
|
||||||
@@ -706,7 +709,7 @@ export class Inspector extends EventTarget {
|
|||||||
rows.push(title);
|
rows.push(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
tor.getTrackers().forEach((tracker, index) => {
|
for (const [index, tracker] of tor.getTrackers().entries()) {
|
||||||
const announceState = Inspector.getAnnounceState(tracker);
|
const announceState = Inspector.getAnnounceState(tracker);
|
||||||
const lastAnnounceStatusHash = Inspector.lastAnnounceStatus(tracker);
|
const lastAnnounceStatusHash = Inspector.lastAnnounceStatus(tracker);
|
||||||
const lastScrapeStatusHash = Inspector.lastScrapeStatus(tracker);
|
const lastScrapeStatusHash = Inspector.lastScrapeStatus(tracker);
|
||||||
@@ -773,7 +776,7 @@ export class Inspector extends EventTarget {
|
|||||||
tier_div.append(element);
|
tier_div.append(element);
|
||||||
|
|
||||||
rows.push(tier_div);
|
rows.push(tier_div);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: modify instead of rebuilding wholesale?
|
// TODO: modify instead of rebuilding wholesale?
|
||||||
@@ -836,7 +839,7 @@ export class Inspector extends EventTarget {
|
|||||||
file_indices: [],
|
file_indices: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
tor.getFiles().forEach((file, index) => {
|
for (const [index, file] of tor.getFiles().entries()) {
|
||||||
const { name } = file;
|
const { name } = file;
|
||||||
const tokens = name.split('/');
|
const tokens = name.split('/');
|
||||||
let walk = tree;
|
let walk = tree;
|
||||||
@@ -856,7 +859,7 @@ export class Inspector extends EventTarget {
|
|||||||
walk.file_index = index;
|
walk.file_index = index;
|
||||||
delete walk.children;
|
delete walk.children;
|
||||||
leaves.push(walk);
|
leaves.push(walk);
|
||||||
});
|
}
|
||||||
|
|
||||||
for (const leaf of leaves) {
|
for (const leaf of leaves) {
|
||||||
const { file_index } = leaf;
|
const { file_index } = leaf;
|
||||||
@@ -901,7 +904,7 @@ export class Inspector extends EventTarget {
|
|||||||
|
|
||||||
_updateFiles() {
|
_updateFiles() {
|
||||||
const { list } = this.elements.files;
|
const { list } = this.elements.files;
|
||||||
const { torrents } = this;
|
const { file_rows, file_torrent, file_torrent_n, torrents } = this;
|
||||||
|
|
||||||
// only show one torrent at a time
|
// only show one torrent at a time
|
||||||
if (torrents.length !== 1) {
|
if (torrents.length !== 1) {
|
||||||
@@ -911,7 +914,7 @@ export class Inspector extends EventTarget {
|
|||||||
|
|
||||||
const [tor] = torrents;
|
const [tor] = torrents;
|
||||||
const n = tor.getFiles().length;
|
const n = tor.getFiles().length;
|
||||||
if (tor !== this.file_torrent || n !== this.file_torrent_n) {
|
if (tor !== file_torrent || n !== file_torrent_n) {
|
||||||
// rebuild the file list...
|
// rebuild the file list...
|
||||||
this._clearFileList();
|
this._clearFileList();
|
||||||
this.file_torrent = tor;
|
this.file_torrent = tor;
|
||||||
@@ -923,7 +926,9 @@ export class Inspector extends EventTarget {
|
|||||||
list.append(fragment);
|
list.append(fragment);
|
||||||
} else {
|
} else {
|
||||||
// ...refresh the already-existing file list
|
// ...refresh the already-existing file list
|
||||||
this.file_rows.forEach((row) => row.refresh());
|
for (const row of file_rows) {
|
||||||
|
row.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ export class OpenDialog extends EventTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onConfirm() {
|
_onConfirm() {
|
||||||
const { remote } = this;
|
const { controller, elements, remote } = this;
|
||||||
const { file_input, folder_input, start_input, url_input } = this.elements;
|
const { file_input, folder_input, start_input, url_input } = elements;
|
||||||
const paused = !start_input.checked;
|
const paused = !start_input.checked;
|
||||||
const destination = folder_input.value.trim();
|
const destination = folder_input.value.trim();
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ export class OpenDialog extends EventTarget {
|
|||||||
remote.sendRequest(o, (response) => {
|
remote.sendRequest(o, (response) => {
|
||||||
if (response.result !== 'success') {
|
if (response.result !== 'success') {
|
||||||
alert(`Error adding "${file.name}": ${response.result}`);
|
alert(`Error adding "${file.name}": ${response.result}`);
|
||||||
this.controller.setCurrentPopup(
|
controller.setCurrentPopup(
|
||||||
new AlertDialog({
|
new AlertDialog({
|
||||||
heading: `Error adding "${file.name}"`,
|
heading: `Error adding "${file.name}"`,
|
||||||
message: response.result,
|
message: response.result,
|
||||||
@@ -93,7 +93,7 @@ export class OpenDialog extends EventTarget {
|
|||||||
|
|
||||||
let url = url_input.value.trim();
|
let url = url_input.value.trim();
|
||||||
if (url.length > 0) {
|
if (url.length > 0) {
|
||||||
if (url.match(/^[\da-f]{40}$/i)) {
|
if (/^[\da-f]{40}$/i.test(url)) {
|
||||||
url = `magnet:?xt=urn:btih:${url}`;
|
url = `magnet:?xt=urn:btih:${url}`;
|
||||||
}
|
}
|
||||||
const o = {
|
const o = {
|
||||||
@@ -107,7 +107,7 @@ export class OpenDialog extends EventTarget {
|
|||||||
console.log(o);
|
console.log(o);
|
||||||
remote.sendRequest(o, (payload, response) => {
|
remote.sendRequest(o, (payload, response) => {
|
||||||
if (response.result !== 'success') {
|
if (response.result !== 'success') {
|
||||||
this.controller.setCurrentPopup(
|
controller.setCurrentPopup(
|
||||||
new AlertDialog({
|
new AlertDialog({
|
||||||
heading: `Error adding "${url}"`,
|
heading: `Error adding "${url}"`,
|
||||||
message: response.result,
|
message: response.result,
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ export class OverflowMenu extends EventTarget {
|
|||||||
select.addEventListener('change', (event_) => {
|
select.addEventListener('change', (event_) => {
|
||||||
const { value } = event_.target;
|
const { value } = event_.target;
|
||||||
console.log(event_);
|
console.log(event_);
|
||||||
if (event_.target.value === unlimited) {
|
if (value === unlimited) {
|
||||||
this.remote.savePrefs({ [RPC._UpSpeedLimited]: false });
|
this.remote.savePrefs({ [RPC._UpSpeedLimited]: false });
|
||||||
} else {
|
} else {
|
||||||
this.remote.savePrefs({
|
this.remote.savePrefs({
|
||||||
@@ -361,7 +361,7 @@ export class OverflowMenu extends EventTarget {
|
|||||||
select.addEventListener('change', (event_) => {
|
select.addEventListener('change', (event_) => {
|
||||||
const { value } = event_.target;
|
const { value } = event_.target;
|
||||||
console.log(event_);
|
console.log(event_);
|
||||||
if (event_.target.value === unlimited) {
|
if (value === unlimited) {
|
||||||
this.remote.savePrefs({ [RPC._DownSpeedLimited]: false });
|
this.remote.savePrefs({ [RPC._DownSpeedLimited]: false });
|
||||||
} else {
|
} else {
|
||||||
this.remote.savePrefs({
|
this.remote.savePrefs({
|
||||||
@@ -460,7 +460,8 @@ export class OverflowMenu extends EventTarget {
|
|||||||
e.textContent = 'Source Code';
|
e.textContent = 'Source Code';
|
||||||
options.append(e);
|
options.append(e);
|
||||||
|
|
||||||
Object.values(actions).forEach(this._updateElement.bind(this));
|
this._updateElement = this._updateElement.bind(this);
|
||||||
|
|
||||||
return { actions, elements, root };
|
return { actions, elements, root };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export class Prefs extends EventTarget {
|
|||||||
static _setCookie(key, value) {
|
static _setCookie(key, value) {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
date.setFullYear(date.getFullYear() + 1);
|
date.setFullYear(date.getFullYear() + 1);
|
||||||
|
// eslint-disable-next-line unicorn/no-document-cookie
|
||||||
document.cookie = `${key}=${value}; SameSite=Strict; expires=${date.toGMTString()}; path=/`;
|
document.cookie = `${key}=${value}; SameSite=Strict; expires=${date.toGMTString()}; path=/`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ export class Prefs extends EventTarget {
|
|||||||
if (value === 'false') {
|
if (value === 'false') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (value.match(/^\d+$/)) {
|
if (/^\d+$/.test(value)) {
|
||||||
return Number.parseInt(value, 10);
|
return Number.parseInt(value, 10);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ export class Remote {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
addTorrentByUrl(url, options) {
|
addTorrentByUrl(url, options) {
|
||||||
if (url.match(/^[\da-f]{40}$/i)) {
|
if (/^[\da-f]{40}$/i.test(url)) {
|
||||||
url = `magnet:?xt=urn:btih:${url}`;
|
url = `magnet:?xt=urn:btih:${url}`;
|
||||||
}
|
}
|
||||||
const o = {
|
const o = {
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ export class RemoveDialog extends EventTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onConfirm() {
|
_onConfirm() {
|
||||||
const { torrents } = this.options;
|
const { remote, torrents, trash } = this.options;
|
||||||
if (torrents.length > 0) {
|
if (torrents.length > 0) {
|
||||||
if (this.options.trash) {
|
if (trash) {
|
||||||
this.options.remote.removeTorrentsAndData(torrents);
|
remote.removeTorrentsAndData(torrents);
|
||||||
} else {
|
} else {
|
||||||
this.options.remote.removeTorrents(torrents);
|
remote.removeTorrents(torrents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,13 +64,13 @@ export class RemoveDialog extends EventTarget {
|
|||||||
static _createMessage(options) {
|
static _createMessage(options) {
|
||||||
let heading = null;
|
let heading = null;
|
||||||
let message = null;
|
let message = null;
|
||||||
const { torrents } = options;
|
const { torrents, trash } = options;
|
||||||
const [torrent] = torrents;
|
const [torrent] = torrents;
|
||||||
if (options.trash && torrents.length === 1) {
|
if (trash && torrents.length === 1) {
|
||||||
heading = `Remove ${torrent.getName()} and delete data?`;
|
heading = `Remove ${torrent.getName()} and delete data?`;
|
||||||
message =
|
message =
|
||||||
'All data downloaded for this torrent will be deleted. Are you sure you want to remove it?';
|
'All data downloaded for this torrent will be deleted. Are you sure you want to remove it?';
|
||||||
} else if (options.trash) {
|
} else if (trash) {
|
||||||
heading = `Remove ${torrents.length} transfers and delete data?`;
|
heading = `Remove ${torrents.length} transfers and delete data?`;
|
||||||
message =
|
message =
|
||||||
'All data downloaded for these torrents will be deleted. Are you sure you want to remove them?';
|
'All data downloaded for these torrents will be deleted. Are you sure you want to remove them?';
|
||||||
|
|||||||
@@ -72,14 +72,14 @@ export class StatisticsDialog extends EventTarget {
|
|||||||
|
|
||||||
static _create() {
|
static _create() {
|
||||||
const elements = createDialogContainer('statistics-dialog');
|
const elements = createDialogContainer('statistics-dialog');
|
||||||
const { workarea } = elements;
|
const { confirm, dismiss, heading, root, workarea } = elements;
|
||||||
elements.confirm.remove();
|
confirm.remove();
|
||||||
elements.dismiss.textContent = 'Close';
|
dismiss.textContent = 'Close';
|
||||||
delete elements.confirm;
|
delete elements.confirm;
|
||||||
|
|
||||||
const heading_text = 'Statistics';
|
const heading_text = 'Statistics';
|
||||||
elements.root.setAttribute('aria-label', heading_text);
|
root.setAttribute('aria-label', heading_text);
|
||||||
elements.heading.textContent = heading_text;
|
heading.textContent = heading_text;
|
||||||
|
|
||||||
const labels = ['Uploaded:', 'Downloaded:', 'Ratio:', 'Running time:'];
|
const labels = ['Uploaded:', 'Downloaded:', 'Ratio:', 'Running time:'];
|
||||||
let section = createInfoSection('Current session', labels);
|
let section = createInfoSection('Current session', labels);
|
||||||
|
|||||||
@@ -11,8 +11,21 @@ import { Formatter } from './formatter.js';
|
|||||||
import { Torrent } from './torrent.js';
|
import { Torrent } from './torrent.js';
|
||||||
import { setTextContent } from './utils.js';
|
import { setTextContent } from './utils.js';
|
||||||
|
|
||||||
class TorrentRendererHelper {
|
const TorrentRendererHelper = {
|
||||||
static getProgressInfo(controller, t) {
|
formatDL: (t) => {
|
||||||
|
return `▼${Formatter.speedBps(t.getDownloadSpeed())}`;
|
||||||
|
},
|
||||||
|
formatETA: (t) => {
|
||||||
|
const eta = t.getETA();
|
||||||
|
if (eta < 0 || eta >= 999 * 60 * 60) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return `ETA: ${Formatter.timeInterval(eta)}`;
|
||||||
|
},
|
||||||
|
formatUL: (t) => {
|
||||||
|
return `▲${Formatter.speedBps(t.getUploadSpeed())}`;
|
||||||
|
},
|
||||||
|
getProgressInfo: (controller, t) => {
|
||||||
const status = t.getStatus();
|
const status = t.getStatus();
|
||||||
const classList = ['torrent-progress-bar'];
|
const classList = ['torrent-progress-bar'];
|
||||||
let percent = null;
|
let percent = null;
|
||||||
@@ -46,29 +59,14 @@ class TorrentRendererHelper {
|
|||||||
classList,
|
classList,
|
||||||
percent,
|
percent,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
static renderProgressbar(controller, t, progressbar) {
|
renderProgressbar: (controller, t, progressbar) => {
|
||||||
const info = TorrentRendererHelper.getProgressInfo(controller, t);
|
const info = TorrentRendererHelper.getProgressInfo(controller, t);
|
||||||
progressbar.className = info.classList.join(' ');
|
progressbar.className = info.classList.join(' ');
|
||||||
progressbar.style['background-size'] = `${info.percent}% 100%, 100% 100%`;
|
progressbar.style['background-size'] = `${info.percent}% 100%, 100% 100%`;
|
||||||
}
|
},
|
||||||
|
};
|
||||||
static formatUL(t) {
|
|
||||||
return `▲${Formatter.speedBps(t.getUploadSpeed())}`;
|
|
||||||
}
|
|
||||||
static formatDL(t) {
|
|
||||||
return `▼${Formatter.speedBps(t.getDownloadSpeed())}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static formatETA(t) {
|
|
||||||
const eta = t.getETA();
|
|
||||||
if (eta < 0 || eta >= 999 * 60 * 60) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return `ETA: ${Formatter.timeInterval(eta)}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
||||||
|
|||||||
@@ -256,9 +256,9 @@ export class Transmission extends EventTarget {
|
|||||||
this.prefs.addEventListener('change', ({ key, value }) =>
|
this.prefs.addEventListener('change', ({ key, value }) =>
|
||||||
this._onPrefChanged(key, value)
|
this._onPrefChanged(key, value)
|
||||||
);
|
);
|
||||||
this.prefs
|
for (const [key, value] of this.prefs.entries()) {
|
||||||
.entries()
|
this._onPrefChanged(key, value);
|
||||||
.forEach(([key, value]) => this._onPrefChanged(key, value));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDaemonPrefs() {
|
loadDaemonPrefs() {
|
||||||
@@ -419,9 +419,9 @@ export class Transmission extends EventTarget {
|
|||||||
_dispatchSelectionChanged() {
|
_dispatchSelectionChanged() {
|
||||||
const nonselected = [];
|
const nonselected = [];
|
||||||
const selected = [];
|
const selected = [];
|
||||||
this._rows.forEach((r) =>
|
for (const r of this._rows) {
|
||||||
(r.isSelected() ? selected : nonselected).push(r.getTorrent())
|
(r.isSelected() ? selected : nonselected).push(r.getTorrent());
|
||||||
);
|
}
|
||||||
|
|
||||||
const event = new Event('torrent-selection-changed');
|
const event = new Event('torrent-selection-changed');
|
||||||
event.nonselected = nonselected;
|
event.nonselected = nonselected;
|
||||||
@@ -455,7 +455,7 @@ export class Transmission extends EventTarget {
|
|||||||
|
|
||||||
// Process key events
|
// Process key events
|
||||||
_keyDown(event_) {
|
_keyDown(event_) {
|
||||||
const { keyCode } = event_;
|
const { ctrlKey, keyCode, metaKey, shiftKey, target } = event_;
|
||||||
|
|
||||||
// look for a shortcut
|
// look for a shortcut
|
||||||
const aria_keys = Transmission._createKeyShortcutFromKeyboardEvent(event_);
|
const aria_keys = Transmission._createKeyShortcutFromKeyboardEvent(event_);
|
||||||
@@ -474,19 +474,14 @@ export class Transmission extends EventTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const any_popup_active = document.querySelector('.popup:not(.hidden)');
|
const any_popup_active = document.querySelector('.popup:not(.hidden)');
|
||||||
const is_input_focused = event_.target.matches('input');
|
const is_input_focused = target.matches('input');
|
||||||
const rows = this._rows;
|
const rows = this._rows;
|
||||||
|
|
||||||
// Some shortcuts can only be used if the following conditions are met:
|
// Some shortcuts can only be used if the following conditions are met:
|
||||||
// 1. when no input fields are focused
|
// 1. when no input fields are focused
|
||||||
// 2. when no other dialogs are visible
|
// 2. when no other dialogs are visible
|
||||||
// 3. when the meta or ctrl key isn't pressed (i.e. opening dev tools shouldn't trigger the info panel)
|
// 3. when the meta or ctrl key isn't pressed (i.e. opening dev tools shouldn't trigger the info panel)
|
||||||
if (
|
if (!is_input_focused && !any_popup_active && !metaKey && !ctrlKey) {
|
||||||
!is_input_focused &&
|
|
||||||
!any_popup_active &&
|
|
||||||
!event_.metaKey &&
|
|
||||||
!event_.ctrlKey
|
|
||||||
) {
|
|
||||||
const shift_key = keyCode === 16; // shift key pressed
|
const shift_key = keyCode === 16; // shift key pressed
|
||||||
const up_key = keyCode === 38; // up key pressed
|
const up_key = keyCode === 38; // up key pressed
|
||||||
const dn_key = keyCode === 40; // down key pressed
|
const dn_key = keyCode === 40; // down key pressed
|
||||||
@@ -520,7 +515,7 @@ export class Transmission extends EventTarget {
|
|||||||
this._deselectRow(rows[last]);
|
this._deselectRow(rows[last]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (event_.shiftKey) {
|
if (shiftKey) {
|
||||||
this._selectRange(r);
|
this._selectRange(r);
|
||||||
} else {
|
} else {
|
||||||
this._setSelectedRow(r);
|
this._setSelectedRow(r);
|
||||||
@@ -582,12 +577,13 @@ export class Transmission extends EventTarget {
|
|||||||
const type = event_.data.Transfer.types
|
const type = event_.data.Transfer.types
|
||||||
.filter((t) => ['text/uri-list', 'text/plain'].contains(t))
|
.filter((t) => ['text/uri-list', 'text/plain'].contains(t))
|
||||||
.pop();
|
.pop();
|
||||||
event_.dataTransfer
|
for (const uri of event_.dataTransfer
|
||||||
.getData(type)
|
.getData(type)
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map((string) => string.trim())
|
.map((string) => string.trim())
|
||||||
.filter((string) => Transmission._isValidURL(string))
|
.filter((string) => Transmission._isValidURL(string))) {
|
||||||
.forEach((uri) => this.remote.addTorrentByUrl(uri, paused));
|
this.remote.addTorrentByUrl(uri, paused);
|
||||||
|
}
|
||||||
|
|
||||||
event_.preventDefault();
|
event_.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
@@ -632,9 +628,9 @@ export class Transmission extends EventTarget {
|
|||||||
const keys = table.shift();
|
const keys = table.shift();
|
||||||
const o = {};
|
const o = {};
|
||||||
for (const row of table) {
|
for (const row of table) {
|
||||||
keys.forEach((key, index) => {
|
for (const [index, key] of keys.entries()) {
|
||||||
o[key] = row[index];
|
o[key] = row[index];
|
||||||
});
|
}
|
||||||
const { id } = o;
|
const { id } = o;
|
||||||
let t = this._torrents[id];
|
let t = this._torrents[id];
|
||||||
if (t) {
|
if (t) {
|
||||||
@@ -656,10 +652,11 @@ export class Transmission extends EventTarget {
|
|||||||
|
|
||||||
if (needinfo.length > 0) {
|
if (needinfo.length > 0) {
|
||||||
// whee, new torrents! get their initial information.
|
// whee, new torrents! get their initial information.
|
||||||
const more_fields = ['id'].concat(
|
const more_fields = [
|
||||||
Torrent.Fields.Metadata,
|
'id',
|
||||||
Torrent.Fields.Stats
|
...Torrent.Fields.Metadata,
|
||||||
);
|
...Torrent.Fields.Stats,
|
||||||
|
];
|
||||||
this.updateTorrents(needinfo, more_fields);
|
this.updateTorrents(needinfo, more_fields);
|
||||||
this.refilterSoon();
|
this.refilterSoon();
|
||||||
}
|
}
|
||||||
@@ -691,12 +688,12 @@ TODO: fix this when notifications get fixed
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
refreshTorrents() {
|
refreshTorrents() {
|
||||||
const fields = ['id'].concat(Torrent.Fields.Stats);
|
const fields = ['id', ...Torrent.Fields.Stats];
|
||||||
this.updateTorrents('recently-active', fields);
|
this.updateTorrents('recently-active', fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
_initializeTorrents() {
|
_initializeTorrents() {
|
||||||
const fields = ['id'].concat(Torrent.Fields.Metadata, Torrent.Fields.Stats);
|
const fields = ['id', ...Torrent.Fields.Metadata, ...Torrent.Fields.Stats];
|
||||||
this.updateTorrents(null, fields);
|
this.updateTorrents(null, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -910,7 +907,9 @@ TODO: fix this when notifications get fixed
|
|||||||
this.prefs.sort_mode,
|
this.prefs.sort_mode,
|
||||||
this.prefs.sort_direction
|
this.prefs.sort_direction
|
||||||
);
|
);
|
||||||
torrents.forEach((tor, index) => (rows[index] = id2row[tor.getId()]));
|
for (const [index, tor] of torrents.entries()) {
|
||||||
|
rows[index] = id2row[tor.getId()];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_refilter(rebuildEverything) {
|
_refilter(rebuildEverything) {
|
||||||
@@ -1036,13 +1035,11 @@ TODO: fix this when notifications get fixed
|
|||||||
this.dirtyTorrents.clear();
|
this.dirtyTorrents.clear();
|
||||||
|
|
||||||
// set the odd/even property
|
// set the odd/even property
|
||||||
rows
|
for (const [index, e] of rows.map((row) => row.getElement()).entries()) {
|
||||||
.map((row) => row.getElement())
|
const even = index % 2 === 0;
|
||||||
.forEach((e, index) => {
|
e.classList.toggle('even', even);
|
||||||
const even = index % 2 === 0;
|
e.classList.toggle('odd', !even);
|
||||||
e.classList.toggle('even', even);
|
}
|
||||||
e.classList.toggle('odd', !even);
|
|
||||||
});
|
|
||||||
|
|
||||||
this._updateStatusbar();
|
this._updateStatusbar();
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -9,26 +9,9 @@
|
|||||||
|
|
||||||
import isEqual from 'lodash.isequal';
|
import isEqual from 'lodash.isequal';
|
||||||
|
|
||||||
export class Utils {
|
export const Utils = {
|
||||||
/**
|
|
||||||
* Checks to see if the content actually changed before poking the DOM.
|
|
||||||
*/
|
|
||||||
static setInnerHTML(e, html) {
|
|
||||||
if (!e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* innerHTML is listed as a string, but the browser seems to change it.
|
|
||||||
* For example, "∞" gets changed to "∞" somewhere down the line.
|
|
||||||
* So, let's use an arbitrary different field to test our state... */
|
|
||||||
if (e.currentHTML !== html) {
|
|
||||||
e.currentHTML = html;
|
|
||||||
e.innerHTML = html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Given a numerator and denominator, return a ratio string */
|
/** Given a numerator and denominator, return a ratio string */
|
||||||
static ratio(numerator, denominator) {
|
ratio(numerator, denominator) {
|
||||||
let result = Math.floor((100 * numerator) / denominator) / 100;
|
let result = Math.floor((100 * numerator) / denominator) / 100;
|
||||||
|
|
||||||
// check for special cases
|
// check for special cases
|
||||||
@@ -42,8 +25,25 @@ export class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Checks to see if the content actually changed before poking the DOM.
|
||||||
|
*/
|
||||||
|
setInnerHTML(e, html) {
|
||||||
|
if (!e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* innerHTML is listed as a string, but the browser seems to change it.
|
||||||
|
* For example, "∞" gets changed to "∞" somewhere down the line.
|
||||||
|
* So, let's use an arbitrary different field to test our state... */
|
||||||
|
if (e.currentHTML !== html) {
|
||||||
|
e.currentHTML = html;
|
||||||
|
e.innerHTML = html;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export function createTabsContainer(id, tabs, callback) {
|
export function createTabsContainer(id, tabs, callback) {
|
||||||
const root = document.createElement('div');
|
const root = document.createElement('div');
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
|
|
||||||
@@ -45,11 +45,7 @@ const config = {
|
|||||||
optimization: {
|
optimization: {
|
||||||
minimizer: [
|
minimizer: [
|
||||||
new TerserPlugin(),
|
new TerserPlugin(),
|
||||||
new OptimizeCSSAssetsPlugin({
|
new CssMinimizerPlugin(),
|
||||||
cssProcessorPluginOptions: {
|
|
||||||
preset: ['default', { discardComments: { removeAll: true } }],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
|
|||||||
4479
web/yarn.lock
4479
web/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user