FEATURE: Customizable rules and plugins for `PrettyText.markdown`.
This commit extends the options which can be passed to `PrettyText.markdown` so that which Markdown-it rules and Discourse Markdown plugins to be used when rendering a text can be customizable. Currently, this extension is mainly used by plugins.
This commit is contained in:
parent
9f5c8644d0
commit
c2afc3915b
|
@ -1493,6 +1493,45 @@ var bar = 'bar';
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("customizing markdown-it rules", function (assert) {
|
||||||
|
assert.cookedOptions(
|
||||||
|
"**bold**",
|
||||||
|
{ markdownItRules: [] },
|
||||||
|
"<p>**bold**</p>",
|
||||||
|
"does not apply bold markdown when rule is not enabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.cookedOptions(
|
||||||
|
"**bold**",
|
||||||
|
{ markdownItRules: ["emphasis"] },
|
||||||
|
"<p><strong>bold</strong></p>",
|
||||||
|
"applies bold markdown when rule is enabled"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("features override", function (assert) {
|
||||||
|
assert.cookedOptions(
|
||||||
|
":grin: @sam",
|
||||||
|
{ featuresOverride: [] },
|
||||||
|
"<p>:grin: @sam</p>",
|
||||||
|
"does not cook emojis when Discourse markdown engines are disabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.cookedOptions(
|
||||||
|
":grin: @sam",
|
||||||
|
{ featuresOverride: ["emoji"] },
|
||||||
|
'<p><img src="/images/emoji/google_classic/grin.png?v=10" title=":grin:" class="emoji" alt=":grin:"> @sam</p>',
|
||||||
|
"cooks emojis when only the emoji markdown engine is enabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.cookedOptions(
|
||||||
|
":grin: @sam",
|
||||||
|
{ featuresOverride: ["mentions", "text-post-process"] },
|
||||||
|
`<p>:grin: <span class="mention">@sam</span></p>`,
|
||||||
|
"cooks mentions when only the mentions markdown engine is enabled"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("emoji", function (assert) {
|
test("emoji", function (assert) {
|
||||||
assert.cooked(
|
assert.cooked(
|
||||||
":smile:",
|
":smile:",
|
||||||
|
|
|
@ -353,6 +353,12 @@ export function setup(opts, siteSettings, state) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (opts.featuresOverride) {
|
||||||
|
Object.keys(opts.features).forEach((feature) => {
|
||||||
|
opts.features[feature] = opts.featuresOverride.includes(feature);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let copy = {};
|
let copy = {};
|
||||||
Object.keys(opts).forEach((entry) => {
|
Object.keys(opts).forEach((entry) => {
|
||||||
copy[entry] = opts[entry];
|
copy[entry] = opts[entry];
|
||||||
|
@ -371,14 +377,22 @@ export function setup(opts, siteSettings, state) {
|
||||||
enableDiffhtmlPreview: siteSettings.enable_diffhtml_preview,
|
enableDiffhtmlPreview: siteSettings.enable_diffhtml_preview,
|
||||||
};
|
};
|
||||||
|
|
||||||
opts.engine = window.markdownit({
|
const markdownitOpts = {
|
||||||
discourse: opts.discourse,
|
discourse: opts.discourse,
|
||||||
html: true,
|
html: true,
|
||||||
breaks: !siteSettings.traditional_markdown_linebreaks,
|
breaks: !siteSettings.traditional_markdown_linebreaks,
|
||||||
xhtmlOut: false,
|
xhtmlOut: false,
|
||||||
linkify: siteSettings.enable_markdown_linkify,
|
linkify: siteSettings.enable_markdown_linkify,
|
||||||
typographer: siteSettings.enable_markdown_typographer,
|
typographer: siteSettings.enable_markdown_typographer,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (opts.discourse.markdownItRules !== undefined) {
|
||||||
|
opts.engine = window
|
||||||
|
.markdownit("zero", markdownitOpts) // Preset for "zero", https://github.com/markdown-it/markdown-it/blob/master/lib/presets/zero.js
|
||||||
|
.enable(opts.discourse.markdownItRules);
|
||||||
|
} else {
|
||||||
|
opts.engine = window.markdownit(markdownitOpts);
|
||||||
|
}
|
||||||
|
|
||||||
const quotation_marks = siteSettings.markdown_typographer_quotation_marks;
|
const quotation_marks = siteSettings.markdown_typographer_quotation_marks;
|
||||||
if (quotation_marks) {
|
if (quotation_marks) {
|
||||||
|
|
|
@ -38,6 +38,8 @@ export function buildOptions(state) {
|
||||||
customEmojiTranslation,
|
customEmojiTranslation,
|
||||||
watchedWordsReplace,
|
watchedWordsReplace,
|
||||||
watchedWordsLink,
|
watchedWordsLink,
|
||||||
|
featuresOverride,
|
||||||
|
markdownItRules,
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
let features = {};
|
let features = {};
|
||||||
|
@ -76,6 +78,8 @@ export function buildOptions(state) {
|
||||||
disableEmojis,
|
disableEmojis,
|
||||||
watchedWordsReplace,
|
watchedWordsReplace,
|
||||||
watchedWordsLink,
|
watchedWordsLink,
|
||||||
|
featuresOverride,
|
||||||
|
markdownItRules,
|
||||||
};
|
};
|
||||||
|
|
||||||
// note, this will mutate options due to the way the API is designed
|
// note, this will mutate options due to the way the API is designed
|
||||||
|
|
|
@ -156,6 +156,17 @@ module PrettyText
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Acceptable options:
|
||||||
|
#
|
||||||
|
# disable_emojis - Disables the emoji markdown engine.
|
||||||
|
# features - A hash where the key is the markdown feature name and the value is a boolean to enable/disable the markdown feature.
|
||||||
|
# The hash is merged into the default features set in pretty-text.js which can be used to add new features or disable existing features.
|
||||||
|
# features_override - An array of markdown feature names to override the default markdown feature set. Currently used by plugins to customize what features should be enabled
|
||||||
|
# when rendering markdown.
|
||||||
|
# markdown_it_rules - An array of markdown rule names which will be applied to the markdown-it engine. Currently used by plugins to customize what markdown-it rules should be
|
||||||
|
# enabled when rendering markdown.
|
||||||
|
# topic_id - Topic id for the post being cooked.
|
||||||
|
# user_id - User id for the post being cooked.
|
||||||
def self.markdown(text, opts = {})
|
def self.markdown(text, opts = {})
|
||||||
# we use the exact same markdown converter as the client
|
# we use the exact same markdown converter as the client
|
||||||
# TODO: use the same extensions on both client and server (in particular the template for mentions)
|
# TODO: use the same extensions on both client and server (in particular the template for mentions)
|
||||||
|
@ -175,6 +186,8 @@ module PrettyText
|
||||||
__paths = #{paths_json};
|
__paths = #{paths_json};
|
||||||
__optInput.getURL = __getURL;
|
__optInput.getURL = __getURL;
|
||||||
#{"__optInput.features = #{opts[:features].to_json};" if opts[:features]}
|
#{"__optInput.features = #{opts[:features].to_json};" if opts[:features]}
|
||||||
|
#{"__optInput.featuresOverride = #{opts[:features_override].to_json};" if opts[:features_override]}
|
||||||
|
#{"__optInput.markdownItRules = #{opts[:markdown_it_rules].to_json};" if opts[:markdown_it_rules]}
|
||||||
__optInput.getCurrentUser = __getCurrentUser;
|
__optInput.getCurrentUser = __getCurrentUser;
|
||||||
__optInput.lookupAvatar = __lookupAvatar;
|
__optInput.lookupAvatar = __lookupAvatar;
|
||||||
__optInput.lookupPrimaryUserGroup = __lookupPrimaryUserGroup;
|
__optInput.lookupPrimaryUserGroup = __lookupPrimaryUserGroup;
|
||||||
|
@ -190,8 +203,8 @@ module PrettyText
|
||||||
__optInput.watchedWordsLink = #{WordWatcher.word_matcher_regexps(:link).to_json};
|
__optInput.watchedWordsLink = #{WordWatcher.word_matcher_regexps(:link).to_json};
|
||||||
JS
|
JS
|
||||||
|
|
||||||
if opts[:topicId]
|
if opts[:topic_id]
|
||||||
buffer << "__optInput.topicId = #{opts[:topicId].to_i};\n"
|
buffer << "__optInput.topicId = #{opts[:topic_id].to_i};\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts[:user_id]
|
if opts[:user_id]
|
||||||
|
@ -279,10 +292,6 @@ module PrettyText
|
||||||
|
|
||||||
def self.cook(text, opts = {})
|
def self.cook(text, opts = {})
|
||||||
options = opts.dup
|
options = opts.dup
|
||||||
|
|
||||||
# we have a minor inconsistency
|
|
||||||
options[:topicId] = opts[:topic_id]
|
|
||||||
|
|
||||||
working_text = text.dup
|
working_text = text.dup
|
||||||
|
|
||||||
sanitized = markdown(working_text, options)
|
sanitized = markdown(working_text, options)
|
||||||
|
|
|
@ -2072,4 +2072,32 @@ HTML
|
||||||
|
|
||||||
expect(cooked).to match_html(html)
|
expect(cooked).to match_html(html)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "customizing markdown-it rules" do
|
||||||
|
it 'customizes the markdown-it rules correctly' do
|
||||||
|
cooked = PrettyText.cook('This is some text **bold**', markdown_it_rules: [])
|
||||||
|
|
||||||
|
expect(cooked).to eq("<p>This is some text **bold**</p>")
|
||||||
|
|
||||||
|
cooked = PrettyText.cook('This is some text **bold**', markdown_it_rules: ["emphasis"])
|
||||||
|
|
||||||
|
expect(cooked).to eq("<p>This is some text <strong>bold</strong></p>")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "enabling/disabling features" do
|
||||||
|
it "allows features to be overriden" do
|
||||||
|
cooked = PrettyText.cook(':grin: @mention', features_override: [])
|
||||||
|
|
||||||
|
expect(cooked).to eq("<p>:grin: @mention</p>")
|
||||||
|
|
||||||
|
cooked = PrettyText.cook(':grin: @mention', features_override: ["emoji"])
|
||||||
|
|
||||||
|
expect(cooked).to eq("<p><img src=\"/images/emoji/twitter/grin.png?v=10\" title=\":grin:\" class=\"emoji\" alt=\":grin:\"> @mention</p>")
|
||||||
|
|
||||||
|
cooked = PrettyText.cook(':grin: @mention', features_override: ["mentions", "text-post-process"])
|
||||||
|
|
||||||
|
expect(cooked).to eq("<p>:grin: <span class=\"mention\">@mention</span></p>")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue