Skip to content

Add TS type check action #1600

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 10 commits into
base: develop
Choose a base branch
from
26 changes: 26 additions & 0 deletions .github/workflows/typescript.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: TypeScript check

on:
push:
branches:
- develop
pull_request:
branches:
- develop

jobs:
typescript:
name: TypeScript type checking
runs-on: ubuntu-latest

steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Install Node
uses: actions/setup-node@v1
with:
node-version: 22
- name: npm install
run: npm install
- name: tsc
run: tsc
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
save-exact = true
6 changes: 3 additions & 3 deletions app/assets/javascripts/categories.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ $(() => {
$caption.find('[data-state="absent"]').hide();
$caption.find('[data-state="present"]').show();

$el.find('.js-tag-select').attr('data-tag-set', tagSetId).attr('disabled', false);
$el.find('.js-tag-select').attr('data-tag-set', tagSetId).attr('disabled', null);
});
}
else {
Expand All @@ -20,15 +20,15 @@ $(() => {
$caption.find('[data-state="absent"]').show();
$caption.find('[data-state="present"]').hide();

$el.find('.js-tag-select').attr('data-tag-set', null).attr('disabled', true);
$el.find('.js-tag-select').attr('data-tag-set', null).attr('disabled', 'true');
});
}
});

$('.js-add-required-topic').on('click', (_ev) => {
const $required = $('.js-required-tags');
const $topic = $('.js-topic-tags');
const union = ($required.val() || []).concat($topic.val() || []);
const union = /** @type {string[]} */($required.val() || []).concat(/** @type {string[]} */ ($topic.val() || []));

const options = $topic.find('option').toArray();
const optionIds = options.map((x) => $(x).attr('value'));
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/character_count.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ $(() => {
*/
const setSubmitButtonDisabledState = (el, state) => {
const isDisabled = state === 'disabled';
el.attr('disabled', isDisabled).toggleClass('is-muted', isDisabled);
el.attr('disabled', isDisabled ? 'true' : null).toggleClass('is-muted', isDisabled);
};

$(document).on('keyup change paste', '[data-character-count]', (ev) => {
Expand Down
2 changes: 2 additions & 0 deletions app/assets/javascripts/closure.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ document.addEventListener('DOMContentLoaded', async () => {
ev.preventDefault();

const self = /** @type {HTMLElement} */(ev.target);
/** @type {HTMLInputElement} */
const activeRadio = self.closest('.js-close-box').querySelector("input[type='radio'][name='close-reason']:checked");

if (!activeRadio) {
QPixel.createNotification('danger', 'You must select a close reason.');
return;
}

/** @type {HTMLInputElement} */
const otherPostInput = activeRadio.closest('.widget--body').querySelector('.js-close-other-post');
const otherPostRequired = activeRadio.dataset.rop === 'true';
const data = {
Expand Down
32 changes: 21 additions & 11 deletions app/assets/javascripts/filters.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$(() => {
$('.js-filter-select').each(async (_, el) => {
$('.js-filter-select').toArray().forEach(async (el) => {
const $select = $(el);
const $form = $select.closest('form');
const $formFilters = $form.find('.form--filter');
Expand Down Expand Up @@ -35,7 +35,7 @@ $(() => {

const hasChanges = [...$formFilters].some((el) => {
const filterValue = filter[el.dataset.name];
let elValue = $(el).val();
let elValue = /** @type {string | undefined[]} */ ($(el).val());
if (filterValue?.constructor == Array) {
elValue = elValue ?? [];
return filterValue.length != elValue.length || filterValue.some((v, i) => v[1] != elValue[i]);
Expand Down Expand Up @@ -68,14 +68,23 @@ $(() => {
}

// Clear out any old options
$select.children().filter((_, option) => option.value && !filters[option.value]).detach();
$select.children().filter((_, /** @type{HTMLOptionElement} */ option) => {
return option.value && !filters[option.value];
}).detach();

$select.select2({
data: Object.keys(filters),
data: Object.keys(filters).map((filterName) => {
return {
id: filterName,
text: filterName
}
}),
tags: true,

templateResult: template,
templateSelection: template
}).on('select2:select', async (evt) => {
});

$select.on('select2:select', /** @type {(event: Select2.Event) => void} */ (async (evt) => {
const filterName = evt.params.data.id;
const preset = filters[filterName];

Expand All @@ -92,14 +101,15 @@ $(() => {
if (value?.constructor == Array) {
$el.val(null);
for (const val of value) {
$el.append(new Option(val[0], val[1], false, true));
$el.append(new Option(val[0], val[1].toString(), false, true));
}
$el.trigger('change');
} else {
$el.val(value).trigger('change');
}
else {
$el.val(/** @type {string} */ (value)).trigger('change');
}
}
});
}));
computeEnables();
}

Expand Down Expand Up @@ -145,4 +155,4 @@ $(() => {

$form.find('.filter-clear').on('click', clear);
});
});
});
6 changes: 5 additions & 1 deletion app/assets/javascripts/licenses.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ $(() => {
});

$('.js-license-select').select2({
/**
* @param {Select2.DataFormat & { element?: HTMLOptionElement }} option
* @returns {string | JQuery}
*/
templateResult: (option) => {
if (option.element) {
const title = $(option.element).attr('data-title');
Expand All @@ -31,4 +35,4 @@ $(() => {
}
}
});
});
});
3 changes: 3 additions & 0 deletions app/assets/javascripts/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ $(() => {
const $tgt = $(ev.target);
const $button = $tgt.is('a') ? $tgt : $tgt.parents('a');
const action = $button.attr('data-action');
/** @type {JQuery<HTMLInputElement>} */
const $field = $('.js-post-field');

const actions = {
Expand Down Expand Up @@ -92,6 +93,7 @@ $(() => {
const $url = $('#markdown-link-url');
const url = $url.val();
const markdown = `[${text}](${url})`;
/** @type {JQuery<HTMLInputElement>} */
const $field = $('.js-post-field');

if ($field[0].selectionStart != null && $field[0].selectionStart !== $field[0].selectionEnd) {
Expand All @@ -108,6 +110,7 @@ $(() => {
});

$(document).on('click', '[data-modal="#markdown-link-insert"]', (_ev) => {
/** @type {JQuery<HTMLInputElement>} */
const $field = $('.js-post-field');
const selection = $field.val().substring($field[0].selectionStart, $field[0].selectionEnd);
if (selection) {
Expand Down
4 changes: 2 additions & 2 deletions app/assets/javascripts/micro_auth/apps.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
document.addEventListener('DOMContentLoaded', () => {
QPixel.DOM.addSelectorListener('click', '.js-copy-key', (ev) => {
const label = ev.target.closest('label');
const field = document.querySelector(`#${label.getAttribute('for')}`);
const label = /** @type {HTMLElement} */ (ev.target).closest('label');
const field = /** @type {HTMLInputElement} */ (document.querySelector(`#${label.getAttribute('for')}`));
navigator.clipboard.writeText(field.value);
field.focus();
field.setSelectionRange(0, field.value.length);
Expand Down
5 changes: 3 additions & 2 deletions app/assets/javascripts/mod_warning.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
document.addEventListener('DOMContentLoaded', () => {
QPixel.DOM.addSelectorListener('input', '.js--warning-template-selection', (ev) => {
const tgt = ev.target;
document.querySelector('.js--warning-template-target textarea').value = atob(tgt.value);
const tgt = /** @type {HTMLInputElement} */ (ev.target);
const input = /** @type {HTMLInputElement} */ (document.querySelector('.js--warning-template-target textarea'));
input.value = atob(tgt.value);
});
});
7 changes: 5 additions & 2 deletions app/assets/javascripts/moderator.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ $(() => {
ev.preventDefault();
const tgt = /** @type {HTMLElement} */(ev.target);
const id = tgt.dataset.flagId;

const resolveCommentElem = tgt.parentNode?.parentNode?.querySelector('.flag-resolve-comment');

const data = {
result: tgt.dataset.result,
message: tgt.parentNode.parentNode.querySelector('.flag-resolve-comment').value
message: resolveCommentElem instanceof HTMLTextAreaElement ? resolveCommentElem.value : ''
};

const req = await QPixel.jsonPost(`/mod/flags/${id}/resolve`, data);
Expand All @@ -34,4 +37,4 @@ $(() => {
QPixel.createNotification('danger', `<strong>Failed:</strong> Unexpected status (${req.status})`);
}
});
});
});
29 changes: 16 additions & 13 deletions app/assets/javascripts/posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ $(() => {
const placeholder = "![Uploading, please wait...]()";

$uploadForm.find('input[type="file"]').on('change', async (evt) => {
const $postField = $('.js-post-field');
const postText = $postField.val();
const cursorPos = $postField[0].selectionStart;
/** @type {HTMLInputElement} */
const postField = document.querySelector('.js-post-field');
const postText = postField.value;
const cursorPos = postField.selectionStart;

$postField.val(stringInsert(postText, cursorPos, placeholder));
postField.value = stringInsert(postText, cursorPos, placeholder);

const $tgt = $(evt.target);
const $form = $tgt.parents('form');
$form.submit();
const tgt = evt.target;
const form = /** @type {HTMLFormElement} **/ (tgt.parentElement.parentElement);
form.submit();
});

$uploadForm.on('submit', async (evt) => {
Expand All @@ -45,7 +46,7 @@ $(() => {
const $tgt = $(evt.target);

const $fileInput = $tgt.find('input[type="file"]');
const files = $fileInput[0].files;
const files = /** @type {HTMLInputElement} */ ($fileInput[0]).files;
if (files.length > 0 && files[0].size >= 2000000) {
$tgt.find('.js-max-size').addClass('has-color-red-700 error-shake');
const postField = $('.js-post-field');
Expand All @@ -61,7 +62,7 @@ $(() => {

const resp = await fetch($tgt.attr('action'), {
method: $tgt.attr('method'),
body: new FormData($tgt[0])
body: new FormData(/** @type {HTMLFormElement} */ ($tgt[0]))
});
const data = await resp.json();
if (resp.status === 200) {
Expand All @@ -74,7 +75,7 @@ $(() => {

$uploadForm.on('ajax:success', async (evt, data) => {
const $tgt = $(evt.target);
$tgt[0].reset();
/** @type {HTMLFormElement} */ ($tgt[0]).reset();

const $postField = $('.js-post-field');
const postText = $postField.val()?.toString();
Expand Down Expand Up @@ -210,9 +211,11 @@ $(() => {
});

postFields.on('paste', async (evt) => {
if (evt.originalEvent.clipboardData.files.length > 0) {
const eventData = /** @type {ClipboardEvent} */ (evt.originalEvent);
if (eventData.clipboardData.files.length > 0) {
/** @type {JQuery<HTMLInputElement>} */
const $fileInput = $uploadForm.find('input[type="file"]');
$fileInput[0].files = evt.originalEvent.clipboardData.files;
$fileInput[0].files = eventData.clipboardData.files;
$fileInput.trigger('change');
}
});
Expand Down Expand Up @@ -355,7 +358,7 @@ $(() => {
}

setTimeout(() => {
$tgt.find('input[type="submit"]').attr('disabled', false);
$tgt.find('input[type="submit"]').attr('disabled', null);
}, 1000);
}
});
Expand Down
3 changes: 2 additions & 1 deletion app/assets/javascripts/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ $(() => {
await QPixel.setPreference(prefName, value, community);
});

$('.item-list--item').find('.badge.is-tag').each(async (_i, e) => {
// We're not using jQuery .each() because it (& TypeScript) has problems accepting async functions.
$('.item-list--item').find('.badge.is-tag').toArray().forEach(async e => {
const prefValue = await QPixel.preference('favorite_tags', true);
if (!prefValue) {
return;
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/privileges.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ $(() => {
const value = data[`${type}_score_threshold`];

const form = editField.clone().val(value ? value.toString() : '').attr('data-name', name).attr('data-type', type);
$tgt.addClass('editing').html(form).append(`<button class="button is-filled js-privilege-submit">Update</button>`);
$tgt.addClass('editing').html(form.html()).append(`<button class="button is-filled js-privilege-submit">Update</button>`);
});

$(document).on('click', '.js-privilege-submit', async (evt) => {
Expand Down
3 changes: 1 addition & 2 deletions app/assets/javascripts/tag_sets.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ $(() => {

$(document).on('click', '.js-edit-name-submit', async (ev) => {
const $tgt = $(ev.target);
console.log($tgt);
const tagSetId = $tgt.data('set-id');
const $name = $tgt.parents('.js-tag-set-name');
const newName = $tgt.parent().children('.js-edit-set-name').val();
Expand All @@ -42,4 +41,4 @@ $(() => {
QPixel.createNotification('danger', `Failed to change name (${response.status})`);
}
});
});
});
Loading