Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
382b1d2250 | |||
629693355a | |||
00a3f8d392 | |||
80b6e8090e | |||
a5f817d896 | |||
51b0244cf2 | |||
01131e2606 | |||
6283b7668e | |||
d058ecc4ea | |||
77a0450b5d | |||
1dd1b9084f | |||
6341807d02 | |||
51a1f30225 | |||
5422482696 | |||
cd7f8b080e | |||
faf29b768f |
12
package.json
12
package.json
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "8.56.0",
|
"version": "8.57.1",
|
||||||
"clientVersion": "1.0.9912",
|
"clientVersion": "1.0.9928",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"@koa/cors": "2.2.2",
|
"@koa/cors": "2.2.2",
|
||||||
"@prezzemolo/rap": "0.1.2",
|
"@prezzemolo/rap": "0.1.2",
|
||||||
"@prezzemolo/zip": "0.0.3",
|
"@prezzemolo/zip": "0.0.3",
|
||||||
"@types/bcryptjs": "2.4.1",
|
"@types/bcryptjs": "2.4.2",
|
||||||
"@types/dateformat": "1.0.1",
|
"@types/dateformat": "1.0.1",
|
||||||
"@types/debug": "0.0.30",
|
"@types/debug": "0.0.30",
|
||||||
"@types/deep-equal": "1.0.1",
|
"@types/deep-equal": "1.0.1",
|
||||||
@ -51,7 +51,7 @@
|
|||||||
"@types/koa-logger": "3.1.0",
|
"@types/koa-logger": "3.1.0",
|
||||||
"@types/koa-mount": "3.0.1",
|
"@types/koa-mount": "3.0.1",
|
||||||
"@types/koa-multer": "1.0.0",
|
"@types/koa-multer": "1.0.0",
|
||||||
"@types/koa-router": "7.0.31",
|
"@types/koa-router": "7.0.32",
|
||||||
"@types/koa-send": "4.1.1",
|
"@types/koa-send": "4.1.1",
|
||||||
"@types/koa-views": "2.0.3",
|
"@types/koa-views": "2.0.3",
|
||||||
"@types/koa__cors": "2.2.3",
|
"@types/koa__cors": "2.2.3",
|
||||||
@ -77,7 +77,7 @@
|
|||||||
"@types/systeminformation": "3.23.0",
|
"@types/systeminformation": "3.23.0",
|
||||||
"@types/tmp": "0.0.33",
|
"@types/tmp": "0.0.33",
|
||||||
"@types/uuid": "3.4.4",
|
"@types/uuid": "3.4.4",
|
||||||
"@types/webpack": "4.4.11",
|
"@types/webpack": "4.4.12",
|
||||||
"@types/webpack-stream": "3.2.10",
|
"@types/webpack-stream": "3.2.10",
|
||||||
"@types/websocket": "0.0.40",
|
"@types/websocket": "0.0.40",
|
||||||
"@types/ws": "6.0.1",
|
"@types/ws": "6.0.1",
|
||||||
@ -221,7 +221,7 @@
|
|||||||
"webfinger.js": "2.6.6",
|
"webfinger.js": "2.6.6",
|
||||||
"webpack": "4.19.1",
|
"webpack": "4.19.1",
|
||||||
"webpack-cli": "3.1.0",
|
"webpack-cli": "3.1.0",
|
||||||
"websocket": "1.0.26",
|
"websocket": "1.0.28",
|
||||||
"ws": "6.0.0",
|
"ws": "6.0.0",
|
||||||
"xev": "2.0.1"
|
"xev": "2.0.1"
|
||||||
},
|
},
|
||||||
|
@ -33,12 +33,16 @@ export default Vue.extend({
|
|||||||
text: '%i18n:@pin%',
|
text: '%i18n:@pin%',
|
||||||
action: this.pin
|
action: this.pin
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin) {
|
||||||
items.push({
|
items.push({
|
||||||
icon: '%fa:trash-alt R%',
|
icon: '%fa:trash-alt R%',
|
||||||
text: '%i18n:@delete%',
|
text: '%i18n:@delete%',
|
||||||
action: this.del
|
action: this.del
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.note.uri) {
|
if (this.note.uri) {
|
||||||
items.push({
|
items.push({
|
||||||
icon: '%fa:external-link-square-alt%',
|
icon: '%fa:external-link-square-alt%',
|
||||||
@ -48,6 +52,7 @@ export default Vue.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="main">
|
<div class="main" :class="{ withBg: $store.state.i.wallpaperUrl != null }">
|
||||||
<template v-if="customize">
|
<template v-if="customize">
|
||||||
<x-draggable v-for="place in ['left', 'right']"
|
<x-draggable v-for="place in ['left', 'right']"
|
||||||
:list="widgets[place]"
|
:list="widgets[place]"
|
||||||
@ -340,7 +340,10 @@ root(isDark)
|
|||||||
display flex
|
display flex
|
||||||
justify-content center
|
justify-content center
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
max-width 1220px
|
max-width 1240px
|
||||||
|
|
||||||
|
&.withBg
|
||||||
|
background rgba(isDark ? #000 : #fff, 0.5)
|
||||||
|
|
||||||
> *
|
> *
|
||||||
.customize-container
|
.customize-container
|
||||||
@ -355,7 +358,7 @@ root(isDark)
|
|||||||
|
|
||||||
> .main
|
> .main
|
||||||
padding 16px
|
padding 16px
|
||||||
width calc(100% - 275px * 2)
|
width calc(100% - 280px * 2)
|
||||||
order 2
|
order 2
|
||||||
|
|
||||||
> .form
|
> .form
|
||||||
@ -371,7 +374,7 @@ root(isDark)
|
|||||||
border-radius 0
|
border-radius 0
|
||||||
|
|
||||||
> *:not(.main)
|
> *:not(.main)
|
||||||
width 275px
|
width 280px
|
||||||
padding 16px 0 16px 0
|
padding 16px 0 16px 0
|
||||||
|
|
||||||
> *:not(:last-child)
|
> *:not(:last-child)
|
||||||
|
@ -14,6 +14,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form">
|
<div class="form">
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<p>%i18n:@banner-url%</p>
|
||||||
|
<input v-model="bannerUrl">
|
||||||
|
</label>
|
||||||
|
<button class="ui" @click="updateMeta">%i18n:@save%</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" v-model="disableRegistration" @change="updateMeta">
|
<input type="checkbox" v-model="disableRegistration" @change="updateMeta">
|
||||||
@ -46,6 +54,7 @@ export default Vue.extend({
|
|||||||
stats: null,
|
stats: null,
|
||||||
disableRegistration: false,
|
disableRegistration: false,
|
||||||
disableLocalTimeline: false,
|
disableLocalTimeline: false,
|
||||||
|
bannerUrl: null,
|
||||||
inviteCode: null,
|
inviteCode: null,
|
||||||
connection: null,
|
connection: null,
|
||||||
connectionId: null
|
connectionId: null
|
||||||
@ -58,6 +67,7 @@ export default Vue.extend({
|
|||||||
(this as any).os.getMeta().then(meta => {
|
(this as any).os.getMeta().then(meta => {
|
||||||
this.disableRegistration = meta.disableRegistration;
|
this.disableRegistration = meta.disableRegistration;
|
||||||
this.disableLocalTimeline = meta.disableLocalTimeline;
|
this.disableLocalTimeline = meta.disableLocalTimeline;
|
||||||
|
this.bannerUrl = meta.bannerUrl;
|
||||||
});
|
});
|
||||||
|
|
||||||
(this as any).api('stats').then(stats => {
|
(this as any).api('stats').then(stats => {
|
||||||
@ -76,7 +86,8 @@ export default Vue.extend({
|
|||||||
updateMeta() {
|
updateMeta() {
|
||||||
(this as any).api('admin/update-meta', {
|
(this as any).api('admin/update-meta', {
|
||||||
disableRegistration: this.disableRegistration,
|
disableRegistration: this.disableRegistration,
|
||||||
disableLocalTimeline: this.disableLocalTimeline
|
disableLocalTimeline: this.disableLocalTimeline,
|
||||||
|
bannerUrl: this.bannerUrl
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,6 +125,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
> .form
|
> .form
|
||||||
> div
|
> div
|
||||||
|
padding 16px
|
||||||
border-bottom solid 1px #eee
|
border-bottom solid 1px #eee
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-welcome">
|
<div class="mk-welcome">
|
||||||
|
<div class="banner" :style="{ backgroundImage: banner ? `url(${banner})` : null }"></div>
|
||||||
|
|
||||||
<button @click="dark">
|
<button @click="dark">
|
||||||
<template v-if="$store.state.device.darkmode">%fa:moon%</template>
|
<template v-if="$store.state.device.darkmode">%fa:moon%</template>
|
||||||
<template v-else>%fa:R moon%</template>
|
<template v-else>%fa:R moon%</template>
|
||||||
@ -154,6 +156,7 @@ export default Vue.extend({
|
|||||||
return {
|
return {
|
||||||
meta: null,
|
meta: null,
|
||||||
stats: null,
|
stats: null,
|
||||||
|
banner: null,
|
||||||
copyright,
|
copyright,
|
||||||
host,
|
host,
|
||||||
name: 'Misskey',
|
name: 'Misskey',
|
||||||
@ -169,6 +172,7 @@ export default Vue.extend({
|
|||||||
this.name = meta.name;
|
this.name = meta.name;
|
||||||
this.description = meta.description;
|
this.description = meta.description;
|
||||||
this.announcements = meta.broadcasts;
|
this.announcements = meta.broadcasts;
|
||||||
|
this.banner = meta.bannerUrl;
|
||||||
});
|
});
|
||||||
|
|
||||||
(this as any).api('stats').then(stats => {
|
(this as any).api('stats').then(stats => {
|
||||||
@ -308,6 +312,26 @@ root(isDark)
|
|||||||
//background-position center
|
//background-position center
|
||||||
//background-size cover
|
//background-size cover
|
||||||
|
|
||||||
|
> .banner
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
height 400px
|
||||||
|
background-position center
|
||||||
|
background-size cover
|
||||||
|
opacity 0.7
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
bottom 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
height 100px
|
||||||
|
background linear-gradient(transparent, isDark ? #191b22 : #f7f7f7)
|
||||||
|
|
||||||
> .forkit
|
> .forkit
|
||||||
position absolute
|
position absolute
|
||||||
top 0
|
top 0
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="wgwfgvvimdjvhjfwxropcwksnzftjqes">
|
<div class="wgwfgvvimdjvhjfwxropcwksnzftjqes">
|
||||||
|
<div class="banner" :style="{ backgroundImage: banner ? `url(${banner})` : null }"></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" :alt="name">
|
<img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" :alt="name">
|
||||||
<p class="host">{{ host }}</p>
|
<p class="host">{{ host }}</p>
|
||||||
@ -80,6 +82,7 @@ export default Vue.extend({
|
|||||||
meta: null,
|
meta: null,
|
||||||
copyright,
|
copyright,
|
||||||
stats: null,
|
stats: null,
|
||||||
|
banner: null,
|
||||||
host,
|
host,
|
||||||
name: 'Misskey',
|
name: 'Misskey',
|
||||||
description: '',
|
description: '',
|
||||||
@ -93,6 +96,7 @@ export default Vue.extend({
|
|||||||
this.name = meta.name;
|
this.name = meta.name;
|
||||||
this.description = meta.description;
|
this.description = meta.description;
|
||||||
this.announcements = meta.broadcasts;
|
this.announcements = meta.broadcasts;
|
||||||
|
this.banner = meta.bannerUrl;
|
||||||
});
|
});
|
||||||
|
|
||||||
(this as any).api('stats').then(stats => {
|
(this as any).api('stats').then(stats => {
|
||||||
@ -121,7 +125,27 @@ root(isDark)
|
|||||||
text-align center
|
text-align center
|
||||||
//background #fff
|
//background #fff
|
||||||
|
|
||||||
> div
|
> .banner
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
height 300px
|
||||||
|
background-position center
|
||||||
|
background-size cover
|
||||||
|
opacity 0.7
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
bottom 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
height 100px
|
||||||
|
background linear-gradient(transparent, isDark ? #191b22 : #f7f7f7)
|
||||||
|
|
||||||
|
> div:not(.banner)
|
||||||
padding 32px
|
padding 32px
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
max-width 500px
|
max-width 500px
|
||||||
|
@ -8,13 +8,20 @@ export type TextElementQuote = {
|
|||||||
quote: string
|
quote: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function(text: string) {
|
export default function(text: string, index: number) {
|
||||||
const match = text.match(/^"([\s\S]+?)\n"/);
|
const match = text.match(/^"([\s\S]+?)\n"/) || text.match(/^\n>([\s\S]+?)(\n\n|$)/) ||
|
||||||
|
(index == 0 ? text.match(/^>([\s\S]+?)(\n\n|$)/) : null);
|
||||||
|
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
const quote = match[0];
|
|
||||||
|
const quote = match[1]
|
||||||
|
.split('\n')
|
||||||
|
.map(line => line.replace(/^>+/g, '').trim())
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'quote',
|
type: 'quote',
|
||||||
content: quote,
|
content: match[0],
|
||||||
quote: match[1].trim(),
|
quote: quote,
|
||||||
} as TextElementQuote;
|
} as TextElementQuote;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
|
import * as mongo from 'mongodb';
|
||||||
|
|
||||||
|
function toString(id: any) {
|
||||||
|
return mongo.ObjectID.prototype.isPrototypeOf(id) ? (id as mongo.ObjectID).toHexString() : id;
|
||||||
|
}
|
||||||
|
|
||||||
export default function(note: any, mutedUserIds: string[]): boolean {
|
export default function(note: any, mutedUserIds: string[]): boolean {
|
||||||
if (mutedUserIds.indexOf(note.userId) != -1) {
|
if (mutedUserIds.includes(toString(note.userId))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.reply != null && mutedUserIds.indexOf(note.reply.userId) != -1) {
|
if (note.reply != null && mutedUserIds.includes(toString(note.reply.userId))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.renote != null && mutedUserIds.indexOf(note.renote.userId) != -1) {
|
if (note.renote != null && mutedUserIds.includes(toString(note.renote.userId))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,4 +14,5 @@ export type IMeta = {
|
|||||||
disableRegistration?: boolean;
|
disableRegistration?: boolean;
|
||||||
disableLocalTimeline?: boolean;
|
disableLocalTimeline?: boolean;
|
||||||
hidedTags?: string[];
|
hidedTags?: string[];
|
||||||
|
bannerUrl?: string;
|
||||||
};
|
};
|
||||||
|
@ -34,6 +34,12 @@ export const meta = {
|
|||||||
'ja-JP': '統計などで無視するハッシュタグ'
|
'ja-JP': '統計などで無視するハッシュタグ'
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
bannerUrl: $.str.optional.nullable.note({
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'インスタンスのバナー画像URL'
|
||||||
|
}
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,6 +65,10 @@ export default (params: any) => new Promise(async (res, rej) => {
|
|||||||
set.hidedTags = ps.hidedTags;
|
set.hidedTags = ps.hidedTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.bannerUrl !== undefined) {
|
||||||
|
set.bannerUrl = ps.bannerUrl;
|
||||||
|
}
|
||||||
|
|
||||||
await Meta.update({}, {
|
await Meta.update({}, {
|
||||||
$set: set
|
$set: set
|
||||||
}, { upsert: true });
|
}, { upsert: true });
|
||||||
|
@ -38,6 +38,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
|
|||||||
driveCapacityPerLocalUserMb: config.localDriveCapacityMb,
|
driveCapacityPerLocalUserMb: config.localDriveCapacityMb,
|
||||||
recaptchaSitekey: config.recaptcha ? config.recaptcha.site_key : null,
|
recaptchaSitekey: config.recaptcha ? config.recaptcha.site_key : null,
|
||||||
swPublickey: config.sw ? config.sw.public_key : null,
|
swPublickey: config.sw ? config.sw.public_key : null,
|
||||||
hidedTags: (me && me.isAdmin) ? meta.hidedTags : undefined
|
hidedTags: (me && me.isAdmin) ? meta.hidedTags : undefined,
|
||||||
|
bannerUrl: meta.bannerUrl
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -21,14 +21,17 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
|
|||||||
|
|
||||||
// Fetch note
|
// Fetch note
|
||||||
const note = await Note.findOne({
|
const note = await Note.findOne({
|
||||||
_id: noteId,
|
_id: noteId
|
||||||
userId: user._id
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (note === null) {
|
if (note === null) {
|
||||||
return rej('note not found');
|
return rej('note not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!user.isAdmin && !note.userId.equals(user._id)) {
|
||||||
|
return rej('access denied');
|
||||||
|
}
|
||||||
|
|
||||||
await deleteNote(user, note);
|
await deleteNote(user, note);
|
||||||
|
|
||||||
res();
|
res();
|
||||||
|
@ -5,6 +5,14 @@ import Mute from '../../models/mute';
|
|||||||
import { publishUserStream } from '../../stream';
|
import { publishUserStream } from '../../stream';
|
||||||
|
|
||||||
export default async function(user: IUser, note: INote, isSpecified = false) {
|
export default async function(user: IUser, note: INote, isSpecified = false) {
|
||||||
|
//#region ミュートしているなら無視
|
||||||
|
const mute = await Mute.find({
|
||||||
|
muterId: user._id
|
||||||
|
});
|
||||||
|
const mutedUserIds = mute.map(m => m.muteeId.toString());
|
||||||
|
if (mutedUserIds.includes(note.userId.toString())) return;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
const unread = await NoteUnread.insert({
|
const unread = await NoteUnread.insert({
|
||||||
noteId: note._id,
|
noteId: note._id,
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
@ -19,14 +27,6 @@ export default async function(user: IUser, note: INote, isSpecified = false) {
|
|||||||
const exist = await NoteUnread.findOne({ _id: unread._id });
|
const exist = await NoteUnread.findOne({ _id: unread._id });
|
||||||
if (exist == null) return;
|
if (exist == null) return;
|
||||||
|
|
||||||
//#region ただしミュートされているなら発行しない
|
|
||||||
const mute = await Mute.find({
|
|
||||||
muterId: user._id
|
|
||||||
});
|
|
||||||
const mutedUserIds = mute.map(m => m.muteeId.toString());
|
|
||||||
if (mutedUserIds.includes(note.userId.toString())) return;
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
User.update({
|
User.update({
|
||||||
_id: user._id
|
_id: user._id
|
||||||
}, {
|
}, {
|
||||||
|
24
test/mfm.ts
24
test/mfm.ts
@ -87,6 +87,30 @@ describe('Text', () => {
|
|||||||
], tokens2);
|
], tokens2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('quote', () => {
|
||||||
|
const tokens1 = analyze('> foo\nbar\nbaz');
|
||||||
|
assert.deepEqual([
|
||||||
|
{ type: 'quote', content: '> foo\nbar\nbaz', quote: 'foo\nbar\nbaz' }
|
||||||
|
], tokens1);
|
||||||
|
|
||||||
|
const tokens2 = analyze('before\n> foo\nbar\nbaz\n\nafter');
|
||||||
|
assert.deepEqual([
|
||||||
|
{ type: 'text', content: 'before' },
|
||||||
|
{ type: 'quote', content: '\n> foo\nbar\nbaz\n\n', quote: 'foo\nbar\nbaz' },
|
||||||
|
{ type: 'text', content: 'after' }
|
||||||
|
], tokens2);
|
||||||
|
|
||||||
|
const tokens3 = analyze('piyo> foo\nbar\nbaz');
|
||||||
|
assert.deepEqual([
|
||||||
|
{ type: 'text', content: 'piyo> foo\nbar\nbaz' }
|
||||||
|
], tokens3);
|
||||||
|
|
||||||
|
const tokens4 = analyze('> foo\n> bar\n> baz');
|
||||||
|
assert.deepEqual([
|
||||||
|
{ type: 'quote', content: '> foo\n> bar\n> baz', quote: 'foo\nbar\nbaz' }
|
||||||
|
], tokens4);
|
||||||
|
});
|
||||||
|
|
||||||
it('url', () => {
|
it('url', () => {
|
||||||
const tokens = analyze('https://himasaku.net');
|
const tokens = analyze('https://himasaku.net');
|
||||||
assert.deepEqual([{
|
assert.deepEqual([{
|
||||||
|
Reference in New Issue
Block a user