index : paprika

A blogging platform written in Rust for Cloudflare Workers, integrated with Standard Notes

diff options
context:
space:
mode:
-rw-r--r--build.rs35
-rw-r--r--package-lock.json6
-rw-r--r--package.json1
-rw-r--r--src/blog.rs34
-rw-r--r--src/hljs.rs17
-rw-r--r--src/hljs_tpl.rs7
-rw-r--r--src/lib.rs1
-rw-r--r--theme/default/head.hbs1
l---------theme/default/static/monokai-sublime.css1
9 files changed, 102 insertions, 1 deletions
diff --git a/build.rs b/build.rs
index 40aee0c..2ab7e9f 100644
--- a/build.rs
+++ b/build.rs
@@ -7,6 +7,12 @@ fn main() {
// Load theme name from config.json and output code to load the theme via include_dir!
let config: serde_json::Value =
serde_json::from_str(&std::fs::read_to_string("./config.json").unwrap()).unwrap();
+
+ generate_theme_loader(&config);
+ generate_hljs_loader(&config);
+}
+
+fn generate_theme_loader(config: &serde_json::Value) {
let theme_name = match config.get("theme") {
Some(name) => name,
None => panic!("Please define `theme` in `config.json`")
@@ -16,4 +22,33 @@ fn main() {
let mut out_file = std::fs::File::create(out_path.join("load_theme.rs")).unwrap();
out_file.write_all(theme_load_code.as_bytes()).unwrap();
out_file.sync_data().unwrap();
+}
+
+fn generate_hljs_loader(config: &serde_json::Value) {
+ println!("cargo:rerun-if-changed=src/hljs_tpl.rs");
+
+ let highlight_lang = match config.get("hljs") {
+ Some(val) => val,
+ None => panic!("Please specify what language for hljs to support in `config.json` with `hljs`")
+ };
+
+ if !highlight_lang.is_array() {
+ panic!("`hljs` is not an array");
+ }
+
+ let highlight_lang = highlight_lang.as_array().unwrap().into_iter().map(|lang| {
+ let lang = lang.as_str().unwrap();
+ // Require only the needed language definition files
+ format!("hljs.registerLanguage('{}', require('highlight.js/lib/languages/{}'));\n", lang, lang)
+ }).collect::<String>();
+
+ let js_code = format!(
+ "const hljs = require(\\\"highlight.js/lib/highlight.js\\\");\n{}module.exports = hljs;",
+ highlight_lang);
+ let rs_code = std::fs::read_to_string("./src/hljs_tpl.rs").unwrap();
+ let rs_code = format!("#[wasm_bindgen(inline_js = \"{}\")]\n{}", js_code, rs_code);
+ let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
+ let mut out_file = std::fs::File::create(out_path.join("load_hljs.rs")).unwrap();
+ out_file.write_all(rs_code.as_bytes()).unwrap();
+ out_file.sync_data().unwrap();
} \ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index bab64b4..a4c924c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1982,6 +1982,12 @@
"minimalistic-assert": "^1.0.1"
}
},
+ "highlight.js": {
+ "version": "9.18.1",
+ "resolved": "https://registry.npm.taobao.org/highlight.js/download/highlight.js-9.18.1.tgz",
+ "integrity": "sha1-7SGqAB/mJSuxCj121HVzxlOf4Tw=",
+ "dev": true
+ },
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/hmac-drbg/download/hmac-drbg-1.0.1.tgz",
diff --git a/package.json b/package.json
index a9e4e5f..aeac0b4 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
"homepage": "https://github.com/PeterCxy/paprika#readme",
"devDependencies": {
"@wasm-tool/wasm-pack-plugin": "^1.2.0",
+ "highlight.js": "^9.18.1",
"webpack": "^4.42.1"
}
}
diff --git a/src/blog.rs b/src/blog.rs
index 28854f0..9acea93 100644
--- a/src/blog.rs
+++ b/src/blog.rs
@@ -120,7 +120,7 @@ impl Post {
// library updates. Updaing this value invalidates all
// existing cache and they will be recompiled when someone
// visits.
-const CACHE_VERSION: &'static str = "0009";
+const CACHE_VERSION: &'static str = "0015";
// The prefix path used for caching remote images
pub const IMG_CACHE_PREFIX: &'static str = "/imgcache/";
@@ -267,6 +267,13 @@ impl PostContentCache {
last_idx = idx + 3 + inserted_id.len();
}
+
+ // Transform all <pre><code> to <pre><code class="hljs">
+ // For syntax highlighting
+ // Note that though the Markdown engine may also insert classes,
+ // because we insert our class before that class, ours will
+ // always take precedence
+ *html = html.replace("<pre><code", "<pre><code class=\"hljs\" ");
}
// Only renders the content and spits out a cache object
@@ -278,10 +285,35 @@ impl PostContentCache {
// because we need to asynchronously transform the events
// which could not be done through mapping on iterators
let mut parser: Vec<Event> = Parser::new_ext(&post.content, Options::all()).collect();
+ let mut in_code_block = false;
+ let mut code_block_lang = "";
for ev in parser.iter_mut() {
match ev {
+ Event::Start(Tag::CodeBlock(block)) => {
+ in_code_block = true;
+ match block {
+ CodeBlockKind::Fenced(lang) => code_block_lang = lang,
+ CodeBlockKind::Indented => code_block_lang = ""
+ }
+ },
+ Event::End(Tag::CodeBlock(_)) => {
+ in_code_block = false;
+ code_block_lang = "";
+ },
Event::Start(tag) | Event::End(tag) => {
Self::transform_tag(tag).await;
+ },
+ Event::Text(text) => {
+ if in_code_block {
+ let highlighted = if code_block_lang != "" {
+ crate::hljs::highlight(code_block_lang, text)
+ } else {
+ crate::hljs::highlight_auto(text)
+ };
+
+ *ev = Event::Html(
+ highlighted.into());
+ }
}
_ => ()
};
diff --git a/src/hljs.rs b/src/hljs.rs
new file mode 100644
index 0000000..7dbdc17
--- /dev/null
+++ b/src/hljs.rs
@@ -0,0 +1,17 @@
+// Simple bindings for Highlight.js
+// We don't have something equivalent in Rust
+// and I don't really want to run these on client
+use js_sys::Reflect;
+use wasm_bindgen::prelude::*;
+
+include!(concat!(env!("OUT_DIR"), "/load_hljs.rs"));
+
+pub fn highlight_auto(code: &str) -> String {
+ Reflect::get(&hljs_highlight_auto(code), &"value".into())
+ .unwrap().as_string().unwrap()
+}
+
+pub fn highlight(lang: &str, code: &str) -> String {
+ Reflect::get(&hljs_highlight(lang, code), &"value".into())
+ .unwrap().as_string().unwrap()
+} \ No newline at end of file
diff --git a/src/hljs_tpl.rs b/src/hljs_tpl.rs
new file mode 100644
index 0000000..19d16bd
--- /dev/null
+++ b/src/hljs_tpl.rs
@@ -0,0 +1,7 @@
+// Will be loaded by build.rs and add #[wasm_bindgen(inline_js = "...")] here
+extern "C" {
+ #[wasm_bindgen(js_name = "highlightAuto")]
+ fn hljs_highlight_auto(code: &str) -> JsValue;
+ #[wasm_bindgen(js_name = "highlight")]
+ fn hljs_highlight(lang: &str, code: &str) -> JsValue;
+} \ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
index fc610f2..20f6858 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,6 +13,7 @@ extern crate serde_json;
mod utils;
mod router;
mod store;
+mod hljs;
mod blog;
mod sn;
mod render;
diff --git a/theme/default/head.hbs b/theme/default/head.hbs
index 18346b9..e2d5d0b 100644
--- a/theme/default/head.hbs
+++ b/theme/default/head.hbs
@@ -2,6 +2,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ #if title }}{{ title }} - {{ blog.title }}{{ else }}{{ blog.title }}{{ /if }}</title>
+ <link rel="stylesheet" href="/static/monokai-sublime.css?ver={{ build_num }}" />
<link rel="stylesheet" href="/static/style.css?ver={{ build_num }}" />
<link rel="alternate" type="application/rss+xml" title="RSS Feed for {{ blog.title }}" href="/feed.xml" />
</head> \ No newline at end of file
diff --git a/theme/default/static/monokai-sublime.css b/theme/default/static/monokai-sublime.css
new file mode 120000
index 0000000..fd6c528
--- /dev/null
+++ b/theme/default/static/monokai-sublime.css
@@ -0,0 +1 @@
+../../../node_modules/highlight.js/styles/monokai-sublime.css \ No newline at end of file