mirror of
https://github.com/thelounge/thelounge.git
synced 2024-05-21 07:36:38 +02:00
some test fixes
This commit is contained in:
parent
b798cfdc64
commit
4c98b81e35
|
@ -1,3 +1,4 @@
|
||||||
public/
|
public/
|
||||||
coverage/
|
coverage/
|
||||||
src/dist/
|
src/dist/
|
||||||
|
dist/
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,3 +8,4 @@ coverage/
|
||||||
public/
|
public/
|
||||||
client/dist
|
client/dist
|
||||||
src/dist
|
src/dist
|
||||||
|
dist/
|
||||||
|
|
|
@ -4,6 +4,7 @@ test/fixtures/.thelounge/logs/
|
||||||
test/fixtures/.thelounge/certificates/
|
test/fixtures/.thelounge/certificates/
|
||||||
test/fixtures/.thelounge/storage/
|
test/fixtures/.thelounge/storage/
|
||||||
src/dist/
|
src/dist/
|
||||||
|
dist/
|
||||||
*.log
|
*.log
|
||||||
*.png
|
*.png
|
||||||
*.svg
|
*.svg
|
||||||
|
|
|
@ -195,7 +195,7 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script lang="ts">
|
<script>
|
||||||
import Mousetrap from "mousetrap";
|
import Mousetrap from "mousetrap";
|
||||||
import Draggable from "vuedraggable";
|
import Draggable from "vuedraggable";
|
||||||
import {filter as fuzzyFilter} from "fuzzy";
|
import {filter as fuzzyFilter} from "fuzzy";
|
||||||
|
@ -209,10 +209,7 @@ import isIgnoredKeybind from "../js/helpers/isIgnoredKeybind";
|
||||||
import distance from "../js/helpers/distance";
|
import distance from "../js/helpers/distance";
|
||||||
import eventbus from "../js/eventbus";
|
import eventbus from "../js/eventbus";
|
||||||
|
|
||||||
import NetworkModel from "../../src/models/network";
|
export default Vue.extend({
|
||||||
import ChannelMode from "../../src/models/chan";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "NetworkList",
|
name: "NetworkList",
|
||||||
components: {
|
components: {
|
||||||
JoinChannel,
|
JoinChannel,
|
||||||
|
@ -484,5 +481,5 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -274,10 +274,6 @@ function fuzzyGrep<T>(term: string, array: Array<T>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function rawNicks() {
|
function rawNicks() {
|
||||||
if (!store.state.activeChannel) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (store.state.activeChannel.channel.users.length > 0) {
|
if (store.state.activeChannel.channel.users.length > 0) {
|
||||||
const users = store.state.activeChannel.channel.users.slice();
|
const users = store.state.activeChannel.channel.users.slice();
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import store from "../store";
|
||||||
function input() {
|
function input() {
|
||||||
const messageIds = [];
|
const messageIds = [];
|
||||||
|
|
||||||
for (const message of store.state.activeChannel.channel.messages) {
|
for (const message of store.state.activeChannel?.channel.messages) {
|
||||||
let toggled = false;
|
let toggled = false;
|
||||||
|
|
||||||
for (const preview of message.previews) {
|
for (const preview of message.previews) {
|
||||||
|
@ -22,7 +22,7 @@ function input() {
|
||||||
// Tell the server we're toggling so it remembers at page reload
|
// Tell the server we're toggling so it remembers at page reload
|
||||||
if (!document.body.classList.contains("public") && messageIds.length > 0) {
|
if (!document.body.classList.contains("public") && messageIds.length > 0) {
|
||||||
socket.emit("msg:preview:toggle", {
|
socket.emit("msg:preview:toggle", {
|
||||||
target: store.state.activeChannel.channel.id,
|
target: store.state.activeChannel?.channel.id,
|
||||||
messageIds: messageIds,
|
messageIds: messageIds,
|
||||||
shown: false,
|
shown: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,7 +22,7 @@ function input() {
|
||||||
// Tell the server we're toggling so it remembers at page reload
|
// Tell the server we're toggling so it remembers at page reload
|
||||||
if (!document.body.classList.contains("public") && messageIds.length > 0) {
|
if (!document.body.classList.contains("public") && messageIds.length > 0) {
|
||||||
socket.emit("msg:preview:toggle", {
|
socket.emit("msg:preview:toggle", {
|
||||||
target: store.state.activeChannel.channel.id,
|
target: store.state.activeChannel?.channel.id,
|
||||||
messageIds: messageIds,
|
messageIds: messageIds,
|
||||||
shown: true,
|
shown: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
// directory, so we iterate over its content, which is a map statically built by
|
// directory, so we iterate over its content, which is a map statically built by
|
||||||
// Webpack.
|
// Webpack.
|
||||||
// Second argument says it's recursive, third makes sure we only load javascript.
|
// Second argument says it's recursive, third makes sure we only load javascript.
|
||||||
const commands = require.context("./", true, /\.js$/);
|
const commands = require.context("./", true, /\.ts$/);
|
||||||
|
|
||||||
export default commands.keys().reduce((acc, path) => {
|
export default commands.keys().reduce<Record<string, unknown>>((acc, path) => {
|
||||||
const command = path.substring(2, path.length - 3);
|
const command = path.substring(2, path.length - 3);
|
||||||
|
|
||||||
if (command === "index") {
|
if (command === "index") {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import socket from "../socket";
|
import socket from "../socket";
|
||||||
import eventbus from "../eventbus";
|
import eventbus from "../eventbus";
|
||||||
|
import type {ClientChan, ClientNetwork} from "../types";
|
||||||
|
import type {Methods} from "../vue";
|
||||||
type ContextMenuItem =
|
type ContextMenuItem =
|
||||||
| ({
|
| ({
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -18,7 +19,11 @@ type ContextMenuItem =
|
||||||
type: "divider";
|
type: "divider";
|
||||||
};
|
};
|
||||||
|
|
||||||
export function generateChannelContextMenu($root, channel, network) {
|
export function generateChannelContextMenu(
|
||||||
|
$root: Methods,
|
||||||
|
channel: ClientChan,
|
||||||
|
network: ClientNetwork
|
||||||
|
) {
|
||||||
const typeMap = {
|
const typeMap = {
|
||||||
lobby: "network",
|
lobby: "network",
|
||||||
channel: "chan",
|
channel: "chan",
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import {ParsedStyle} from "./parseStyle";
|
|
||||||
|
|
||||||
// Return true if any section of "a" or "b" parts (defined by their start/end
|
// Return true if any section of "a" or "b" parts (defined by their start/end
|
||||||
|
|
||||||
|
import {Part} from "./merge";
|
||||||
|
|
||||||
// markers) intersect each other, false otherwise.
|
// markers) intersect each other, false otherwise.
|
||||||
function anyIntersection(a: ParsedStyle, b: ParsedStyle) {
|
function anyIntersection(a: Part, b: Part) {
|
||||||
return (
|
return (
|
||||||
(a.start <= b.start && b.start < a.end) ||
|
(a.start <= b.start && b.start < a.end) ||
|
||||||
(a.start < b.end && b.end <= a.end) ||
|
(a.start < b.end && b.end <= a.end) ||
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const matchFormatting =
|
const matchFormatting =
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
/\x02|\x1D|\x1F|\x16|\x0F|\x11|\x1E|\x03(?:[0-9]{1,2}(?:,[0-9]{1,2})?)?|\x04(?:[0-9a-f]{6}(?:,[0-9a-f]{6})?)?/gi;
|
/\x02|\x1D|\x1F|\x16|\x0F|\x11|\x1E|\x03(?:[0-9]{1,2}(?:,[0-9]{1,2})?)?|\x04(?:[0-9a-f]{6}(?:,[0-9a-f]{6})?)?/gi;
|
||||||
|
|
||||||
export default (message: string) => message.replace(matchFormatting, "").trim();
|
export default (message: string) => message.replace(matchFormatting, "").trim();
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import {ParsedStyle} from "./parseStyle";
|
|
||||||
|
|
||||||
// Create plain text entries corresponding to areas of the text that match no
|
// Create plain text entries corresponding to areas of the text that match no
|
||||||
// existing entries. Returns an empty array if all parts of the text have been
|
// existing entries. Returns an empty array if all parts of the text have been
|
||||||
|
|
||||||
|
import {Part} from "./merge";
|
||||||
|
|
||||||
// parsed into recognizable entries already.
|
// parsed into recognizable entries already.
|
||||||
function fill(existingEntries: ParsedStyle[], text: string) {
|
function fill(existingEntries: Part[], text: string) {
|
||||||
let position = 0;
|
let position = 0;
|
||||||
|
|
||||||
// Fill inner parts of the text. For example, if text is `foobarbaz` and both
|
// Fill inner parts of the text. For example, if text is `foobarbaz` and both
|
||||||
// `foo` and `baz` have matched into an entry, this will return a dummy entry
|
// `foo` and `baz` have matched into an entry, this will return a dummy entry
|
||||||
// corresponding to `bar`.
|
// corresponding to `bar`.
|
||||||
const result = existingEntries.reduce((acc: Omit<ParsedStyle, "text">[], textSegment) => {
|
const result = existingEntries.reduce<Part[]>((acc, textSegment) => {
|
||||||
if (textSegment.start > position) {
|
if (textSegment.start > position) {
|
||||||
acc.push({
|
acc.push({
|
||||||
start: position,
|
start: position,
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
// ")", "[", "]", "{", "}", and "|" in string.
|
// ")", "[", "]", "{", "}", and "|" in string.
|
||||||
// See https://lodash.com/docs/#escapeRegExp
|
// See https://lodash.com/docs/#escapeRegExp
|
||||||
import escapeRegExp from "lodash/escapeRegExp";
|
import escapeRegExp from "lodash/escapeRegExp";
|
||||||
|
import {Part} from "./merge";
|
||||||
|
|
||||||
// Given an array of channel prefixes (such as "#" and "&") and an array of user
|
// Given an array of channel prefixes (such as "#" and "&") and an array of user
|
||||||
// modes (such as "@" and "+"), this function extracts channels and nicks from a
|
// modes (such as "@" and "+"), this function extracts channels and nicks from a
|
||||||
// text.
|
// text.
|
||||||
// It returns an array of objects for each channel found with their start index,
|
// It returns an array of objects for each channel found with their start index,
|
||||||
// end index and channel name.
|
// end index and channel name.
|
||||||
function findChannels(text, channelPrefixes, userModes) {
|
function findChannels(text: string, channelPrefixes: string[], userModes: string[]) {
|
||||||
// `userModePattern` is necessary to ignore user modes in /whois responses.
|
// `userModePattern` is necessary to ignore user modes in /whois responses.
|
||||||
// For example, a voiced user in #thelounge will have a /whois response of:
|
// For example, a voiced user in #thelounge will have a /whois response of:
|
||||||
// > foo is on the following channels: +#thelounge
|
// > foo is on the following channels: +#thelounge
|
||||||
|
@ -18,7 +19,7 @@ function findChannels(text, channelPrefixes, userModes) {
|
||||||
const channelPattern = `(?:^|\\s)[${userModePattern}]*([${channelPrefixPattern}][^ \u0007]+)`;
|
const channelPattern = `(?:^|\\s)[${userModePattern}]*([${channelPrefixPattern}][^ \u0007]+)`;
|
||||||
const channelRegExp = new RegExp(channelPattern, "g");
|
const channelRegExp = new RegExp(channelPattern, "g");
|
||||||
|
|
||||||
const result = [];
|
const result: ChannelPart[] = [];
|
||||||
let match;
|
let match;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -38,4 +39,8 @@ function findChannels(text, channelPrefixes, userModes) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ChannelPart = Part & {
|
||||||
|
channel: string;
|
||||||
|
};
|
||||||
|
|
||||||
export default findChannels;
|
export default findChannels;
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
const emojiRegExp = require("emoji-regex")();
|
import emojiRegExp from "emoji-regex";
|
||||||
|
import {Part} from "./merge";
|
||||||
|
|
||||||
function findEmoji(text) {
|
const regExp = emojiRegExp();
|
||||||
const result = [];
|
|
||||||
|
function findEmoji(text: string) {
|
||||||
|
const result: EmojiPart[] = [];
|
||||||
let match;
|
let match;
|
||||||
|
|
||||||
while ((match = emojiRegExp.exec(text))) {
|
while ((match = regExp.exec(text))) {
|
||||||
result.push({
|
result.push({
|
||||||
start: match.index,
|
start: match.index,
|
||||||
end: match.index + match[0].length,
|
end: match.index + match[0].length,
|
||||||
|
@ -15,4 +18,8 @@ function findEmoji(text) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EmojiPart = Part & {
|
||||||
|
emoji: string;
|
||||||
|
};
|
||||||
|
|
||||||
export default findEmoji;
|
export default findEmoji;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import LinkifyIt, {Match} from "linkify-it";
|
import LinkifyIt, {Match} from "linkify-it";
|
||||||
|
import {Part} from "./merge";
|
||||||
|
|
||||||
type OurMatch = Match & {
|
type OurMatch = Match & {
|
||||||
noschema?: boolean;
|
noschema?: boolean;
|
||||||
|
@ -24,7 +25,8 @@ LinkifyIt.prototype.normalize = function normalize(match: OurMatch) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const linkify = LinkifyIt().tlds(require("tlds")).tlds("onion", true);
|
import tlds from "tlds";
|
||||||
|
const linkify = LinkifyIt().tlds(tlds).tlds("onion", true);
|
||||||
|
|
||||||
// Known schemes to detect in text
|
// Known schemes to detect in text
|
||||||
const commonSchemes = [
|
const commonSchemes = [
|
||||||
|
@ -68,7 +70,7 @@ function findLinksWithSchema(text: string) {
|
||||||
return matches.filter((url) => !url.noschema).map(returnUrl);
|
return matches.filter((url) => !url.noschema).map(returnUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
function returnUrl(url: OurMatch) {
|
function returnUrl(url: OurMatch): LinkPart {
|
||||||
return {
|
return {
|
||||||
start: url.index,
|
start: url.index,
|
||||||
end: url.lastIndex,
|
end: url.lastIndex,
|
||||||
|
@ -76,4 +78,8 @@ function returnUrl(url: OurMatch) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LinkPart = Part & {
|
||||||
|
link: string;
|
||||||
|
};
|
||||||
|
|
||||||
export {findLinks, findLinksWithSchema};
|
export {findLinks, findLinksWithSchema};
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
|
import {Part} from "./merge";
|
||||||
|
|
||||||
const nickRegExp = /([\w[\]\\`^{|}-]+)/g;
|
const nickRegExp = /([\w[\]\\`^{|}-]+)/g;
|
||||||
|
|
||||||
function findNames(text, users) {
|
export type NamePart = Part & {
|
||||||
const result = [];
|
nick: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function findNames(text: string, nicks: string[]): NamePart[] {
|
||||||
|
const result: NamePart[] = [];
|
||||||
|
|
||||||
// Return early if we don't have any nicknames to find
|
// Return early if we don't have any nicknames to find
|
||||||
if (users.length === 0) {
|
if (nicks.length === 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
let match;
|
let match;
|
||||||
|
|
||||||
while ((match = nickRegExp.exec(text))) {
|
while ((match = nickRegExp.exec(text))) {
|
||||||
if (users.indexOf(match[1]) > -1) {
|
if (nicks.indexOf(match[1]) > -1) {
|
||||||
result.push({
|
result.push({
|
||||||
start: match.index,
|
start: match.index,
|
||||||
end: match.index + match[1].length,
|
end: match.index + match[1].length,
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
import anyIntersection from "./anyIntersection";
|
import anyIntersection from "./anyIntersection";
|
||||||
import fill from "./fill";
|
import fill from "./fill";
|
||||||
|
import {ChannelPart} from "./findChannels";
|
||||||
|
import {EmojiPart} from "./findEmoji";
|
||||||
|
import {NamePart} from "./findNames";
|
||||||
|
|
||||||
|
type TextPart = Part & {
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Fragment = TextPart;
|
||||||
|
|
||||||
// Merge text part information within a styling fragment
|
// Merge text part information within a styling fragment
|
||||||
function assign(textPart, fragment) {
|
function assign(textPart: Part, fragment: Fragment) {
|
||||||
const fragStart = fragment.start;
|
const fragStart = fragment.start;
|
||||||
const start = Math.max(fragment.start, textPart.start);
|
const start = Math.max(fragment.start, textPart.start);
|
||||||
const end = Math.min(fragment.end, textPart.end);
|
const end = Math.min(fragment.end, textPart.end);
|
||||||
|
@ -11,10 +20,20 @@ function assign(textPart, fragment) {
|
||||||
return Object.assign({}, fragment, {start, end, text});
|
return Object.assign({}, fragment, {start, end, text});
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortParts(a, b) {
|
function sortParts(a: Part, b: Part) {
|
||||||
return a.start - b.start || b.end - a.end;
|
return a.start - b.start || b.end - a.end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Part = {
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
fragments?: Fragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
type MergedPart = TextPart | NamePart | EmojiPart | ChannelPart;
|
||||||
|
|
||||||
|
type MergedPartWithFragments = MergedPart & {fragments: Fragment[]};
|
||||||
|
|
||||||
// Merge the style fragments within the text parts, taking into account
|
// Merge the style fragments within the text parts, taking into account
|
||||||
// boundaries and text sections that have not matched to links or channels.
|
// boundaries and text sections that have not matched to links or channels.
|
||||||
// For example, given a string "foobar" where "foo" and "bar" have been
|
// For example, given a string "foobar" where "foo" and "bar" have been
|
||||||
|
@ -22,9 +41,13 @@ function sortParts(a, b) {
|
||||||
// different styles, the first resulting part will contain fragments "fo" and
|
// different styles, the first resulting part will contain fragments "fo" and
|
||||||
// "o", and the second resulting part will contain "b" and "ar". "o" and "b"
|
// "o", and the second resulting part will contain "b" and "ar". "o" and "b"
|
||||||
// fragments will contain duplicate styling attributes.
|
// fragments will contain duplicate styling attributes.
|
||||||
function merge(textParts, styleFragments, cleanText) {
|
function merge(
|
||||||
|
textParts: MergedPart[],
|
||||||
|
styleFragments: Fragment[],
|
||||||
|
cleanText: string
|
||||||
|
): MergedPart[] {
|
||||||
// Remove overlapping parts
|
// Remove overlapping parts
|
||||||
textParts = textParts.sort(sortParts).reduce((prev, curr) => {
|
textParts = textParts.sort(sortParts).reduce<MergedPart[]>((prev, curr) => {
|
||||||
const intersection = prev.some((p) => anyIntersection(p, curr));
|
const intersection = prev.some((p) => anyIntersection(p, curr));
|
||||||
|
|
||||||
if (intersection) {
|
if (intersection) {
|
||||||
|
@ -37,11 +60,14 @@ function merge(textParts, styleFragments, cleanText) {
|
||||||
// Every section of the original text that has not been captured in a "part"
|
// Every section of the original text that has not been captured in a "part"
|
||||||
// is filled with "text" parts, dummy objects with start/end but no extra
|
// is filled with "text" parts, dummy objects with start/end but no extra
|
||||||
// metadata.
|
// metadata.
|
||||||
const allParts = textParts.concat(fill(textParts, cleanText)).sort(sortParts); // Sort all parts identified based on their position in the original text
|
|
||||||
|
const filled = fill(textParts, cleanText) as TextPart[];
|
||||||
|
const allParts: MergedPart[] = [...textParts, ...filled].sort(sortParts); // Sort all parts identified based on their position in the original text
|
||||||
|
|
||||||
// Distribute the style fragments within the text parts
|
// Distribute the style fragments within the text parts
|
||||||
return allParts.map((textPart) => {
|
return allParts.map((textPart) => {
|
||||||
textPart.fragments = styleFragments
|
// TODO: remove any type casting.
|
||||||
|
(textPart as any).fragments = styleFragments
|
||||||
.filter((fragment) => anyIntersection(textPart, fragment))
|
.filter((fragment) => anyIntersection(textPart, fragment))
|
||||||
.map((fragment) => assign(textPart, fragment));
|
.map((fragment) => assign(textPart, fragment));
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,17 @@ import LinkPreviewToggle from "../../components/LinkPreviewToggle.vue";
|
||||||
import LinkPreviewFileSize from "../../components/LinkPreviewFileSize.vue";
|
import LinkPreviewFileSize from "../../components/LinkPreviewFileSize.vue";
|
||||||
import InlineChannel from "../../components/InlineChannel.vue";
|
import InlineChannel from "../../components/InlineChannel.vue";
|
||||||
import Username from "../../components/Username.vue";
|
import Username from "../../components/Username.vue";
|
||||||
|
import {VNode} from "vue";
|
||||||
|
import Network from "src/models/network";
|
||||||
|
import {Message} from "src/models/msg";
|
||||||
|
|
||||||
const emojiModifiersRegex = /[\u{1f3fb}-\u{1f3ff}]|\u{fe0f}/gu;
|
const emojiModifiersRegex = /[\u{1f3fb}-\u{1f3ff}]|\u{fe0f}/gu;
|
||||||
|
|
||||||
|
type createElement = (tag: string, props: any, children: any) => VNode;
|
||||||
|
|
||||||
// Create an HTML `span` with styling information for a given fragment
|
// Create an HTML `span` with styling information for a given fragment
|
||||||
function createFragment(fragment, createElement) {
|
// TODO: remove any
|
||||||
|
function createFragment(fragment: Record<any, string>, createElement: createElement) {
|
||||||
const classes = [];
|
const classes = [];
|
||||||
|
|
||||||
if (fragment.bold) {
|
if (fragment.bold) {
|
||||||
|
@ -44,7 +50,7 @@ function createFragment(fragment, createElement) {
|
||||||
classes.push("irc-monospace");
|
classes.push("irc-monospace");
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {};
|
const data = {} as any;
|
||||||
let hasData = false;
|
let hasData = false;
|
||||||
|
|
||||||
if (classes.length > 0) {
|
if (classes.length > 0) {
|
||||||
|
@ -68,7 +74,7 @@ function createFragment(fragment, createElement) {
|
||||||
|
|
||||||
// Transform an IRC message potentially filled with styling control codes, URLs,
|
// Transform an IRC message potentially filled with styling control codes, URLs,
|
||||||
// nicknames, and channels into a string of HTML elements to display on the client.
|
// nicknames, and channels into a string of HTML elements to display on the client.
|
||||||
function parse(createElement, text, message = undefined, network = undefined) {
|
function parse(createElement: createElement, text: string, message?: Message, network?: Network) {
|
||||||
// Extract the styling information and get the plain text version from it
|
// Extract the styling information and get the plain text version from it
|
||||||
const styleFragments = parseStyle(text);
|
const styleFragments = parseStyle(text);
|
||||||
const cleanText = styleFragments.map((fragment) => fragment.text).join("");
|
const cleanText = styleFragments.map((fragment) => fragment.text).join("");
|
||||||
|
@ -76,14 +82,15 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
||||||
// On the plain text, find channels and URLs, returned as "parts". Parts are
|
// On the plain text, find channels and URLs, returned as "parts". Parts are
|
||||||
// arrays of objects containing start and end markers, as well as metadata
|
// arrays of objects containing start and end markers, as well as metadata
|
||||||
// depending on what was found (channel or link).
|
// depending on what was found (channel or link).
|
||||||
const channelPrefixes = network ? network.serverOptions.CHANTYPES : ["#", "&"];
|
const channelPrefixes = network?.serverOptions?.CHANTYPES || ["#", "&"];
|
||||||
const userModes = network?.serverOptions?.PREFIX.symbols || ["!", "@", "%", "+"];
|
const userModes = network?.serverOptions?.PREFIX.symbols || ["!", "@", "%", "+"];
|
||||||
const channelParts = findChannels(cleanText, channelPrefixes, userModes);
|
const channelParts = findChannels(cleanText, channelPrefixes, userModes);
|
||||||
const linkParts = findLinks(cleanText);
|
const linkParts = findLinks(cleanText);
|
||||||
const emojiParts = findEmoji(cleanText);
|
const emojiParts = findEmoji(cleanText);
|
||||||
const nameParts = findNames(cleanText, message ? message.users || [] : []);
|
// TODO: remove type casting.
|
||||||
|
const nameParts = findNames(cleanText, message ? (message.users as string[]) || [] : []);
|
||||||
|
|
||||||
const parts = channelParts.concat(linkParts).concat(emojiParts).concat(nameParts);
|
const parts = [...channelParts, ...linkParts, ...emojiParts, ...nameParts];
|
||||||
|
|
||||||
// The channel the message belongs to might not exist if the user isn't joined to it.
|
// The channel the message belongs to might not exist if the user isn't joined to it.
|
||||||
const messageChannel = message ? message.channel : null;
|
const messageChannel = message ? message.channel : null;
|
||||||
|
@ -91,12 +98,12 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
||||||
// Merge the styling information with the channels / URLs / nicks / text objects and
|
// Merge the styling information with the channels / URLs / nicks / text objects and
|
||||||
// generate HTML strings with the resulting fragments
|
// generate HTML strings with the resulting fragments
|
||||||
return merge(parts, styleFragments, cleanText).map((textPart) => {
|
return merge(parts, styleFragments, cleanText).map((textPart) => {
|
||||||
const fragments = textPart.fragments.map((fragment) =>
|
const fragments = textPart.fragments?.map((fragment) =>
|
||||||
createFragment(fragment, createElement)
|
createFragment(fragment, createElement)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Wrap these potentially styled fragments with links and channel buttons
|
// Wrap these potentially styled fragments with links and channel buttons
|
||||||
if (textPart.link) {
|
if ("link" in textPart) {
|
||||||
const preview =
|
const preview =
|
||||||
message &&
|
message &&
|
||||||
message.previews &&
|
message.previews &&
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export default (stringUri) => {
|
export default (stringUri: string) => {
|
||||||
const data = {};
|
const data = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export default (count) => {
|
export default (count: number) => {
|
||||||
if (count < 1000) {
|
if (count < 1000) {
|
||||||
return count.toString();
|
return count.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,12 @@ import store from "../store";
|
||||||
import location from "../location";
|
import location from "../location";
|
||||||
let lastServerHash = null;
|
let lastServerHash = null;
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
g_TheLoungeRemoveLoading: () => void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
socket.on("auth:success", function () {
|
socket.on("auth:success", function () {
|
||||||
store.commit("currentUserVisibleError", "Loading messages…");
|
store.commit("currentUserVisibleError", "Loading messages…");
|
||||||
updateLoadingMessage();
|
updateLoadingMessage();
|
||||||
|
@ -83,10 +89,10 @@ function showSignIn() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadPage(message) {
|
function reloadPage(message: string) {
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
store.commit("currentUserVisibleError", message);
|
store.commit("currentUserVisibleError", message);
|
||||||
location.reload(true);
|
location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLoadingMessage() {
|
function updateLoadingMessage() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import io, {Socket} from "socket.io-client";
|
import io, {Socket} from "socket.io-client";
|
||||||
|
|
||||||
const socket = io({
|
const socket: Socket = io({
|
||||||
transports: JSON.parse(document.body.dataset.transports || "['polling', 'websocket']"),
|
transports: JSON.parse(document.body.dataset.transports || "['polling', 'websocket']"),
|
||||||
path: window.location.pathname + "socket.io/",
|
path: window.location.pathname + "socket.io/",
|
||||||
autoConnect: false,
|
autoConnect: false,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Vue from "vue";
|
||||||
import Vuex from "vuex";
|
import Vuex from "vuex";
|
||||||
import {createSettingsStore} from "./store-settings";
|
import {createSettingsStore} from "./store-settings";
|
||||||
import storage from "./localStorage";
|
import storage from "./localStorage";
|
||||||
|
import {ClientChan, ClientNetwork} from "./types";
|
||||||
|
|
||||||
const appName = document.title;
|
const appName = document.title;
|
||||||
|
|
||||||
|
@ -21,15 +22,15 @@ function detectDesktopNotificationState() {
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
appLoaded: boolean;
|
appLoaded: boolean;
|
||||||
activeChannel?: {
|
activeChannel: {
|
||||||
network: Network;
|
network: ClientNetwork;
|
||||||
channel: ClientChan;
|
channel: ClientChan;
|
||||||
};
|
};
|
||||||
currentUserVisibleError: string | null;
|
currentUserVisibleError: string | null;
|
||||||
desktopNotificationState: "granted" | "blocked" | "nohttps" | "unsupported";
|
desktopNotificationState: "granted" | "blocked" | "nohttps" | "unsupported";
|
||||||
isAutoCompleting: boolean;
|
isAutoCompleting: boolean;
|
||||||
isConnected: boolean;
|
isConnected: boolean;
|
||||||
networks: Network[];
|
networks: ClientNetwork[];
|
||||||
// TODO: type
|
// TODO: type
|
||||||
mentions: any[];
|
mentions: any[];
|
||||||
hasServiceWorker: boolean;
|
hasServiceWorker: boolean;
|
||||||
|
|
8
client/index.ts → client/js/types.d.ts
vendored
8
client/index.ts → client/js/types.d.ts
vendored
|
@ -1,4 +1,5 @@
|
||||||
import Chan from "../src/models/chan";
|
import Chan from "../../src/models/chan";
|
||||||
|
import Network from "../../src/models/network";
|
||||||
|
|
||||||
declare module "*.vue" {
|
declare module "*.vue" {
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
|
@ -10,4 +11,9 @@ interface LoungeWindow extends Window {
|
||||||
|
|
||||||
type ClientChan = Chan & {
|
type ClientChan = Chan & {
|
||||||
moreHistoryAvailable: boolean;
|
moreHistoryAvailable: boolean;
|
||||||
|
editTopic: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ClientNetwork = Network & {
|
||||||
|
isJoinChannelShown: boolean;
|
||||||
};
|
};
|
|
@ -12,22 +12,31 @@ import eventbus from "./eventbus";
|
||||||
import "./socket-events";
|
import "./socket-events";
|
||||||
import "./webpush";
|
import "./webpush";
|
||||||
import "./keybinds";
|
import "./keybinds";
|
||||||
|
import {ClientChan} from "./types";
|
||||||
|
|
||||||
const favicon = document.getElementById("favicon");
|
const favicon = document.getElementById("favicon");
|
||||||
const faviconNormal = favicon?.getAttribute("href") || "";
|
const faviconNormal = favicon?.getAttribute("href") || "";
|
||||||
const faviconAlerted = favicon?.dataset.other || "";
|
const faviconAlerted = favicon?.dataset.other || "";
|
||||||
|
|
||||||
new Vue({
|
type Data = {};
|
||||||
|
export type Methods = {
|
||||||
|
switchToChannel: (channel: ClientChan) => void;
|
||||||
|
closeChannel: (channel: ClientChan) => void;
|
||||||
|
};
|
||||||
|
type Computed = {};
|
||||||
|
type Props = {};
|
||||||
|
|
||||||
|
new Vue<Data, Methods, Computed, Props>({
|
||||||
el: "#viewport",
|
el: "#viewport",
|
||||||
router,
|
router,
|
||||||
mounted() {
|
mounted() {
|
||||||
socket.open();
|
socket.open();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
switchToChannel(channel: Channel) {
|
switchToChannel(channel: ClientChan) {
|
||||||
navigate("RoutedChat", {id: channel.id});
|
navigate("RoutedChat", {id: channel.id});
|
||||||
},
|
},
|
||||||
closeChannel(channel: Channel) {
|
closeChannel(channel: ClientChan) {
|
||||||
if (channel.type === "lobby") {
|
if (channel.type === "lobby") {
|
||||||
eventbus.emit(
|
eventbus.emit(
|
||||||
"confirm-dialog",
|
"confirm-dialog",
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
"files": [
|
"files": [
|
||||||
"../package.json"
|
"../package.json"
|
||||||
] /* If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. When a 'files' property is specified, only those files and those specified by 'include' are included. */,
|
] /* If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. When a 'files' property is specified, only those files and those specified by 'include' are included. */,
|
||||||
|
// "exclude": [],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"sourceMap": false /*Create source map files for emitted JavaScript files. See more: https://www.typescriptlang.org/tsconfig#sourceMap */,
|
"sourceMap": false /*Create source map files for emitted JavaScript files. See more: https://www.typescriptlang.org/tsconfig#sourceMap */,
|
||||||
"jsx": "preserve" /* Specify what JSX code is generated. */,
|
"jsx": "preserve" /* Specify what JSX code is generated. */,
|
||||||
|
@ -19,6 +20,8 @@
|
||||||
"module": "es2015",
|
"module": "es2015",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
|
||||||
|
// TODO: Remove eventually, this is due to typescript checking vue files that don't have lang="ts".
|
||||||
|
"checkJs": false,
|
||||||
// TODO: Remove eventually
|
// TODO: Remove eventually
|
||||||
"noImplicitAny": false /*Enable error reporting for expressions and declarations with an implied any type. See more: https://www.typescriptlang.org/tsconfig#noImplicitAny */
|
"noImplicitAny": false /*Enable error reporting for expressions and declarations with an implied any type. See more: https://www.typescriptlang.org/tsconfig#noImplicitAny */
|
||||||
} /* Instructs the TypeScript compiler how to compile .ts files. */
|
} /* Instructs the TypeScript compiler how to compile .ts files. */
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
"lint:tsc": "tsc --noEmit",
|
"lint:tsc": "tsc --noEmit",
|
||||||
"start": "node src/dist/src/index start",
|
"start": "node src/dist/src/index start",
|
||||||
"test": "run-p --aggregate-output --continue-on-error lint:* test:*",
|
"test": "run-p --aggregate-output --continue-on-error lint:* test:*",
|
||||||
"test:mocha": "NODE_ENV=test webpack --mode=development && nyc --nycrc-path=test/.nycrc-mocha.json mocha --colors --config=test/.mocharc.yml",
|
"test:mocha": "NODE_ENV=test webpack --mode=development && nyc --nycrc-path=test/.nycrc-mocha.json mocha --require ts-node/register --colors --config=test/.mocharc.yml",
|
||||||
"watch": "webpack --watch"
|
"watch": "webpack --watch"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
|
@ -17,7 +17,7 @@ import SqliteMessageStorage from "./plugins/messageStorage/sqlite";
|
||||||
import TextFileMessageStorage from "./plugins/messageStorage/text";
|
import TextFileMessageStorage from "./plugins/messageStorage/text";
|
||||||
import Network, {NetworkWithIrcFramework} from "./models/network";
|
import Network, {NetworkWithIrcFramework} from "./models/network";
|
||||||
import ClientManager from "./clientManager";
|
import ClientManager from "./clientManager";
|
||||||
import {MessageStorage} from "./types/plugins/messageStorage";
|
import {MessageStorage, SearchQuery, SearchResponse} from "./plugins/messageStorage/types";
|
||||||
|
|
||||||
const events = [
|
const events = [
|
||||||
"away",
|
"away",
|
||||||
|
@ -598,9 +598,15 @@ class Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
search(query: string) {
|
search(query: SearchQuery): Promise<SearchResponse> {
|
||||||
if (this.messageProvider === undefined) {
|
if (this.messageProvider === undefined) {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve({
|
||||||
|
results: [],
|
||||||
|
target: "",
|
||||||
|
networkUuid: "",
|
||||||
|
offset: 0,
|
||||||
|
searchTerm: query?.searchTerm,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.messageProvider.search(query);
|
return this.messageProvider.search(query);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Client from "../../client";
|
import Client from "../../client";
|
||||||
import Chan, {Channel} from "../../models/chan";
|
import Chan, {Channel} from "../../models/chan";
|
||||||
import Network, {NetworkWithIrcFramework} from "../../models/network";
|
import Network, {NetworkWithIrcFramework} from "../../models/network";
|
||||||
|
import {PackageInfo} from "../packages";
|
||||||
|
|
||||||
export type PluginInputHandler = (
|
export type PluginInputHandler = (
|
||||||
this: Client,
|
this: Client,
|
||||||
|
@ -97,7 +98,7 @@ const getCommands = () =>
|
||||||
.concat(passThroughCommands)
|
.concat(passThroughCommands)
|
||||||
.sort();
|
.sort();
|
||||||
|
|
||||||
const addPluginCommand = (packageInfo, command, func) => {
|
const addPluginCommand = (packageInfo: PackageInfo, command, func) => {
|
||||||
func.packageInfo = packageInfo;
|
func.packageInfo = packageInfo;
|
||||||
pluginCommands.set(command, func);
|
pluginCommands.set(command, func);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,8 +7,9 @@ import Config from "../../config";
|
||||||
import Msg, {Message} from "../../models/msg";
|
import Msg, {Message} from "../../models/msg";
|
||||||
import Client from "../../client";
|
import Client from "../../client";
|
||||||
import Chan, {Channel} from "../../models/chan";
|
import Chan, {Channel} from "../../models/chan";
|
||||||
import type {SqliteMessageStorage as ISqliteMessageStorage} from "../../types/plugins/messageStorage";
|
import type {SearchResponse, SqliteMessageStorage as ISqliteMessageStorage} from "./types";
|
||||||
import Network from "../../models/network";
|
import Network from "../../models/network";
|
||||||
|
import {SearchQuery} from "./types";
|
||||||
|
|
||||||
// TODO; type
|
// TODO; type
|
||||||
let sqlite3: any;
|
let sqlite3: any;
|
||||||
|
@ -209,9 +210,15 @@ class SqliteMessageStorage implements ISqliteMessageStorage {
|
||||||
}) as Promise<Message[]>;
|
}) as Promise<Message[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
search(query: {searchTerm: string; networkUuid: string; channelName: string; offset: string}) {
|
search(query: SearchQuery): Promise<SearchResponse> {
|
||||||
if (!this.isEnabled) {
|
if (!this.isEnabled) {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve({
|
||||||
|
results: [],
|
||||||
|
target: "",
|
||||||
|
networkUuid: "",
|
||||||
|
offset: 0,
|
||||||
|
searchTerm: query?.searchTerm,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using the '@' character to escape '%' and '_' in patterns.
|
// Using the '@' character to escape '%' and '_' in patterns.
|
||||||
|
@ -243,7 +250,7 @@ class SqliteMessageStorage implements ISqliteMessageStorage {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
const response = {
|
const response: SearchResponse = {
|
||||||
searchTerm: query.searchTerm,
|
searchTerm: query.searchTerm,
|
||||||
target: query.channelName,
|
target: query.channelName,
|
||||||
networkUuid: query.networkUuid,
|
networkUuid: query.networkUuid,
|
||||||
|
@ -263,7 +270,8 @@ class SqliteMessageStorage implements ISqliteMessageStorage {
|
||||||
|
|
||||||
export default SqliteMessageStorage;
|
export default SqliteMessageStorage;
|
||||||
|
|
||||||
function parseSearchRowsToMessages(id, rows) {
|
// TODO: type any
|
||||||
|
function parseSearchRowsToMessages(id: string, rows: any[]) {
|
||||||
const messages: Msg[] = [];
|
const messages: Msg[] = [];
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import filenamify from "filenamify";
|
||||||
|
|
||||||
import log from "../../log";
|
import log from "../../log";
|
||||||
import Config from "../../config";
|
import Config from "../../config";
|
||||||
import {MessageStorage} from "../../types/plugins/messageStorage";
|
import {MessageStorage} from "./types";
|
||||||
import Client from "../../client";
|
import Client from "../../client";
|
||||||
import Channel from "../../models/chan";
|
import Channel from "../../models/chan";
|
||||||
import {Message, MessageType} from "../../models/msg";
|
import {Message, MessageType} from "../../models/msg";
|
||||||
|
|
|
@ -22,6 +22,22 @@ interface MessageStorage {
|
||||||
canProvideMessages(): boolean;
|
canProvideMessages(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SearchQuery = {
|
||||||
|
searchTerm: string;
|
||||||
|
networkUuid: string;
|
||||||
|
channelName: string;
|
||||||
|
offset: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SearchResponse = Omit<SearchQuery, "channelName" | "offset"> & {
|
||||||
|
results: Message[];
|
||||||
|
target: string;
|
||||||
|
offset: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SearchFunction = (query: SearchQuery) => Promise<SearchResponse>;
|
||||||
|
|
||||||
export interface SqliteMessageStorage extends MessageStorage {
|
export interface SqliteMessageStorage extends MessageStorage {
|
||||||
database: Database;
|
database: Database;
|
||||||
|
search: SearchFunction;
|
||||||
}
|
}
|
|
@ -12,12 +12,14 @@ import fs from "fs";
|
||||||
import Utils from "../../command-line/utils";
|
import Utils from "../../command-line/utils";
|
||||||
import Client from "../../client";
|
import Client from "../../client";
|
||||||
|
|
||||||
type PackageInfo = {
|
export type PackageInfo = {
|
||||||
packageName: string;
|
packageName: string;
|
||||||
thelounge?: {supports: any};
|
thelounge?: {supports: any};
|
||||||
version: string;
|
version: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
files?: string[];
|
files?: string[];
|
||||||
|
// Legacy support
|
||||||
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const stylesheets: string[] = [];
|
const stylesheets: string[] = [];
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
import {PackageInfo} from "./index";
|
||||||
import Client from "../../client";
|
import Client from "../../client";
|
||||||
import Chan from "../../models/chan";
|
import Chan from "../../models/chan";
|
||||||
import Msg, {MessageType, UserInMessage} from "../../models/msg";
|
import Msg, {MessageType, UserInMessage} from "../../models/msg";
|
||||||
|
|
||||||
export default class PublicClient {
|
export default class PublicClient {
|
||||||
private client: Client;
|
private client: Client;
|
||||||
private packageInfo: any;
|
private packageInfo: PackageInfo;
|
||||||
|
|
||||||
constructor(client, packageInfo) {
|
constructor(client: Client, packageInfo: PackageInfo) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.packageInfo = packageInfo;
|
this.packageInfo = packageInfo;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +25,7 @@ export default class PublicClient {
|
||||||
*
|
*
|
||||||
* @param {Object} attributes
|
* @param {Object} attributes
|
||||||
*/
|
*/
|
||||||
createChannel(attributes) {
|
createChannel(attributes: Partial<Chan>) {
|
||||||
return this.client.createChannel(attributes);
|
return this.client.createChannel(attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"extends": "../tsconfig.base.json" /* Path to base configuration file to inherit from. Requires TypeScript version 2.1 or later. */,
|
"extends": "../tsconfig.base.json" /* Path to base configuration file to inherit from. Requires TypeScript version 2.1 or later. */,
|
||||||
"include": [
|
"include": [
|
||||||
"**/*"
|
"**/*",
|
||||||
|
"../client/js/helpers/ircmessageparser/*.ts"
|
||||||
] /* Specifies a list of glob patterns that match files to be included in compilation. If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. Requires TypeScript version 2.0 or later. */,
|
] /* Specifies a list of glob patterns that match files to be included in compilation. If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. Requires TypeScript version 2.0 or later. */,
|
||||||
"files": [
|
"files": [
|
||||||
"../babel.config.cjs",
|
|
||||||
"../client/js/constants.ts",
|
"../client/js/constants.ts",
|
||||||
"../client/js/helpers/ircmessageparser/cleanIrcMessage.ts",
|
|
||||||
"../client/js/helpers/ircmessageparser/findLinks.ts",
|
"../babel.config.cjs",
|
||||||
"../defaults/config.js",
|
"../defaults/config.js",
|
||||||
"../package.json",
|
"../package.json",
|
||||||
"../webpack.config.ts"
|
"../webpack.config.ts"
|
||||||
|
|
1
src/types/index.d.ts
vendored
1
src/types/index.d.ts
vendored
|
@ -1,2 +1 @@
|
||||||
import "./modules";
|
import "./modules";
|
||||||
import "./plugins";
|
|
||||||
|
|
1
src/types/plugins/index.d.ts
vendored
1
src/types/plugins/index.d.ts
vendored
|
@ -1 +0,0 @@
|
||||||
import "./messageStorage";
|
|
|
@ -5,4 +5,4 @@ reporter: dot
|
||||||
interactive: false
|
interactive: false
|
||||||
spec: "test/**/*.ts"
|
spec: "test/**/*.ts"
|
||||||
ignore: "test/client/**"
|
ignore: "test/client/**"
|
||||||
require: "test/fixtures/env"
|
require: "test/fixtures/env.ts"
|
||||||
|
|
6
test/fixtures/.thelounge/config.js
vendored
6
test/fixtures/.thelounge/config.js
vendored
|
@ -1,13 +1,13 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var config = require("../../../defaults/config.js");
|
import config from "../../../defaults/config.js";
|
||||||
|
|
||||||
config.defaults.name = "Example IRC Server";
|
config.defaults.name = "Example IRC Server";
|
||||||
config.defaults.host = "irc.example.com";
|
config.defaults.host = "irc.example.com";
|
||||||
config.public = true;
|
config.public = true;
|
||||||
config.prefetch = true;
|
config.prefetch = true;
|
||||||
config.host = config.bind = "127.0.0.1";
|
config.host = bind = "127.0.0.1";
|
||||||
config.port = 61337;
|
config.port = 61337;
|
||||||
config.transports = ["websocket"];
|
config.transports = ["websocket"];
|
||||||
|
|
||||||
module.exports = config;
|
export default config;
|
||||||
|
|
2
test/fixtures/env.ts
vendored
2
test/fixtures/env.ts
vendored
|
@ -10,7 +10,7 @@ config.setHome(home);
|
||||||
|
|
||||||
import STSPolicies from "../../src/plugins/sts"; // Must be imported *after* setHome
|
import STSPolicies from "../../src/plugins/sts"; // Must be imported *after* setHome
|
||||||
|
|
||||||
const mochaGlobalTeardown = async function () {
|
const mochaGlobalTeardown = function () {
|
||||||
STSPolicies.refresh.cancel(); // Cancel debounced function, so it does not write later
|
STSPolicies.refresh.cancel(); // Cancel debounced function, so it does not write later
|
||||||
fs.unlinkSync(STSPolicies.stsFile);
|
fs.unlinkSync(STSPolicies.stsFile);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
"../src"
|
"../src"
|
||||||
] /* Specifies a list of glob patterns that match files to be included in compilation. If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. Requires TypeScript version 2.0 or later. */,
|
] /* Specifies a list of glob patterns that match files to be included in compilation. If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. Requires TypeScript version 2.0 or later. */,
|
||||||
"files": [
|
"files": [
|
||||||
"../babel.config.cjs"
|
"../babel.config.cjs",
|
||||||
|
"../src/helper.ts"
|
||||||
] /* If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. When a 'files' property is specified, only those files and those specified by 'include' are included. */,
|
] /* If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. When a 'files' property is specified, only those files and those specified by 'include' are included. */,
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"files": true
|
"files": true
|
||||||
|
|
|
@ -47,7 +47,8 @@
|
||||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
/* outDir is necessary because otherwise the built output for files like babel.config.cjs would overwrite the input. */
|
||||||
|
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||||
// "removeComments": true, /* Disable emitting comments. */
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
"./babel.config.cjs",
|
"./babel.config.cjs",
|
||||||
"./src/helper.ts"
|
"./src/helper.ts"
|
||||||
] /* If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. When a 'files' property is specified, only those files and those specified by 'include' are included. */,
|
] /* If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. When a 'files' property is specified, only those files and those specified by 'include' are included. */,
|
||||||
|
// "exclude": [],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "./client" /* Path to referenced tsconfig or to folder containing tsconfig. */
|
"path": "./client" /* Path to referenced tsconfig or to folder containing tsconfig. */
|
||||||
|
|
|
@ -187,6 +187,10 @@ export default (env: any, argv: any) => {
|
||||||
filename: "css/style.css",
|
filename: "css/style.css",
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: "css/style.css",
|
||||||
|
}),
|
||||||
|
|
||||||
// Client tests that require Vue may end up requireing socket.io
|
// Client tests that require Vue may end up requireing socket.io
|
||||||
new webpack.NormalModuleReplacementPlugin(
|
new webpack.NormalModuleReplacementPlugin(
|
||||||
/js(\/|\\)socket\.js/,
|
/js(\/|\\)socket\.js/,
|
||||||
|
|
Loading…
Reference in a new issue