discourse/app/assets/javascripts/discourse-markdown-it/src/index.js
Godfrey Chan 9a1695ccc1
DEV: remove markdown-it-bundle and custom build code (#23859)
With Embroider, we can rely on async `import()` to do the splitting
for us.

This commit extracts from `pretty-text` all the parts that are
meant to be loaded async into a new `discourse-markdown-it` package
that is also a V2 addon (meaning that all files are presumed unused
until they are imported, aka "static").

Mostly I tried to keep the very discourse specific stuff (accessing
site settings and loading plugin features) inside discourse proper,
while the new package aims to have some resembalance of a general
purpose library, a MarkdownIt++ if you will. It is far from perfect
because of how all the "options" stuff work but I think it's a good
start for more refactorings (clearing up the interfaces) to happen
later.

With this, pretty-text and app/lib/text are mostly a kitchen sink
of loosely related text processing utilities.

After the refactor, a lot more code related to setting up the
engine are now loaded lazily, which should be a pretty nice win. I
also noticed that we are currently pulling in the `xss` library at
initial load to power the "sanitize" stuff, but I suspect with a
similar refactoring effort those usages can be removed too. (See
also #23790).

This PR does not attempt to fix the sanitize issue, but I think it
sets things up on the right trajectory for that to happen later.

Co-authored-by: David Taylor <david@taylorhq.com>
2023-11-06 16:59:49 +00:00

75 lines
1.7 KiB
JavaScript

import { cook as cookIt } from "./engine";
import DEFAULT_FEATURES from "./features";
import buildOptions from "./options";
import setup from "./setup";
function NOOP(ident) {
return ident;
}
export default class DiscourseMarkdownIt {
static withDefaultFeatures() {
return this.withFeatures(DEFAULT_FEATURES);
}
static withCustomFeatures(features) {
return this.withFeatures([...DEFAULT_FEATURES, ...features]);
}
static withFeatures(features) {
const withOptions = (options) => this.withOptions(features, options);
return { withOptions };
}
static withOptions(features, rawOptions) {
const { options, siteSettings, state } = buildOptions(rawOptions);
// note, this will mutate options due to the way the API is designed
// may need a refactor
setup(features, options, siteSettings, state);
return new DiscourseMarkdownIt(options);
}
static minimal() {
return this.withFeatures([]).withOptions({ siteSettings: {} });
}
constructor(options) {
if (!options.setup) {
throw new Error(
"Cannot construct DiscourseMarkdownIt from raw options, " +
"use DiscourseMarkdownIt.withOptions() instead"
);
}
this.options = options;
}
disableSanitizer() {
this.options.sanitizer = this.options.discourse.sanitizer = NOOP;
}
cook(raw) {
if (!raw || raw.length === 0) {
return "";
}
let result;
result = cookIt(raw, this.options);
return result ? result : "";
}
parse(markdown, env = {}) {
return this.options.engine.parse(markdown, env);
}
sanitize(html) {
return this.options.sanitizer(html).trim();
}
get linkify() {
return this.options.engine.linkify;
}
}