Integrate Eslint and TypeScript into build & linting process (#142)

This commit is contained in:
David Joseph Guzsik 2021-10-09 21:58:07 +02:00 committed by GitHub
parent 0487a82db6
commit 77e689e29a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 2002 additions and 133 deletions

View file

@ -8,7 +8,8 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
- name: Cache mix deps
uses: actions/cache@v2
with:
path: |
_build
@ -25,3 +26,28 @@ jobs:
run: |
docker-compose run app mix sobelow --config
docker-compose run app mix deps.audit
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Cache node_modules
id: cache-node-modules
uses: actions/cache@v2
with:
path: ./assets/node_modules
key: node_modules-${{ hashFiles('./assets/package-lock.json') }}
- name: Install npm dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci --ignore-scripts
working-directory: ./assets
- name: Run ESLint
run: npm run lint
working-directory: ./assets

2
assets/.eslintignore Normal file
View file

@ -0,0 +1,2 @@
js/vendor/*
webpack.config.js

View file

@ -2,16 +2,24 @@ env:
browser: true
es6: true
parser: '@typescript-eslint/parser'
parserOptions:
ecmaVersion: 6
sourceType: module
plugins:
- '@typescript-eslint'
globals:
ga: false
md5: false
Sortable: false
ActionCable: false
extends:
- 'plugin:@typescript-eslint/recommended'
rules:
accessor-pairs: 2
array-bracket-spacing: 0
@ -83,7 +91,6 @@ rules:
no-console: 0
no-const-assign: 2
no-constant-condition: 2
no-continue: 2
no-control-regex: 2
no-debugger: 2
no-delete-var: 2
@ -245,3 +252,10 @@ rules:
wrap-regex: 0
yield-star-spacing: 2
yoda: [2, 'never']
# Disable rules which are impossible to satisfy (types require .ts extension)
overrides:
- files:
- '*.js'
rules:
'@typescript-eslint/explicit-module-boundary-types': 0

View file

@ -1,2 +0,0 @@
vendor/*
match_query.js

View file

@ -81,7 +81,8 @@ function initializeFilters() {
}
function unmarshal(data) {
try { return JSON.parse(data); } catch (_) { return data; }
try { return JSON.parse(data); }
catch (_) { return data; }
}
function loadBooruData() {

View file

@ -31,14 +31,16 @@ const actions = {
disable(data) { selectorCb(data.base, data.value, el => el.disabled = true); },
copy(data) { document.querySelector(data.value).select();
document.execCommand('copy'); },
copy(data) {
document.querySelector(data.value).select();
document.execCommand('copy');
},
inputvalue(data) { document.querySelector(data.value).value = data.el.dataset.setValue; },
selectvalue(data) { document.querySelector(data.value).value = data.el.querySelector(':checked').dataset.setValue; },
checkall(data) { $$(`${data.value} input[type=checkbox]`).forEach(c => { c.checked = !c.checked; }) },
checkall(data) { $$(`${data.value} input[type=checkbox]`).forEach(c => { c.checked = !c.checked; }); },
focus(data) { document.querySelector(data.value).focus(); },
@ -50,8 +52,8 @@ const actions = {
tab(data) {
const block = data.el.parentNode.parentNode,
newTab = $(`.block__tab[data-tab="${data.value}"]`),
loadTab = data.el.dataset.loadTab;
newTab = $(`.block__tab[data-tab="${data.value}"]`),
loadTab = data.el.dataset.loadTab;
// Switch tab
const selectedTab = block.querySelector('.selected');
@ -100,8 +102,8 @@ function matchAttributes(event) {
for (const action in actions) {
const attr = `data-${event.type}-${action.toLowerCase()}`,
el = event.target && event.target.closest(`[${attr}]`),
value = el && el.getAttribute(attr);
el = event.target && event.target.closest(`[${attr}]`),
value = el && el.getAttribute(attr);
if (el) {
// Return true if you don't want to preventDefault

View file

@ -30,10 +30,12 @@ function commentPosted(response) {
if (requestOk) {
response.text().then(text => {
if (text.includes('<div class="flash flash--warning">'))
if (text.includes('<div class="flash flash--warning">')) {
window.location.reload();
else
}
else {
displayComments(container, text);
}
});
}
else {

View file

@ -47,9 +47,8 @@ function selectVersion(imageWidth, imageHeight, imageSize, imageMime) {
if (imageMime === 'video/webm' || imageSize <= 26214400) {
return 'full';
}
else {
return 'large';
}
return 'large';
}
/**

View file

@ -6,9 +6,9 @@ import { fetchJson } from './utils/requests';
import { $ } from './utils/dom';
const endpoints = {
vote(imageId) { return `/images/${imageId}/vote` },
fave(imageId) { return `/images/${imageId}/fave` },
hide(imageId) { return `/images/${imageId}/hide` },
vote(imageId) { return `/images/${imageId}/vote`; },
fave(imageId) { return `/images/${imageId}/fave`; },
hide(imageId) { return `/images/${imageId}/hide`; },
};
const spoilerDownvoteMsg =
@ -34,16 +34,16 @@ function modifyCache(callback) {
cacheEl.value = JSON.stringify(callback(JSON.parse(cacheEl.value)));
}
function cacheStatus(image_id, interaction_type, value) {
function cacheStatus(imageId, interactionType, value) {
modifyCache(cache => {
cache[`${image_id}${interaction_type}`] = { image_id, interaction_type, value };
cache[`${imageId}${interactionType}`] = { imageId, interactionType, value };
return cache;
});
}
function uncacheStatus(image_id, interaction_type) {
function uncacheStatus(imageId, interactionType) {
modifyCache(cache => {
delete cache[`${image_id}${interaction_type}`];
delete cache[`${imageId}${interactionType}`];
return cache;
});
}

View file

@ -168,7 +168,7 @@ function wrapSelection(textarea, options) {
});
}
newText = prefix + newText + suffix
newText = prefix + newText + suffix;
return {
newText,
@ -188,7 +188,7 @@ function wrapLines(textarea, options, eachLine = true) {
: text.split(/\n/g).map(line => prefix + line.trim() + suffix).join('\n');
// Force a space at the end of lines with only blockquote markers
newText = newText.replace(/^((?:>\s+)*)>$/gm, '$1> ')
newText = newText.replace(/^((?:>\s+)*)>$/gm, '$1> ');
return { newText, caretOffset: emptyText ? prefix.length : newText.length };
}, eachLine);

View file

@ -5,18 +5,18 @@
const tokenList = [
['fuzz', /^~(?:\d+(\.\d+)?|\.\d+)/],
['boost', /^\^[\-\+]?\d+(\.\d+)?/],
['quoted_lit', /^\s*"(?:(?:[^"]|\\")+)"/],
['boost', /^\^[-+]?\d+(\.\d+)?/],
['quoted_lit', /^\s*"(?:[^"]|\\")+"/],
['lparen', /^\s*\(\s*/],
['rparen', /^\s*\)\s*/],
['and_op', /^\s*(?:\&\&|AND)\s+/],
['and_op', /^\s*(?:&&|AND)\s+/],
['and_op', /^\s*,\s*/],
['or_op', /^\s*(?:\|\||OR)\s+/],
['not_op', /^\s*NOT(?:\s+|(?=\())/],
['not_op', /^\s*[\!\-]\s*/],
['not_op', /^\s*[!-]\s*/],
['space', /^\s+/],
['word', /^(?:[^\s,\(\)\^~]|\\[\s,\(\)\^~])+/],
['word', /^(?:[^\s,\(\)]|\\[\s,\(\)])+/]
['word', /^(?:[^\s,()^~]|\\[\s,()^~])+/],
['word', /^(?:[^\s,()]|\\[\s,()])+/]
],
numberFields = ['id', 'width', 'height', 'aspect_ratio',
'comment_count', 'score', 'upvotes', 'downvotes',
@ -35,6 +35,7 @@ const tokenList = [
id: 'data-image-id',
width: 'data-width',
height: 'data-height',
/* eslint-disable camelcase */
aspect_ratio: 'data-aspect-ratio',
comment_count: 'data-comment-count',
tag_count: 'data-tag-count',
@ -43,10 +44,11 @@ const tokenList = [
sha512_hash: 'data-sha512',
orig_sha512_hash: 'data-orig-sha512',
created_at: 'data-created-at'
/* eslint-enable camelcase */
};
function SearchTerm(termStr, options) {
function SearchTerm(termStr) {
this.term = termStr.trim();
this.parsed = false;
}
@ -57,8 +59,6 @@ SearchTerm.prototype.append = function(substr) {
};
SearchTerm.prototype.parseRangeField = function(field) {
let qual;
if (numberFields.indexOf(field) !== -1) {
return [field, 'eq', 'number'];
}
@ -67,7 +67,7 @@ SearchTerm.prototype.parseRangeField = function(field) {
return [field, 'eq', 'date'];
}
qual = /^(\w+)\.([lg]te?|eq)$/.exec(field);
const qual = /^(\w+)\.([lg]te?|eq)$/.exec(field);
if (qual) {
if (numberFields.indexOf(qual[1]) !== -1) {
@ -116,28 +116,29 @@ SearchTerm.prototype.parseRelativeDate = function(dateVal, qual) {
}
}
else {
throw `Cannot parse date string: ${dateVal}`;
throw new Error(`Cannot parse date string: ${dateVal}`);
}
};
SearchTerm.prototype.parseAbsoluteDate = function(dateVal, qual) {
let parseRes = [
/^(\d{4})/,
/^\-(\d{2})/,
/^\-(\d{2})/,
/^(?:\s+|T|t)(\d{2})/,
/^:(\d{2})/,
/^:(\d{2})/
],
timeZoneOffset = [0, 0],
timeData = [0, 0, 1, 0, 0, 0],
bottomDate = null,
topDate = null,
const parseRes = [
/^(\d{4})/,
/^-(\d{2})/,
/^-(\d{2})/,
/^(?:\s+|T|t)(\d{2})/,
/^:(\d{2})/,
/^:(\d{2})/
],
timeZoneOffset = [0, 0],
timeData = [0, 0, 1, 0, 0, 0],
origDateVal = dateVal;
let topDate = null,
i,
match,
origDateVal = dateVal;
bottomDate = null,
localDateVal = origDateVal;
match = /([\+\-])(\d{2}):(\d{2})$/.exec(dateVal);
match = /([+-])(\d{2}):(\d{2})$/.exec(localDateVal);
if (match) {
timeZoneOffset[0] = parseInt(match[2], 10);
timeZoneOffset[1] = parseInt(match[3], 10);
@ -145,18 +146,18 @@ SearchTerm.prototype.parseAbsoluteDate = function(dateVal, qual) {
timeZoneOffset[0] *= -1;
timeZoneOffset[1] *= -1;
}
dateVal = dateVal.substr(0, dateVal.length - 6);
localDateVal = localDateVal.substr(0, localDateVal.length - 6);
}
else {
dateVal = dateVal.replace(/[Zz]$/, '');
localDateVal = localDateVal.replace(/[Zz]$/, '');
}
for (i = 0; i < parseRes.length; i += 1) {
if (dateVal.length === 0) {
if (localDateVal.length === 0) {
break;
}
match = parseRes[i].exec(dateVal);
match = parseRes[i].exec(localDateVal);
if (match) {
if (i === 1) {
timeData[i] = parseInt(match[1], 10) - 1;
@ -164,17 +165,17 @@ SearchTerm.prototype.parseAbsoluteDate = function(dateVal, qual) {
else {
timeData[i] = parseInt(match[1], 10);
}
dateVal = dateVal.substr(
match[0].length, dateVal.length - match[0].length
localDateVal = localDateVal.substr(
match[0].length, localDateVal.length - match[0].length
);
}
else {
throw `Cannot parse date string: ${origDateVal}`;
throw new Error(`Cannot parse date string: ${origDateVal}`);
}
}
if (dateVal.length > 0) {
throw `Cannot parse date string: ${origDateVal}`;
if (localDateVal.length > 0) {
throw new Error(`Cannot parse date string: ${origDateVal}`);
}
// Apply the user-specified time zone offset. The JS Date constructor
@ -210,9 +211,8 @@ SearchTerm.prototype.parseDate = function(dateVal, qual) {
}
};
SearchTerm.prototype.parse = function(substr) {
let matchArr,
rangeParsing,
SearchTerm.prototype.parse = function() {
let rangeParsing,
candidateTermSpace,
termCandidate;
@ -230,7 +230,7 @@ SearchTerm.prototype.parse = function(substr) {
this.termSpace = 'tags';
this.termType = 'literal';
matchArr = this.term.split(':');
const matchArr = this.term.split(':');
if (matchArr.length > 1) {
candidateTermSpace = matchArr[0];
@ -258,7 +258,7 @@ SearchTerm.prototype.parse = function(substr) {
this.term = termCandidate;
this.termSpace = candidateTermSpace;
}
else if (candidateTermSpace == 'my') {
else if (candidateTermSpace === 'my') {
this.termType = 'my';
this.termSpace = termCandidate;
}
@ -285,9 +285,9 @@ SearchTerm.prototype.parse = function(substr) {
SearchTerm.prototype._normalizeTerm = function() {
if (!this.wildcardable) {
return this.term.replace('\"', '"');
return this.term.replace('"', '"');
}
return this.term.replace(/\\([^\*\?])/g, '$1');
return this.term.replace(/\\([^*?])/g, '$1');
};
SearchTerm.prototype.fuzzyMatch = function(targetStr) {
@ -309,16 +309,16 @@ SearchTerm.prototype.fuzzyMatch = function(targetStr) {
targetDistance = this.fuzz;
}
targetStr = targetStr.toLowerCase();
const targetStrLower = targetStr.toLowerCase();
for (i = 0; i <= targetStr.length; i += 1) {
for (i = 0; i <= targetStrLower.length; i += 1) {
v1.push(i);
}
for (i = 0; i < this.term.length; i += 1) {
v2[0] = i;
for (j = 0; j < targetStr.length; j += 1) {
const cost = this.term[i] === targetStr[j] ? 0 : 1;
for (j = 0; j < targetStrLower.length; j += 1) {
const cost = this.term[i] === targetStrLower[j] ? 0 : 1;
v2[j + 1] = Math.min(
// Deletion.
v1[j + 1] + 1,
@ -327,8 +327,8 @@ SearchTerm.prototype.fuzzyMatch = function(targetStr) {
// Substitution or No Change.
v1[j] + cost
);
if (i > 1 && j > 1 && this.term[i] === targetStr[j - 1] &&
targetStr[i - 1] === targetStr[j]) {
if (i > 1 && j > 1 && this.term[i] === targetStrLower[j - 1] &&
targetStrLower[i - 1] === targetStrLower[j]) {
v2[j + 1] = Math.min(v2[j], v0[j - 1] + cost);
}
}
@ -339,7 +339,7 @@ SearchTerm.prototype.fuzzyMatch = function(targetStr) {
v2 = temp;
}
return v1[targetStr.length] <= targetDistance;
return v1[targetStrLower.length] <= targetDistance;
};
SearchTerm.prototype.exactMatch = function(targetStr) {
@ -354,10 +354,8 @@ SearchTerm.prototype.interactionMatch = function(imageID, type, interaction, int
let ret = false;
interactions.forEach(v => {
if (v.image_id == imageID && v.interaction_type == type && (interaction == null || v.value == interaction)) {
if (v.image_id === imageID && v.interaction_type === type && (interaction === null || v.value === interaction)) {
ret = true;
return;
}
});
@ -365,8 +363,9 @@ SearchTerm.prototype.interactionMatch = function(imageID, type, interaction, int
};
SearchTerm.prototype.match = function(target) {
// eslint-disable-next-line @typescript-eslint/no-this-alias,consistent-this
const ohffs = this;
let ret = false,
ohffs = this,
compFunc,
numbuh,
date;
@ -427,9 +426,9 @@ SearchTerm.prototype.match = function(target) {
}
else if (this.termType === 'date') {
// Date matching.
date = (new Date(
date = new Date(
target.getAttribute(termSpaceToImageField[this.termSpace])
)).getTime();
).getTime();
switch (this.compare) {
// The open-left, closed-right date range specified by the
@ -481,23 +480,25 @@ SearchTerm.prototype.match = function(target) {
return ret;
};
function generateLexArray(searchStr, options) {
let opQueue = [],
searchTerm = null,
function generateLexArray(searchStr) {
const opQueue = [],
groupNegate = [],
tokenStack = [];
let searchTerm = null,
boost = null,
fuzz = null,
lparenCtr = 0,
negate = false,
groupNegate = [],
tokenStack = [],
boostFuzzStr = '';
boostFuzzStr = '',
localSearchStr = searchStr;
while (searchStr.length > 0) {
while (localSearchStr.length > 0) {
// eslint-disable-next-line no-loop-func
tokenList.every(tokenArr => {
let tokenName = tokenArr[0],
tokenRE = tokenArr[1],
match = tokenRE.exec(searchStr),
balanced, op;
const tokenName = tokenArr[0],
tokenRE = tokenArr[1];
let match = tokenRE.exec(localSearchStr),
op;
if (match) {
match = match[0];
@ -560,15 +561,18 @@ function generateLexArray(searchStr, options) {
break;
case 'rparen':
if (lparenCtr > 0) {
searchTerm.append(match);
if (searchTerm) {
searchTerm.append(match);
}
else {
searchTerm = new SearchTerm(match);
}
lparenCtr -= 1;
}
else {
balanced = false;
while (opQueue.length) {
op = opQueue.shift();
if (op === 'lparen') {
balanced = true;
break;
}
tokenStack.push(op);
@ -587,7 +591,7 @@ function generateLexArray(searchStr, options) {
boostFuzzStr += match;
}
else {
searchTerm = new SearchTerm(match, options);
searchTerm = new SearchTerm(match);
}
break;
case 'boost':
@ -596,7 +600,7 @@ function generateLexArray(searchStr, options) {
boostFuzzStr += match;
}
else {
searchTerm = new SearchTerm(match, options);
searchTerm = new SearchTerm(match);
}
break;
case 'quoted_lit':
@ -604,7 +608,7 @@ function generateLexArray(searchStr, options) {
searchTerm.append(match);
}
else {
searchTerm = new SearchTerm(match, options);
searchTerm = new SearchTerm(match);
}
break;
case 'word':
@ -617,7 +621,7 @@ function generateLexArray(searchStr, options) {
searchTerm.append(match);
}
else {
searchTerm = new SearchTerm(match, options);
searchTerm = new SearchTerm(match);
}
break;
default:
@ -628,8 +632,8 @@ function generateLexArray(searchStr, options) {
}
// Truncate string and restart the token tests.
searchStr = searchStr.substr(
match.length, searchStr.length - match.length
localSearchStr = localSearchStr.substr(
match.length, localSearchStr.length - match.length
);
// Break since we have found a match.
@ -652,7 +656,7 @@ function generateLexArray(searchStr, options) {
if (opQueue.indexOf('rparen') !== -1 ||
opQueue.indexOf('lparen') !== -1) {
throw 'Mismatched parentheses.';
throw new Error('Mismatched parentheses.');
}
// Memory-efficient concatenation of remaining operators queue to the
@ -663,8 +667,8 @@ function generateLexArray(searchStr, options) {
}
function parseTokens(lexicalArray) {
let operandStack = [],
negate, op1, op2, parsed;
const operandStack = [];
let negate, op1, op2;
lexicalArray.forEach((token, i) => {
if (token !== 'not_op') {
negate = lexicalArray[i + 1] === 'not_op';
@ -674,7 +678,7 @@ function parseTokens(lexicalArray) {
op1 = operandStack.pop();
if (typeof op1 === 'undefined' || typeof op2 === 'undefined') {
throw 'Missing operand.';
throw new Error('Missing operand.');
}
operandStack.push(new SearchAST(token, negate, op1, op2));
@ -691,7 +695,7 @@ function parseTokens(lexicalArray) {
});
if (operandStack.length > 1) {
throw 'Missing operator.';
throw new Error('Missing operator.');
}
op1 = operandStack.pop();
@ -707,8 +711,8 @@ function parseTokens(lexicalArray) {
return op1;
}
function parseSearch(searchStr, options) {
return parseTokens(generateLexArray(searchStr, options));
function parseSearch(searchStr) {
return parseTokens(generateLexArray(searchStr));
}
function isTerminal(operand) {
@ -724,25 +728,27 @@ function SearchAST(op, negate, leftOperand, rightOperand) {
}
function combineOperands(ast1, ast2, parentAST) {
let localAst1;
if (parentAST.op === 'and_op') {
ast1 = ast1 && ast2;
localAst1 = ast1 && ast2;
}
else {
ast1 = ast1 || ast2;
localAst1 = ast1 || ast2;
}
if (parentAST.negate) {
return !ast1;
return !localAst1;
}
return ast1;
return localAst1;
}
// Evaluation of the AST in regard to a target image
SearchAST.prototype.hitsImage = function(image) {
let treeStack = [],
// Left side node.
ast1 = this,
const treeStack = [];
// Left side node.
// eslint-disable-next-line @typescript-eslint/no-this-alias,consistent-this
let ast1 = this,
// Right side node.
ast2,
// Parent node of the current subtree.
@ -834,9 +840,9 @@ SearchAST.prototype.hitsImage = function(image) {
SearchAST.prototype.dumpTree = function() {
// Dumps to string a simple diagram of the syntax tree structure
// (starting with this object as the root) for debugging purposes.
let retStrArr = [],
treeQueue = [['', this]],
treeArr,
const retStrArr = [],
treeQueue = [['', this]];
let treeArr,
prefix,
tree;

View file

@ -8,7 +8,7 @@ import { delegate } from './utils/events';
import store from './utils/store';
const NOTIFICATION_INTERVAL = 600000,
NOTIFICATION_EXPIRES = 300000;
NOTIFICATION_EXPIRES = 300000;
function makeRequest(verb) {
return fetchJson(verb, '/notifications/unread').then(handleError);
@ -17,7 +17,7 @@ function makeRequest(verb) {
function bindSubscriptionLinks() {
delegate(document, 'fetchcomplete', {
'.js-subscription-link': event => {
const target = $("#js-subscription-target");
const target = $('#js-subscription-target');
event.detail.text().then(text => {
target.outerHTML = text;
});

View file

@ -1,4 +1,4 @@
import { $, $$, clearEl, removeEl, insertBefore } from './utils/dom';
import { $, $$, removeEl} from './utils/dom';
import { delegate, leftClick } from './utils/events';
function pollOptionRemover(_event, target) {
@ -28,7 +28,7 @@ function pollOptionCreator() {
const newHtml = prevFieldCopy.outerHTML.replace(/(\d+)/g, `${existingOptionCount}`);
// Insert copy before the button
addPollOptionButton.insertAdjacentHTML("beforebegin", newHtml);
addPollOptionButton.insertAdjacentHTML('beforebegin', newHtml);
existingOptionCount++;
}

View file

@ -90,11 +90,11 @@ function setupPreviews() {
const getCacheKey = () => {
return (previewAnon && previewAnon.checked ? 'anon;' : '') + textarea.value;
}
};
const previewedTextAttribute = 'data-previewed-text';
const updatePreview = () => {
const cachedValue = getCacheKey()
const cachedValue = getCacheKey();
if (previewContent.getAttribute(previewedTextAttribute) === cachedValue) return;
previewContent.setAttribute(previewedTextAttribute, cachedValue);

View file

@ -41,8 +41,8 @@ const keyCodes = {
82() { click('.js-rand'); }, // R - go to random image
83() { click('.js-source-link'); }, // S - go to image source
76() { click('.js-tag-sauce-toggle'); }, // L - edit tags
79() { openFullView() }, // O - open original
86() { openFullViewNewTab() }, // V - open original in a new tab
79() { openFullView(); }, // O - open original
86() { openFullViewNewTab(); }, // V - open original in a new tab
70() { // F - favourite image
getHover() ? click(`a.interaction--fave[data-image-id="${getHover()}"]`)
: click('.block__header a.interaction--fave');

View file

@ -7,7 +7,7 @@
import { $$ } from './utils/dom';
function hideStaffTools() {
if (window.booru.hideStaffTools == "true") {
if (window.booru.hideStaffTools === 'true') {
$$('.js-staff-action').forEach(el => {
el.classList.add('hidden');
});

View file

@ -102,7 +102,7 @@ function setupTagsInput(tagBlock) {
if (name.length === 0 || tags.indexOf(name) !== -1) return;
// Remove instead if the tag name starts with a minus
if (name[0] === "-") {
if (name[0] === '-') {
name = name.slice(1); // eslint-disable-line no-param-reassign
const tagLink = $(`[data-tag-name="${escapeCss(name)}"]`, container);

View file

@ -58,11 +58,11 @@ function formRemote(event, target) {
headers: headers(),
body: new FormData(target)
}).then(response => {
if (response && response.status == 300) {
if (response && response.status === 300) {
window.location.reload(true);
return;
}
fire(target, 'fetchcomplete', response)
fire(target, 'fetchcomplete', response);
});
}

View file

@ -122,7 +122,7 @@ function setupImageUpload() {
});
// Fetch on "enter" in url field
remoteUrl.addEventListener("keydown", function(event) {
remoteUrl.addEventListener('keydown', event => {
if (event.keyCode === 13) { // Hit enter
fetchButton.click();
}

1792
assets/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,28 @@
{
"scripts": {
"deploy": "cross-env NODE_ENV=production webpack",
"lint": "eslint . --ext .js,.ts",
"watch": "webpack --watch"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.4",
"brunch": "^3.0.0",
"copycat-brunch": "^1.1.1",
"postcss": "^8.3.7"
"postcss": "^8.3.7",
"tslib": "^2.3.1"
},
"devDependencies": {
"@rollup/plugin-typescript": "^8.2.5",
"@typescript-eslint/eslint-plugin": "^4.32.0",
"@typescript-eslint/parser": "^4.32.0",
"acorn": "^7.4.1",
"autoprefixer": "^10.3.5",
"copy-webpack-plugin": "^6.4.1",
"cross-env": "^7.0.3",
"css-loader": "^5.2.7",
"css-minimizer-webpack-plugin": "^2.0.0",
"eslint": "^7.32.0",
"eslint-webpack-plugin": "^3.0.1",
"file-loader": "^6.2.0",
"ignore-emit-webpack-plugin": "^2.0.6",
"mini-css-extract-plugin": "^2.3.0",
@ -33,6 +40,7 @@
"source-map-support": "^0.5.20",
"style-loader": "^1.3.0",
"terser-webpack-plugin": "^3.1.0",
"typescript": "^4.4",
"webpack": "^5.53.0",
"webpack-cli": "^4.8.0",
"webpack-rollup-loader": "^0.8.0"

9
assets/tsconfig.json Normal file
View file

@ -0,0 +1,9 @@
{
"compilerOptions": {
"noEmit": true,
"baseUrl": "./js",
"target": "ES2018",
"allowJs": true,
"strict": true
}
}

View file

@ -5,12 +5,14 @@ const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const IgnoreEmitPlugin = require('ignore-emit-webpack-plugin');
const ESLintPlugin = require('eslint-webpack-plugin');
const isDevelopment = process.env.NODE_ENV !== 'production';
const includePaths = require('rollup-plugin-includepaths')();
const multiEntry = require('rollup-plugin-multi-entry')();
const buble = require('rollup-plugin-buble')({ transforms: { dangerousForOf: true } });
const typescript = require('@rollup/plugin-typescript')();
let plugins = [
new IgnoreEmitPlugin(/css\/.*(?<!css)$/),
@ -24,7 +26,16 @@ let plugins = [
],
}),
];
if (!isDevelopment){
if (isDevelopment) {
plugins = plugins.concat([
new ESLintPlugin({
extensions: ['js', 'ts'],
failOnError: true,
failOnWarning: isDevelopment
})
]);
}
else {
plugins = plugins.concat([
new TerserPlugin({
cache: true,
@ -90,6 +101,7 @@ module.exports = {
buble,
includePaths,
multiEntry,
typescript,
]
}
},