Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@traptitech/traq-markdown-it

Package Overview
Dependencies
Maintainers
2
Versions
86
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@traptitech/traq-markdown-it - npm Package Compare versions

Comparing version 5.3.3 to 5.3.4

dist/index.global.js

149

dist/index.d.ts

@@ -1,12 +0,143 @@

export type { Store } from './Store';
export type { Embedding, EmbeddingOrUrl, ExternalUrl, EmbeddingFile, EmbeddingMessage } from './embeddingExtractor';
export { MarkdownRenderResult, traQMarkdownIt } from './traQMarkdownIt';
export { AnimeEffect, SizeEffect } from './stamp';
export { createHighlightFunc } from './highlight';
import { User, Channel, UserGroup, Stamp } from '@traptitech/traq';
import Token from 'markdown-it/lib/token';
import MarkdownIt, { Options } from 'markdown-it';
import katexE from 'katex';
import Renderer, { RenderRuleRecord } from 'markdown-it/lib/renderer';
export { default as markPlugin } from 'markdown-it-mark';
export { default as spoilerPlugin } from '@traptitech/markdown-it-spoiler';
export { default as stampPlugin, animeEffectSet, sizeEffectSet } from './stamp';
export { default as jsonPlugin } from './json';
export { default as katexPlugin } from '@traptitech/markdown-it-katex';
export { useContainer } from './container';
export { stampCssPlugin } from './stampCss';
interface Store {
getUser(id: string): Readonly<Pick<User, 'id'>> | undefined;
getChannel(id: string): Readonly<Pick<Channel, 'id'>> | undefined;
getChannelPath(id: string): string;
getUserGroup(id: string): Readonly<Pick<UserGroup, 'members'>> | undefined;
getMe(): Readonly<Pick<User, 'id'>> | undefined;
getStampByName(name: string): Readonly<Pick<Stamp, 'name' | 'fileId'>> | undefined;
getUserByName(name: string): Readonly<Pick<User, 'iconFileId'>> | undefined;
}
declare type Embedding = EmbeddingFile | EmbeddingMessage;
declare type EmbeddingOrUrl = InternalUrl | ExternalUrl | Embedding;
declare type EmbeddingBase = {
type: EmbeddingType;
id: string;
};
declare type InternalUrl = {
type: 'internal';
};
declare type ExternalUrl = {
type: 'url';
url: string;
};
declare type EmbeddingType = 'file' | 'message';
declare type EmbeddingFile = EmbeddingBase & {
type: 'file';
};
declare type EmbeddingMessage = EmbeddingBase & {
type: 'message';
};
interface TokenWithEmbeddingData extends Token {
children: TokenWithEmbeddingData[] | null;
embedding?: EmbeddingOrUrl;
}
declare class EmbeddingExtractor {
readonly pathNameEmbeddingTypeMap: ReadonlyMap<string, Embedding['type']>;
readonly embeddingOrigin: string;
constructor(embeddingOrigin: string);
extractType(url: Readonly<URL>): EmbeddingOrUrl['type'];
extractId(url: Readonly<URL>, type: EmbeddingOrUrl['type']): Embedding['id'] | undefined;
urlToEmbeddingData(url: string): EmbeddingOrUrl | undefined;
/**
* パースされたトークンの木構造から再帰的にURLを抽出する
* 同時にトークンにURLの情報を付与する
*/
extractUrlsFromTokens(tokens: readonly TokenWithEmbeddingData[], embeddings?: EmbeddingOrUrl[]): EmbeddingOrUrl[];
/**
* markdownからURL・埋め込みURLを抽出する
*
* @param typeIdExtractor マッチ結果から埋め込み/通常URLの種別を返す関数
* @param idExtractor マッチ結果から埋め込みidを返す関数(通常URL時は`undefined`)
*/
extract(tokens: readonly Token[]): EmbeddingOrUrl[];
/**
* paragraphのchildのtokenの配列から末尾の埋め込みを取り除く
*
* @param tokens ネストされていないtokenの配列
* @returns 取り除かれたらtrue
*/
removeTailEmbeddingsFromTailParagraph(tokens: Array<Readonly<TokenWithEmbeddingData>>): boolean;
/**
* 末尾の埋め込みURLを除去する
*
* 末尾から「改行」の連続(あってもなくてもよい)、「「埋め込み」を含むparagraph」の場合だけを確認する
*/
removeTailEmbeddings(tokens: Array<Readonly<TokenWithEmbeddingData>>): void;
/**
* markdownから埋め込みURLを抽出してすべて置換する
*/
replace(tokens: readonly TokenWithEmbeddingData[]): void;
}
declare class InlineRenderer extends Renderer {
/**
* Rulesのblockルールは使われない
* blockRulesに入れたもののみ利用される
*/
private blockRules;
setRules(defaultRules?: RenderRuleRecord | null, overrideRules?: RenderRuleRecord): void;
renderContent(token: Token): string;
render(tokens: Token[], options: Options, env: any): string;
}
declare type MarkdownRenderResult = {
embeddings: EmbeddingOrUrl[];
rawText: string;
renderedText: string;
};
declare class traQMarkdownIt {
readonly mdOptions: {
readonly breaks: true;
readonly linkify: true;
readonly highlight: (code: string, lang: string) => string;
};
readonly katexOptions: {
readonly katex: typeof katexE;
readonly output: "html";
readonly strict: (errCode: string) => string;
readonly maxSize: 100;
readonly blockClass: "is-scroll";
};
readonly md: MarkdownIt;
readonly embeddingExtractor: EmbeddingExtractor;
readonly inlineRenderer: InlineRenderer;
constructor(store: Readonly<Store>, whitelist: readonly string[] | undefined, embeddingOrigin: string);
setPlugin(store: Readonly<Store>, whitelist: readonly string[]): void;
setRendererRule(): void;
_render(tokens: Token[]): string;
render(text: string): MarkdownRenderResult;
_renderInline(tokens: Token[]): string;
renderInline(text: string): MarkdownRenderResult;
}
declare const animeEffects: readonly ["rotate", "rotate-inv", "wiggle", "parrot", "zoom", "inversion", "turn", "turn-v", "happa", "pyon", "flashy", "pull", "atsumori", "stretch", "stretch-v", "conga", "conga-inv", "marquee", "marquee-inv", "rainbow", "ascension", "shake", "party", "attract"];
declare const sizeEffects: readonly ["ex-large", "large", "small"];
declare const animeEffectSet: ReadonlySet<string>;
declare const sizeEffectSet: ReadonlySet<string>;
declare type AnimeEffect = typeof animeEffects[number];
declare type SizeEffect = typeof sizeEffects[number];
/**
* @param _baseUrl スタンプの画像の`/api/v3`の前につくURLの部分 (tailing slashなし)
*/
declare function stampPlugin(md: MarkdownIt, _store: Readonly<Pick<Store, 'getUserByName' | 'getStampByName'>>, _baseUrl?: string): void;
declare const createHighlightFunc: (preClass: string, withCaption?: boolean, useSubsetForAuto?: boolean) => (code: string, lang: string) => string;
declare function messageExtendsPlugin(md: MarkdownIt, _store: Readonly<Store>): void;
declare const useContainer: (md: MarkdownIt, labels?: readonly string[]) => void;
declare const stampCssPlugin: (md: MarkdownIt, stamps: readonly string[], enableSize?: boolean) => void;
export { AnimeEffect, Embedding, EmbeddingFile, EmbeddingMessage, EmbeddingOrUrl, ExternalUrl, MarkdownRenderResult, SizeEffect, Store, animeEffectSet, createHighlightFunc, messageExtendsPlugin as jsonPlugin, sizeEffectSet, stampCssPlugin, stampPlugin, traQMarkdownIt, useContainer };

