PERF: Cache PrettyText instance for rendering composer preview (#9987)

PERF: Cache PrettyText instance for rendering composer preview (#9987)

Previously we were building pretty-text from scratch on every keypress

diff --git a/app/assets/javascripts/discourse/app/components/d-editor.js b/app/assets/javascripts/discourse/app/components/d-editor.js
index 87cec77..b8fda2c 100644
--- a/app/assets/javascripts/discourse/app/components/d-editor.js
+++ b/app/assets/javascripts/discourse/app/components/d-editor.js
@@ -9,7 +9,7 @@ import discourseComputed, {
 } from "discourse-common/utils/decorators";
 import { categoryHashtagTriggerRule } from "discourse/lib/category-hashtags";
 import { search as searchCategoryTag } from "discourse/lib/category-tag-search";
-import { cookAsync } from "discourse/lib/text";
+import { generateCookFunction } from "discourse/lib/text";
 import { getRegister } from "discourse-common/lib/get-owner";
 import { findRawTemplate } from "discourse-common/lib/raw-templates";
 import { siteDir } from "discourse/lib/text-direction";
@@ -344,15 +344,26 @@ export default Component.extend({
     return toolbar;
   },
 
+  cachedCookAsync(text) {
+    if (this._cachedCookFunction) {
+      return Promise.resolve(this._cachedCookFunction(text));
+    }
+
+    const markdownOptions = this.markdownOptions || {};
+    return generateCookFunction(markdownOptions).then(cook => {
+      this._cachedCookFunction = cook;
+      return cook(text);
+    });
+  },
+
   _updatePreview() {
     if (this._state !== "inDOM") {
       return;
     }
 
     const value = this.value;
-    const markdownOptions = this.markdownOptions || {};
 
-    cookAsync(value, markdownOptions).then(cooked => {
+    this.cachedCookAsync(value).then(cooked => {
       if (this.isDestroyed) {
         return;
       }
diff --git a/app/assets/javascripts/discourse/app/lib/text.js b/app/assets/javascripts/discourse/app/lib/text.js
index afca941..a6d5bf2 100644
--- a/app/assets/javascripts/discourse/app/lib/text.js
+++ b/app/assets/javascripts/discourse/app/lib/text.js
@@ -38,6 +38,15 @@ export function cookAsync(text, options) {
   return loadMarkdownIt().then(() => cook(text, options));
 }
 
+// Warm up pretty text with a set of options and return a function
+// which can be used to cook without rebuilding prettytext every time
+export function generateCookFunction(options) {
+  return loadMarkdownIt().then(() => {
+    const prettyText = createPrettyText(options);
+    return text => prettyText.cook(text);
+  });
+}
+
 export function sanitize(text, options) {
   return textSanitize(text, new WhiteLister(options));
 }

GitHub sha: 833b5d89

This commit appears in #9987 which was approved by eviltrout. It was merged by davidtaylorhq.