Skip to content

Commit

Permalink
Run frontend lint in CI (#180)
Browse files Browse the repository at this point in the history
Adds `npm run lint` to CI and fixes all the previous lint errors.
  • Loading branch information
mikkeldenker authored Mar 13, 2024
1 parent 7bca42f commit 5ce97ab
Show file tree
Hide file tree
Showing 22 changed files with 183 additions and 127 deletions.
53 changes: 39 additions & 14 deletions crates/core/src/api/autosuggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,32 @@ use axum::{extract, response::IntoResponse, Json};
use serde::Serialize;
use utoipa::{IntoParams, ToSchema};

use super::State;
use crate::highlighted::HighlightedFragment;

const HIGHLIGHTED_PREFIX: &str = "<b style=\"font-weight: 500;\">";
const HIGHLIGHTED_POSTFIX: &str = "</b>";
use super::State;

fn highlight(query: &str, suggestion: &str) -> String {
fn highlight(query: &str, suggestion: &str) -> Vec<HighlightedFragment> {
let idx = suggestion
.chars()
.zip(query.chars())
.position(|(suggestion_char, query_char)| suggestion_char != query_char)
.unwrap_or(query.chars().count());

let mut new_suggestion: String = suggestion.chars().take(idx).collect();
new_suggestion += HIGHLIGHTED_PREFIX;
new_suggestion += suggestion.chars().skip(idx).collect::<String>().as_str();
new_suggestion += HIGHLIGHTED_POSTFIX;
let mut new_suggestion = vec![HighlightedFragment::new_unhighlighted(
suggestion.chars().take(idx).collect(),
)];

new_suggestion.push(HighlightedFragment::new_highlighted(
suggestion.chars().skip(idx).collect::<String>(),
));

new_suggestion
}

#[derive(Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct Suggestion {
highlighted: String,
highlighted: Vec<HighlightedFragment>,
raw: String,
}

Expand Down Expand Up @@ -95,28 +98,50 @@ pub async fn browser(

#[cfg(test)]
mod tests {
use crate::highlighted::HighlightedKind;

use super::*;

const HIGHLIGHTED_PREFIX: &str = "<b>";
const HIGHLIGHTED_POSTFIX: &str = "</b>";

fn highlight_fragments(frags: &[HighlightedFragment]) -> String {
frags
.iter()
.map(|frag| match frag.kind {
HighlightedKind::Normal => frag.text.clone(),
HighlightedKind::Highlighted => {
format!(
"{}{}{}",
HIGHLIGHTED_PREFIX,
frag.text(),
HIGHLIGHTED_POSTFIX
)
}
})
.collect()
}

#[test]
fn suffix_highlight() {
assert_eq!(
highlight("", "test"),
highlight_fragments(&highlight("", "test")),
format!("{HIGHLIGHTED_PREFIX}test{HIGHLIGHTED_POSTFIX}")
);
assert_eq!(
highlight("t", "test"),
highlight_fragments(&highlight("t", "test")),
format!("t{HIGHLIGHTED_PREFIX}est{HIGHLIGHTED_POSTFIX}")
);
assert_eq!(
highlight("te", "test"),
highlight_fragments(&highlight("te", "test")),
format!("te{HIGHLIGHTED_PREFIX}st{HIGHLIGHTED_POSTFIX}")
);
assert_eq!(
highlight("tes", "test"),
highlight_fragments(&highlight("tes", "test")),
format!("tes{HIGHLIGHTED_PREFIX}t{HIGHLIGHTED_POSTFIX}")
);
assert_eq!(
highlight("test", "test"),
highlight_fragments(&highlight("test", "test")),
format!("test{HIGHLIGHTED_PREFIX}{HIGHLIGHTED_POSTFIX}")
);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/core/src/api/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ use utoipa_swagger_ui::SwaggerUi;
crate::search_prettifier::CodeOrText,
crate::snippet::TextSnippet,
crate::snippet::TextSnippetFragment,
crate::snippet::TextSnippetFragmentKind,
crate::highlighted::HighlightedFragment,
crate::highlighted::HighlightedKind,
crate::entity_index::entity::EntitySnippet,
crate::entity_index::entity::EntitySnippetFragment,
Expand Down
55 changes: 55 additions & 0 deletions crates/core/src/highlighted.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Stract is an open source web search engine.
// Copyright (C) 2024 Stract ApS
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use utoipa::ToSchema;

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, ToSchema)]
#[serde(rename_all = "camelCase")]
pub enum HighlightedKind {
Normal,
Highlighted,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct HighlightedFragment {
pub kind: HighlightedKind,
pub text: String,
}

impl HighlightedFragment {
pub fn new_unhighlighted(text: String) -> Self {
Self::new_normal(text)
}

pub fn new_normal(text: String) -> Self {
Self {
kind: HighlightedKind::Normal,
text,
}
}

pub fn new_highlighted(text: String) -> Self {
Self {
kind: HighlightedKind::Highlighted,
text,
}
}

pub fn text(&self) -> &str {
&self.text
}
}
5 changes: 3 additions & 2 deletions crates/core/src/inverted_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@ use url::Url;
use crate::collector::{Hashes, MainCollector};
use crate::config::SnippetConfig;
use crate::fastfield_reader::FastFieldReader;
use crate::highlighted::HighlightedFragment;
use crate::query::shortcircuit::ShortCircuitQuery;
use crate::query::Query;
use crate::ranking::initial::Score;
use crate::ranking::pipeline::RecallRankingWebpage;
use crate::ranking::SignalAggregator;
use crate::schema::{FastField, Field, TextField};
use crate::search_ctx::Ctx;
use crate::snippet;
use crate::snippet::TextSnippet;
use crate::snippet::{self, TextSnippetFragment};
use crate::tokenizer::{
BigramTokenizer, Identity, JsonField, SiteOperatorUrlTokenizer, TrigramTokenizer,
};
Expand Down Expand Up @@ -475,7 +476,7 @@ impl InvertedIndex {
};

page.snippet = TextSnippet {
fragments: vec![TextSnippetFragment::new_unhighlighted(snippet)],
fragments: vec![HighlightedFragment::new_unhighlighted(snippet)],
};
} else {
let min_body_len = if url.is_homepage() {
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ mod executor;
mod external_sort;
mod fastfield_reader;
pub mod feed;
mod highlighted;
mod human_website_annotations;
pub mod hyperloglog;
pub mod image_store;
Expand Down
24 changes: 15 additions & 9 deletions crates/core/src/search_prettifier/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use url::Url;
use utoipa::ToSchema;

use crate::{
highlighted::HighlightedFragment,
inverted_index::RetrievedWebpage,
ranking::{Signal, SignalScore},
snippet::TextSnippet,
Expand Down Expand Up @@ -57,12 +58,12 @@ pub enum RichSnippet {
#[serde(rename_all = "camelCase")]
pub struct HighlightedSpellCorrection {
pub raw: String,
pub highlighted: String,
pub highlighted: Vec<HighlightedFragment>,
}

impl From<web_spell::Correction> for HighlightedSpellCorrection {
fn from(correction: web_spell::Correction) -> Self {
let mut highlighted = String::new();
let mut highlighted = Vec::new();
let mut raw = String::new();

for term in correction.terms {
Expand All @@ -71,22 +72,27 @@ impl From<web_spell::Correction> for HighlightedSpellCorrection {
orig: _,
correction,
} => {
highlighted
.push_str(&("<b><i>".to_string() + correction.as_str() + "</i></b>"));
let mut correction = correction.trim().to_string();
correction.push(' ');

highlighted.push(HighlightedFragment::new_highlighted(correction.clone()));
raw.push_str(&correction);
}
CorrectionTerm::NotCorrected(orig) => {
highlighted.push_str(&orig);
let mut orig = orig.trim().to_string();
orig.push(' ');

highlighted.push(HighlightedFragment::new_normal(orig.clone()));
raw.push_str(&orig);
}
}

raw.push(' ');
highlighted.push(' ');
}

raw = raw.trim_end().to_string();
highlighted = highlighted.trim_end().to_string();

if let Some(last) = highlighted.last_mut() {
last.text = last.text.trim_end().to_string();
}

Self { raw, highlighted }
}
Expand Down
54 changes: 14 additions & 40 deletions crates/core/src/snippet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use std::ops::Range;

use crate::config::SnippetConfig;
use crate::highlighted::{HighlightedFragment, HighlightedKind};
use crate::query::Query;
use crate::tokenizer::{BigramTokenizer, Normal, Stemmed, Tokenizer, TrigramTokenizer};
use crate::web_spell::sentence_ranges;
Expand Down Expand Up @@ -46,37 +47,10 @@ struct PassageCandidate {
doc_terms: HashMap<String, u64>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, ToSchema)]
#[serde(rename_all = "camelCase")]
pub enum TextSnippetFragmentKind {
Normal,
Highlighted,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct TextSnippetFragment {
pub kind: TextSnippetFragmentKind,
text: String,
}

impl TextSnippetFragment {
pub fn new_unhighlighted(text: String) -> Self {
TextSnippetFragment {
kind: TextSnippetFragmentKind::Normal,
text,
}
}

pub fn text(&self) -> &str {
&self.text
}
}

#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct TextSnippet {
pub fragments: Vec<TextSnippetFragment>,
pub fragments: Vec<HighlightedFragment>,
}

impl TextSnippet {
Expand Down Expand Up @@ -125,23 +99,23 @@ impl SnippetBuilder {

for range in self.highlights {
if range.start > last_end {
fragments.push(TextSnippetFragment {
kind: TextSnippetFragmentKind::Normal,
fragments.push(HighlightedFragment {
kind: HighlightedKind::Normal,
text: self.fragment[last_end..range.start].to_string(),
});
}

fragments.push(TextSnippetFragment {
kind: TextSnippetFragmentKind::Highlighted,
fragments.push(HighlightedFragment {
kind: HighlightedKind::Highlighted,
text: self.fragment[range.start..range.end].to_string(),
});

last_end = range.end;
}

if last_end < self.fragment.len() {
fragments.push(TextSnippetFragment {
kind: TextSnippetFragmentKind::Normal,
fragments.push(HighlightedFragment {
kind: HighlightedKind::Normal,
text: self.fragment[last_end..].to_string(),
});
}
Expand Down Expand Up @@ -301,7 +275,7 @@ fn snippet_string(
&& snip
.fragments
.iter()
.any(|f| f.kind == TextSnippetFragmentKind::Highlighted)
.any(|f| f.kind == HighlightedKind::Highlighted)
{
return snip;
}
Expand All @@ -327,8 +301,8 @@ pub fn generate(query: &Query, text: &str, region: &Region, config: SnippetConfi

if text.is_empty() {
return TextSnippet {
fragments: vec![TextSnippetFragment {
kind: TextSnippetFragmentKind::Normal,
fragments: vec![HighlightedFragment {
kind: HighlightedKind::Normal,
text: "".to_string(),
}],
};
Expand Down Expand Up @@ -373,9 +347,9 @@ Survey in 2016, 2017, and 2018."#;

text.fragments
.into_iter()
.map(|TextSnippetFragment { kind, text }| match kind {
TextSnippetFragmentKind::Normal => text,
TextSnippetFragmentKind::Highlighted => {
.map(|HighlightedFragment { kind, text }| match kind {
HighlightedKind::Normal => text,
HighlightedKind::Highlighted => {
format!("{HIGHLIGHTEN_PREFIX}{}{HIGHLIGHTEN_POSTFIX}", text)
}
})
Expand Down
3 changes: 3 additions & 0 deletions frontend/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ node_modules
pnpm-lock.yaml
package-lock.json
yarn.lock

# Ignore auto-generated api stubs
src/lib/api/index.ts
6 changes: 6 additions & 0 deletions frontend/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ module.exports = {
'plugin:svelte/recommended',
'prettier',
],
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
],
},
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"build": "vite build",
"preview": "vite preview",
"test": "npm run test:integration && npm run test:unit",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check": "svelte-kit sync && svelte-check --fail-on-warnings --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin prettier-plugin-svelte --plugin prettier-plugin-tailwindcss --check . && eslint .",
"format": "prettier --plugin prettier-plugin-svelte --plugin prettier-plugin-tailwindcss --write .",
Expand Down
Loading

0 comments on commit 5ce97ab

Please sign in to comment.