@@ -0,8 +1,760 @@

"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
'use strict'
// src/traQMarkdownIt.ts
var _markdownit = require('markdown-it'); var _markdownit2 = _interopRequireDefault(_markdownit);
var _markdownitmark = require('markdown-it-mark'); var _markdownitmark2 = _interopRequireDefault(_markdownitmark);
var _markdownitspoiler = require('@traptitech/markdown-it-spoiler'); var _markdownitspoiler2 = _interopRequireDefault(_markdownitspoiler);
if (process.env.NODE_ENV === 'production') {
module.exports = require('./traq-markdown-it.cjs.production.min.js')
} else {
module.exports = require('./traq-markdown-it.cjs.development.js')
// src/stamp.ts
var _markdownitregexp = require('@traptitech/markdown-it-regexp'); var _markdownitregexp2 = _interopRequireDefault(_markdownitregexp);
// src/util.ts
var htmlReplaceRe = /[&<>"]/g;
var htmlReplaceMap = new Map([
["&", "&amp;"],
["<", "&lt;"],
[">", "&gt;"],
['"', "&quot;"]
]);
var replaceHtmlChar = (ch) => htmlReplaceMap.get(ch);
var escapeHtml = (html) => {
if (htmlReplaceRe.test(html)) {
return html.replace(htmlReplaceRe, replaceHtmlChar);
}
return html;
};
// src/data/stampEffects.ts
var animeEffects = [
"rotate",
"rotate-inv",
"wiggle",
"parrot",
"zoom",
"inversion",
"turn",
"turn-v",
"happa",
"pyon",
"flashy",
"pull",
"atsumori",
"stretch",
"stretch-v",
"conga",
"conga-inv",
"marquee",
"marquee-inv",
"rainbow",
"ascension",
"shake",
"party",
"attract"
];
var sizeEffects = ["ex-large", "large", "small"];
// src/stamp.ts
var store;
var baseUrl = "";
var animeEffectSet = new Set(animeEffects);
var sizeEffectSet = new Set(sizeEffects);
var animeEffectAliasMap = new Map([
["marquee", "conga"],
["marquee-inv", "conga-inv"]
]);
var maxEffectCount = 5;
var wrapWithEffect = (stampHtml, animeEffects2, sizeEffect) => {
const filterOpenTag = animeEffects2.map((e, i) => `<span class="emoji-effect ${e}${i === 0 && sizeEffect ? ` ${sizeEffect}` : ""}">`).join("");
const filterCloseTag = "</span>".repeat(animeEffects2.length);
return filterOpenTag + stampHtml + filterCloseTag;
};
var isSizeEffect = (e) => sizeEffectSet.has(e);
var isAnimeEffect = (e) => animeEffectSet.has(e);
var renderStampDomWithStyle = (rawMatch, stampName, imgTitle, style, effects) => {
const escapedTitle = escapeHtml(imgTitle);
const escapedStyle = escapeHtml(style);
const escapedName = escapeHtml(stampName);
const sizeEffects2 = effects.filter(isSizeEffect);
const animeEffects2 = effects.filter(isAnimeEffect);
if (sizeEffects2.length + animeEffects2.length < effects.length) {
return rawMatch;
}
if (animeEffects2.length > maxEffectCount) {
return rawMatch;
}
const replacedAnimeEffects = animeEffects2.map((e) => {
var _a;
return (_a = animeEffectAliasMap.get(e)) != null ? _a : e;
});
const sizeEffectClass = sizeEffects2[sizeEffects2.length - 1] || "";
const stampHtml = `<i class="emoji message-emoji ${sizeEffectClass}" title=":${escapedTitle}:" style="${escapedStyle};">:${escapedName}:</i>`;
return wrapWithEffect(stampHtml, replacedAnimeEffects, sizeEffectClass);
};
var renderStampDom = (rawMatch, stampName, imgTitle, imgUrl, effects) => renderStampDomWithStyle(rawMatch, stampName, imgTitle, `background-image: url(${imgUrl})`, effects);
var stampReg = /^[a-zA-Z0-9+_-]{1,32}$/;
var hslReg = /(?<color>hsl\(\d+,\s*[\d]+(?:\.[\d]+)?%,\s*[\d]+(?:\.[\d]+)?%\))(?<effects>.*)/;
var hexReg = /0x(?<color>[0-9a-fA-F]{6})(?<effects>.*)/;
var renderHslStamp = (match) => {
const { color, effects } = match.groups;
return renderStampDomWithStyle(`:${match[0]}:`, color, color, `background-color: ${color}`, effects === "" ? [] : effects.split(".").slice(1));
};
var renderHexStamp = (match) => {
const { color, effects } = match.groups;
return renderStampDomWithStyle(`:${match[0]}:`, `0x${color}`, `0x${color}`, `background-color: #${color}`, effects === "" ? [] : effects.split(".").slice(1));
};
var renderUserStamp = (stampName, raw, effects) => {
const userName = stampName.slice(1);
const user = store.getUserByName(userName);
if (!user) {
return raw;
}
return renderStampDom(raw, stampName, stampName, `${baseUrl}/api/v3/files/${user.iconFileId}`, effects);
};
var renderNormalStamp = (stampName, raw, effects) => {
const stamp = store.getStampByName(stampName);
if (!stamp) {
return raw;
}
return renderStampDom(raw, stampName, stamp.name, `${baseUrl}/api/v3/files/${stamp.fileId}`, effects);
};
var renderStamp = (match) => {
const raw = match[0];
const { inner } = match.groups;
const hexMatch = hexReg.exec(inner);
if (hexMatch) {
return renderHexStamp(hexMatch);
}
const hslMatch = hslReg.exec(inner);
if (hslMatch) {
return renderHslStamp(hslMatch);
}
const [stampName = "", ...effects] = inner.split(".");
if (stampName.startsWith("@")) {
return renderUserStamp(stampName, raw, effects);
}
if (!stampReg.exec(stampName)) {
return raw;
}
return renderNormalStamp(stampName, raw, effects);
};
var stampRegExp = /:(?<inner>(?:[a-zA-Z0-9+_-]{1,32}|@(?:Webhook#)?[a-zA-Z0-9_-]+|\w+\([^:<>"'=+!?]+\))[\w+-.]*):/;
function stampPlugin(md, _store, _baseUrl) {
store = _store;
if (_baseUrl) {
baseUrl = _baseUrl;
}
_markdownitregexp2.default.call(void 0, stampRegExp, renderStamp)(md);
}
// src/json.ts
var _markdownitjson = require('markdown-it-json'); var _markdownitjson2 = _interopRequireDefault(_markdownitjson);
var store2;
var isStructData = (data) => {
const anyData = data;
return typeof anyData["type"] === "string" && typeof anyData["raw"] === "string" && typeof anyData["id"] === "string";
};
var validate = (data) => {
if (!isStructData(data)) {
return false;
}
const { type, id } = data;
return type === "user" || type === "channel" && !!store2.getChannel(id) || type === "group";
};
var transformUser = (state, { type, id, raw }) => {
const attributes = [];
const me = store2.getMe();
attributes.push(["href", `javascript:openUserModal('${id}')`]);
if (id === (me == null ? void 0 : me.id)) {
attributes.push(["class", "message-user-link-highlight message-user-link"]);
} else {
attributes.push(["class", "message-user-link"]);
}
let t = state.push("traq_extends_link_open", "a", 1);
t.attrs = attributes;
t.meta = { type, data: id };
t = state.push("text", "", 0);
t.content = raw;
state.push("traq_extends_link_close", "a", -1);
};
var transformUserGroup = (state, { type, id, raw }) => {
var _a, _b;
const attributes = [];
const userGroup = store2.getUserGroup(id);
const me = store2.getMe();
attributes.push(["href", `javascript:openGroupModal('${id}')`]);
if ((_b = (_a = userGroup == null ? void 0 : userGroup.members) == null ? void 0 : _a.some((user) => user.id === (me == null ? void 0 : me.id))) != null ? _b : false) {
attributes.push([
"class",
"message-group-link-highlight message-group-link"
]);
} else {
attributes.push(["class", "message-group-link"]);
}
let t = state.push("traq_extends_link_open", "a", 1);
t.attrs = attributes;
t.meta = { type, data: id };
t = state.push("text", "", 0);
t.content = raw;
state.push("traq_extends_link_close", "a", -1);
};
var transformChannel = (state, { type, id, raw }) => {
const attributes = [];
attributes.push([
"href",
`javascript:changeChannel('${store2.getChannelPath(id)}')`
]);
attributes.push(["class", "message-channel-link"]);
let t = state.push("traq_extends_link_open", "a", 1);
t.attrs = attributes;
t.meta = { type, data: raw };
t = state.push("text", "", 0);
t.content = raw;
state.push("traq_extends_link_close", "a", -1);
};
var transform = (state, data) => {
if (data.type === "user") {
transformUser(state, data);
return;
}
if (data.type === "channel" && store2.getChannel(data.id)) {
transformChannel(state, data);
return;
}
if (data.type === "group") {
transformUserGroup(state, data);
return;
}
};
function messageExtendsPlugin(md, _store) {
store2 = _store;
_markdownitjson2.default.call(void 0, validate, transform)(md);
}
// src/traQMarkdownIt.ts
var _markdownitkatex = require('@traptitech/markdown-it-katex'); var _markdownitkatex2 = _interopRequireDefault(_markdownitkatex);
var _katex = require('katex'); var _katex2 = _interopRequireDefault(_katex);
var _markdownitlinkattributes = require('markdown-it-link-attributes'); var _markdownitlinkattributes2 = _interopRequireDefault(_markdownitlinkattributes);
var _markdownitimagefilter = require('markdown-it-image-filter'); var _markdownitimagefilter2 = _interopRequireDefault(_markdownitimagefilter);
// src/highlight.ts
var _highlightjs = require('highlight.js'); var _highlightjs2 = _interopRequireDefault(_highlightjs);
// src/default/language_subset.ts
var language_subset_default = [
"actionscript",
"awk",
"bash",
"basic",
"bnf",
"brainfuck",
"csharp",
"h",
"cpp",
"cmake",
"coq",
"css",
"clojure",
"coffeescript",
"crystal",
"d",
"dart",
"delphi",
"diff",
"django",
"dockerfile",
"elixir",
"elm",
"fsharp",
"fortran",
"go",
"gradle",
"groovy",
"xml",
"http",
"haml",
"handlebars",
"haxe",
"ini",
"json",
"java",
"javascript",
"kotlin",
"tex",
"less",
"lisp",
"livescript",
"lua",
"makefile",
"markdown",
"mathematica",
"matlab",
"moonscript",
"nginx",
"nimrod",
"ocaml",
"objectivec",
"glsl",
"php",
"perl",
"plaintext",
"pgsql",
"powershell",
"processing",
"prolog",
"protobuf",
"python",
"k",
"r",
"ruby",
"scss",
"sql",
"scheme",
"shell",
"stylus",
"swift",
"twig",
"typescript",
"vbnet",
"vbscript",
"verilog",
"vim",
"x86asm",
"xquery",
"yaml",
"zephir"
];
// src/highlight.ts
var noHighlightRe = /^(no-?highlight|plain|text)$/i;
var createHighlightFunc = (preClass, withCaption = true, useSubsetForAuto = true) => (code, lang) => {
let langName;
let citeTag = "";
if (withCaption) {
const [_langName, langCaption] = lang.split(":");
langName = _langName != null ? _langName : "";
if (langCaption) {
citeTag = `<cite>${escapeHtml(langCaption)}</cite>`;
}
} else {
langName = lang;
}
if (_highlightjs2.default.getLanguage(langName)) {
const result = _highlightjs2.default.highlight(code, { language: langName });
return `<pre class="${preClass}">${citeTag}<code class="lang-${result.language}">${result.value}</code></pre>`;
} else if (noHighlightRe.test(langName)) {
return `<pre class="${preClass}">${citeTag}<code>${escapeHtml(code)}</code></pre>`;
} else {
const result = _highlightjs2.default.highlightAuto(code, useSubsetForAuto ? language_subset_default : void 0);
return `<pre class="${preClass}">${citeTag}<code class="lang-${result.language}">${result.value}</code></pre>`;
}
};
// src/default/domain_whitelist.ts
var domain_whitelist_default = [
"libra.tokyotech.org",
"user-images.githubusercontent.com",
"git.trap.jp",
"wiki.trapti.tech",
"wiki.trap.jp",
"md.trapti.tech",
"md.trap.jp",
"trap.jp",
"traq-dev.tokyotech.org"
];
// src/embeddingExtractor.ts
var uuidRegexp = /^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}/;
var EmbeddingExtractor = class {
constructor(embeddingOrigin) {
this.pathNameEmbeddingTypeMap = new Map([
["files", "file"],
["messages", "message"]
]);
this.embeddingOrigin = embeddingOrigin;
}
extractType(url) {
var _a, _b;
if (url.origin !== this.embeddingOrigin)
return "url";
const name = (_a = url.pathname.split("/")[1]) != null ? _a : "";
return (_b = this.pathNameEmbeddingTypeMap.get(name)) != null ? _b : "internal";
}
extractId(url, type) {
var _a;
if (url.origin !== this.embeddingOrigin || type === "url" || type === "internal")
return void 0;
const id = (_a = url.pathname.split("/")[2]) != null ? _a : "";
return uuidRegexp.test(id) ? id : void 0;
}
urlToEmbeddingData(url) {
let urlObj;
try {
urlObj = new URL(url);
} catch (e2) {
return;
}
if (urlObj.protocol !== "http:" && urlObj.protocol !== "https:") {
return;
}
const type = this.extractType(urlObj);
const id = this.extractId(urlObj, type);
if (type === "internal") {
return;
} else if (type === "url") {
return { type, url };
} else if (id) {
return { type, id };
}
return;
}
extractUrlsFromTokens(tokens, embeddings = []) {
let inSpoilerCount = 0;
for (const token of tokens) {
if (token.children) {
this.extractUrlsFromTokens(token.children, embeddings);
continue;
}
if (token.type === "spoiler_open") {
inSpoilerCount++;
continue;
}
if (token.type === "spoiler_close" && inSpoilerCount > 0) {
inSpoilerCount--;
continue;
}
if (inSpoilerCount > 0)
continue;
if (token.type === "link_open") {
const url = token.attrGet("href");
if (url) {
const embedding = this.urlToEmbeddingData(url);
if (embedding) {
token.embedding = embedding;
embeddings.push(embedding);
}
}
}
}
return embeddings;
}
extract(tokens) {
const embeddings = this.extractUrlsFromTokens(tokens);
const knownIdSet = new Set();
const filteredEmbeddings = [];
for (const embedding of embeddings) {
if (embedding.type === "internal")
continue;
if (embedding.type === "url") {
filteredEmbeddings.push(embedding);
continue;
}
if (!knownIdSet.has(embedding.id)) {
knownIdSet.add(embedding.id);
filteredEmbeddings.push(embedding);
}
}
return filteredEmbeddings;
}
removeTailEmbeddingsFromTailParagraph(tokens) {
var _a;
let isInLink = false;
let removeStartIndex = -1;
for (let i = tokens.length - 1; i >= 0; i--) {
const token = tokens[i];
if (token.type === "softbreak") {
if (removeStartIndex > 0 && !isInLink) {
removeStartIndex = i;
}
continue;
}
if (token.type === "link_open" && token.markup === "linkify") {
isInLink = false;
const type = (_a = token.embedding) == null ? void 0 : _a.type;
if (type === "file" || type === "message") {
removeStartIndex = i;
continue;
}
}
if (isInLink)
continue;
if (token.type === "link_close" && token.markup === "linkify") {
isInLink = true;
continue;
}
if (removeStartIndex === -1) {
return false;
}
break;
}
tokens.splice(removeStartIndex, tokens.length - removeStartIndex);
return true;
}
removeTailEmbeddings(tokens) {
var _a;
let isInParagraph = false;
let paragraphCloseToken = void 0;
for (let i = tokens.length - 1; i >= 0; i--) {
const token = tokens[i];
if (token.type === "hardbreak")
continue;
if (token.type === "paragraph_close") {
isInParagraph = true;
paragraphCloseToken = token;
continue;
}
if (!(isInParagraph && token.type === "inline") || !token.children) {
return;
}
const removed = this.removeTailEmbeddingsFromTailParagraph(token.children);
if (removed) {
tokens.splice(i + 1, tokens.length - (i + 1));
const lastToken = tokens[tokens.length - 1];
if (lastToken && lastToken.type === "inline" && ((_a = lastToken.children) == null ? void 0 : _a.length) === 0) {
tokens.pop();
}
if (paragraphCloseToken) {
tokens.push(paragraphCloseToken);
}
}
}
}
replace(tokens) {
let linkOpenToken = void 0;
for (const token of tokens) {
if (token.children) {
this.replace(token.children);
continue;
}
if (token.embedding) {
linkOpenToken = token;
}
if (!linkOpenToken)
continue;
if (token.type === "text") {
if (linkOpenToken.embedding.type === "message") {
token.content = "[[\u5F15\u7528\u30E1\u30C3\u30BB\u30FC\u30B8]]";
} else if (linkOpenToken.embedding.type === "file") {
token.content = "[[\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB]]";
}
continue;
}
if (token.type === "link_close" && token.markup === "linkify") {
linkOpenToken = void 0;
continue;
}
}
}
};
// src/InlineRenderer.ts
var _renderer = require('markdown-it/lib/renderer'); var _renderer2 = _interopRequireDefault(_renderer);
var InlineRenderer = class extends _renderer2.default {
constructor() {
super(...arguments);
this.blockRules = {};
}
setRules(defaultRules = null, overrideRules = {}) {
if (defaultRules !== null) {
this.rules = __spreadValues({}, defaultRules);
}
this.rules.softbreak = () => " ";
this.blockRules.softbreak = () => " ";
this.rules.hardbreak = () => " ";
this.blockRules.hardbreak = () => " ";
this.rules.image = (tokens, idx) => {
var _a, _b;
const token = tokens[idx];
const attrsStr = ((_a = token.attrs) != null ? _a : []).filter(([attr]) => attr !== "src" && attr !== "alt").map(([key, val]) => `${escapeHtml(key)}="${escapeHtml(val)}"`).join(" ");
return `<a href="${(_b = token.attrGet("src")) != null ? _b : ""}" ${attrsStr} data-is-image>${token.content}</a>`;
};
this.blockRules.code_block = this.rules.code_inline;
this.blockRules.fence = this.rules.code_inline;
this.blockRules.hr = (tokens, idx) => ` ${tokens[idx].markup} `;
this.blockRules.heading_open = (tokens, idx) => `${"#".repeat(+tokens[idx].tag.slice(1))} `;
this.blockRules.blockquote_open = (tokens, idx) => `${tokens[idx].markup} `;
this.blockRules.list_item_open = (tokens, idx) => `${tokens[idx].markup} `;
this.blockRules.th_open = () => "| ";
this.blockRules.td_open = () => "| ";
this.blockRules.tr_close = () => " |";
for (const [key, val] of Object.entries(overrideRules)) {
this.rules[key] = val;
this.blockRules[key] = val;
}
}
renderContent(token) {
return escapeHtml(token.content);
}
render(tokens, options, env) {
var _a, _b;
let result = "";
for (const [i, token] of tokens.entries()) {
const type = token.type;
const blockRule = this.blockRules[type];
if (token.nesting === 1 && ((_a = tokens[i - 1]) == null ? void 0 : _a.nesting) === -1) {
result += " ";
}
if (type === "inline") {
result += this.renderInline((_b = token.children) != null ? _b : [], options, env);
} else if (blockRule !== void 0) {
result += blockRule(tokens, i, options, env, this);
} else {
result += this.renderContent(token);
}
}
return result;
}
};
// src/traQMarkdownIt.ts
var traQMarkdownIt = class {
constructor(store3, whitelist = domain_whitelist_default, embeddingOrigin) {
this.mdOptions = {
breaks: true,
linkify: true,
highlight: createHighlightFunc("traq-code traq-lang")
};
this.katexOptions = {
katex: _katex2.default,
output: "html",
strict: (errCode) => errCode === "unicodeTextInMathMode" ? "ignore" : "warn",
maxSize: 100,
blockClass: "is-scroll"
};
this.md = new (0, _markdownit2.default)(this.mdOptions);
this.inlineRenderer = new InlineRenderer();
this.embeddingExtractor = new EmbeddingExtractor(embeddingOrigin);
this.setRendererRule();
this.setPlugin(store3, whitelist);
}
setPlugin(store3, whitelist) {
this.md.use(_markdownitmark2.default).use(_markdownitspoiler2.default, true).use(messageExtendsPlugin, store3).use(stampPlugin, store3).use(_markdownitkatex2.default, this.katexOptions).use(_markdownitlinkattributes2.default, {
attrs: {
target: "_blank",
rel: "nofollow noopener noreferrer"
}
}).use(_markdownitimagefilter2.default.call(void 0, whitelist, { httpsOnly: true }));
const dummyMd = new (0, _markdownit2.default)(this.mdOptions);
dummyMd.use(_markdownitkatex2.default, __spreadProps(__spreadValues({}, this.katexOptions), {
maxSize: 1,
macros: {
"\\Huge": () => "",
"\\huge": () => "",
"\\LARGE": () => "",
"\\Large": () => "",
"\\large": () => ""
}
}));
this.inlineRenderer.setRules(this.md.renderer.rules, {
math_inline: dummyMd.renderer.rules.math_inline,
math_block: dummyMd.renderer.rules.math_inline
});
}
setRendererRule() {
this.md.block.State.prototype.skipEmptyLines = function skipEmptyLines(from) {
for (let max = this.lineMax; from < max; from++) {
if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) {
break;
}
this.push("hardbreak", "br", 0);
}
return from;
};
}
_render(tokens) {
return this.md.renderer.render(tokens, this.mdOptions, {});
}
render(text) {
const parsed = this.md.parse(text, {});
const embeddings = this.embeddingExtractor.extract(parsed);
this.embeddingExtractor.removeTailEmbeddings(parsed);
return {
embeddings,
rawText: text,
renderedText: this._render(parsed)
};
}
_renderInline(tokens) {
return this.inlineRenderer.render(tokens, this.mdOptions, {});
}
renderInline(text) {
const parsed = this.md.parse(text, {});
const embeddings = this.embeddingExtractor.extract(parsed);
this.embeddingExtractor.removeTailEmbeddings(parsed);
this.embeddingExtractor.replace(parsed);
return {
embeddings,
rawText: text,
renderedText: this._renderInline(parsed)
};
}
};
// src/index.ts
// src/container.ts
var _markdownitcontainer = require('markdown-it-container'); var _markdownitcontainer2 = _interopRequireDefault(_markdownitcontainer);
var defaultLabels = ["success", "info", "warning", "danger"];
var useContainer = (md, labels = defaultLabels) => {
labels.forEach((label) => {
md.use(_markdownitcontainer2.default, label);
});
};
// src/stampCss.ts
var stampRegExp2 = /:((?:[a-zA-Z0-9+_-]{1,32}|@[a-zA-Z0-9_-]+)[\w+-.]*):/;
var stampRegExpWithSize = /:((?:[a-zA-Z0-9+_-]{1,32}|@[a-zA-Z0-9_-]+)[\w+-.]*)[:;]/;
var renderUserStamp2 = (wrappedName, name, size) => {
const username = name.slice(1);
return `<i class="emoji s${size}" title="${wrappedName}" style="background-image: url(https://q.trap.jp/api/v3/public/icon/${username})">${wrappedName}</i>`;
};
var renderStamp2 = (wrappedName, name, size) => {
const className = `e_${name.replace(/\+/g, "_-plus-_")}`;
return `<i class="emoji s${size} ${className}" title="${wrappedName}">${wrappedName}</i>`;
};
var stampCssPlugin = (md, stamps, enableSize = false) => {
_markdownitregexp2.default.call(void 0, enableSize ? stampRegExpWithSize : stampRegExp2, ([wrappedName = "", name = ""]) => {
const size = enableSize ? wrappedName.endsWith(":") ? 32 : 16 : 24;
if (name.startsWith("@")) {
return renderUserStamp2(wrappedName, name, size);
}
if (!stamps.includes(name))
return wrappedName;
return renderStamp2(wrappedName, name, size);
})(md);
};
exports.animeEffectSet = animeEffectSet; exports.createHighlightFunc = createHighlightFunc; exports.jsonPlugin = messageExtendsPlugin; exports.katexPlugin = _markdownitkatex2.default; exports.markPlugin = _markdownitmark2.default; exports.sizeEffectSet = sizeEffectSet; exports.spoilerPlugin = _markdownitspoiler2.default; exports.stampCssPlugin = stampCssPlugin; exports.stampPlugin = stampPlugin; exports.traQMarkdownIt = traQMarkdownIt; exports.useContainer = useContainer;
//# sourceMappingURL=index.js.map

63

package.json
{
"name": "@traptitech/traq-markdown-it",
"version": "5.3.3",
"version": "5.3.4",
"description": "Markdown parser for traQ.",
"main": "dist/index.js",
"module": "dist/traq-markdown-it.esm.js",
"module": "dist/index.mjs",
"typings": "dist/index.d.ts",

@@ -13,10 +13,11 @@ "files": [

"engines": {
"node": ">=10"
"node": ">=12"
},
"scripts": {
"start": "tsdx watch",
"build": "tsdx build && npm run build:css",
"start": "tsup src/index.ts --watch src",
"build": "tsup src/index.ts --dts --sourcemap --format esm,cjs,iife && npm run build:css",
"build:css": "sass ./src/css/index.scss ./dist/index.css",
"test": "tsdx test",
"lint": "tsdx lint"
"test": "jest",
"lint": "eslint --cache \"{src,test}/**/*.ts\"",
"prepare": "husky install"
},

@@ -37,6 +38,5 @@ "repository": {

"@traptitech/markdown-it-spoiler": "^1.1.6",
"core-js": "^3.12.0",
"highlight.js": "^11.0.0",
"katex": "^0.13.9",
"markdown-it": "^12.0.6",
"highlight.js": "^11.1.0",
"katex": "^0.13.13",
"markdown-it": "^12.1.0",
"markdown-it-container": "^3.0.0",

@@ -49,29 +49,22 @@ "markdown-it-image-filter": "^1.1.0",

"devDependencies": {
"@babel/preset-env": "^7.14.1",
"@traptitech/traq": "3.6.6-0",
"@types/jest": "^25.2.3",
"@types/katex": "^0.11.0",
"@types/markdown-it": "12.0.2",
"@typescript-eslint/eslint-plugin": "^4.22.1",
"@typescript-eslint/parser": "^4.22.1",
"eslint": "^7.25.0",
"eslint-config-prettier": "^7.2.0",
"@traptitech/traq": "3.7.7-3",
"@types/jest": "^26.0.24",
"@types/katex": "^0.11.1",
"@types/markdown-it": "12.0.3",
"@typescript-eslint/eslint-plugin": "^4.28.4",
"@typescript-eslint/parser": "^4.28.4",
"es-jest": "^1.2.0",
"eslint": "^7.31.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-tree-shaking": "^1.9.0",
"husky": "^4.3.0",
"prettier": "^2.2.1",
"sass": "^1.34.0",
"eslint-plugin-tree-shaking": "^1.9.2",
"husky": "^7.0.1",
"jest": "^27.0.6",
"prettier": "^2.3.2",
"sass": "^1.35.2",
"ts-dedent": "^2.1.1",
"tsdx": "^0.14.1",
"tslib": "^2.2.0",
"typescript": "^4.2.4"
"tsup": "^4.12.5",
"typescript": "^4.3.5"
},
"husky": {
"hooks": {
"pre-commit": "tsdx lint"
}
},
"sideEffects": [
"core-js/*"
]
"sideEffects": false
}

@@ -6,30 +6,16 @@ # traq-markdown-it

[![codecov](https://codecov.io/gh/traPtitech/traq-markdown-it/branch/master/graph/badge.svg)](https://codecov.io/gh/traPtitech/traq-markdown-it)
[![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=traPtitech/traq-markdown-it)](https://dependabot.com)
Markdown parser for traQ.
This project was bootstrapped with [TSDX](https://github.com/jaredpalmer/tsdx).
## Local Development
Below is a list of commands you will probably find useful.
### `npm start`
Rebuild when ts files changed.
Runs the project in development/watch mode. Your project will be rebuilt upon changes. TSDX has a special logger for you convenience. Error messages are pretty printed and formatted for compatibility VS Code's Problems tab.
<img src="https://user-images.githubusercontent.com/4060187/52168303-574d3a00-26f6-11e9-9f3b-71dbec9ebfcb.gif" width="600" />
Your library will be rebuilt if you make edits.
### `npm run build`
Build ts & sass.
Bundles the package to the `dist` folder.
The package is optimized and bundled with Rollup into multiple formats (CommonJS, UMD, and ES Module).
<img src="https://user-images.githubusercontent.com/4060187/52168322-a98e5b00-26f6-11e9-8cf6-222d716b75ef.gif" width="600" />
### `npm test`
Runs tests.
Runs the test watcher (Jest) in an interactive mode.
By default, runs tests related to files changed since the last commit.
### `npm run lint`
Runs lint.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc