Skip to content

feat: scope-level symbol search #1116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 90 additions & 122 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ deno_semver = "0.7.1"
flate2 = "1"
thiserror = "2"
async-tar = "0.4.2"
deno_graph = "0.89.4"
deno_ast = { version = "0.46.5", features = ["view"] }
deno_doc = { version = "0.171.1", features = ["comrak"] }
deno_graph = "0.91.0"
deno_ast = { version = "0.47.0", features = ["view"] }
deno_doc = { version = "0.174.0", features = ["comrak"] }
deno_error = "0.5.5"
comrak = { version = "0.29.0", default-features = false }
ammonia = "4.0.0"
Expand Down
39 changes: 26 additions & 13 deletions api/src/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use deno_ast::ModuleSpecifier;
use deno_ast::ParsedSource;
use deno_ast::SourceRange;
use deno_ast::SourceRangedForSpanned;
use deno_doc::html::search::SearchIndexNode;
use deno_doc::DocNodeDef;
use deno_error::JsErrorBox;
use deno_graph::source::load_data_url;
Expand Down Expand Up @@ -68,7 +69,7 @@ pub struct PackageAnalysisOutput {
pub data: PackageAnalysisData,
pub module_graph_2: HashMap<String, ModuleInfo>,
pub doc_nodes_json: Bytes,
pub doc_search_json: serde_json::Value,
pub doc_search: Vec<SearchIndexNode>,
pub dependencies: HashSet<(DependencyKind, PackageReqReference)>,
pub npm_tarball: NpmTarball,
pub readme_path: Option<PackagePath>,
Expand Down Expand Up @@ -174,7 +175,6 @@ async fn analyze_package_inner(
jsr_url_provider: &PassthroughJsrUrlProvider,
es_parser: Some(&module_analyzer.analyzer),
resolver: Default::default(),
npm_resolver: Default::default(),
workspace_fast_check: WorkspaceFastCheckOption::Enabled(&workspace_members),
});

Expand Down Expand Up @@ -252,8 +252,8 @@ async fn analyze_package_inner(
doc_nodes,
main_entrypoint,
info.rewrite_map,
scope,
name,
scope.clone(),
name.clone(),
version,
true,
None,
Expand All @@ -266,20 +266,34 @@ async fn analyze_package_inner(
bun: None,
},
registry_url.to_string(),
Some(format!("{scope}/{name}/")),
);
let search_index = deno_doc::html::generate_search_index(&ctx);
let doc_search_json = if let serde_json::Value::Object(mut obj) = search_index
{
obj.remove("nodes").unwrap()
} else {
unreachable!()
};

let doc_nodes = ctx
.doc_nodes
.values()
.flatten()
.map(std::borrow::Cow::Borrowed);
let partitions =
deno_doc::html::partition::partition_nodes_by_name(&ctx, doc_nodes, true);
let render_ctx = deno_doc::html::RenderContext::new(&ctx, &[], deno_doc::html::UrlResolveKind::AllSymbols);

let mut doc_search = partitions
.into_iter()
.flat_map(|(name, nodes)| {
deno_doc::html::search::doc_nodes_into_search_index_node(
&render_ctx, nodes, name, None,
)
})
.collect::<Vec<_>>();

doc_search.sort_by(|a, b| a.file.cmp(&b.file));

Ok(PackageAnalysisOutput {
data: PackageAnalysisData { exports, files },
module_graph_2,
doc_nodes_json,
doc_search_json,
doc_search,
dependencies,
npm_tarball,
readme_path,
Expand Down Expand Up @@ -602,7 +616,6 @@ async fn rebuild_npm_tarball_inner(
jsr_url_provider: &PassthroughJsrUrlProvider,
es_parser: Some(&module_analyzer.analyzer),
resolver: None,
npm_resolver: None,
workspace_fast_check: WorkspaceFastCheckOption::Enabled(&workspace_members),
});

Expand Down
33 changes: 33 additions & 0 deletions api/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ pub fn api_router() -> Router<Body, ApiError> {
util::json(publishing_task::get_handler),
)
.scope("/tickets", tickets_router())
.get("/ddoc/style.css", util::cache(CacheDuration::ONE_MINUTE, ddoc_style_handler))
.get("/ddoc/comrak.css", util::cache(CacheDuration::ONE_MINUTE, ddoc_comrak_handler))
.get("/ddoc/script.js", util::cache(CacheDuration::ONE_MINUTE, ddoc_script_handler))
.get("/.well-known/openapi", openapi_handler)
.build()
.unwrap()
Expand All @@ -74,3 +77,33 @@ async fn openapi_handler(
.unwrap();
Ok(resp)
}

async fn ddoc_style_handler(
_: hyper::Request<Body>,
) -> util::ApiResult<Response<Body>> {
let resp = Response::builder()
.header("Content-Type", "text/css")
.body(Body::from(deno_doc::html::STYLESHEET))
.unwrap();
Ok(resp)
}

async fn ddoc_comrak_handler(
_: hyper::Request<Body>,
) -> util::ApiResult<Response<Body>> {
let resp = Response::builder()
.header("Content-Type", "text/css")
.body(Body::from(deno_doc::html::comrak::COMRAK_STYLESHEET))
.unwrap();
Ok(resp)
}

async fn ddoc_script_handler(
_: hyper::Request<Body>,
) -> util::ApiResult<Response<Body>> {
let resp = Response::builder()
.header("Content-Type", "application/javascript")
.body(Body::from(deno_doc::html::SCRIPT_JS))
.unwrap();
Ok(resp)
}
14 changes: 6 additions & 8 deletions api/src/api/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ use routerify_query::RequestQueryExt;
use serde::Deserialize;
use serde::Serialize;
use sha2::Digest;
use std::borrow::Cow;
use std::io;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::Ordering;
Expand Down Expand Up @@ -1248,6 +1247,8 @@ pub async fn get_docs_handler(
readme,
package.runtime_compat,
registry_url,
None,
None,
)
.map_err(|e| {
error!("failed to generate docs: {}", e);
Expand All @@ -1257,9 +1258,6 @@ pub async fn get_docs_handler(

match docs {
GeneratedDocsOutput::Docs(docs) => Ok(ApiPackageVersionDocs::Content {
css: Cow::Borrowed(deno_doc::html::STYLESHEET),
comrak_css: Cow::Borrowed(deno_doc::html::comrak::COMRAK_STYLESHEET),
script: Cow::Borrowed(deno_doc::html::SCRIPT_JS),
breadcrumbs: docs.breadcrumbs,
toc: docs.toc,
main: docs.main,
Expand Down Expand Up @@ -1335,9 +1333,10 @@ pub async fn get_docs_search_handler(
false,
package.runtime_compat,
registry_url,
Some(format!("{scope}/{package_name}/")),
);

let search_index = deno_doc::html::generate_search_index(&ctx);
let search_index = deno_doc::html::search::generate_search_index(&ctx);

Ok(search_index)
}
Expand Down Expand Up @@ -1407,6 +1406,8 @@ pub async fn get_docs_search_html_handler(
None,
package.runtime_compat,
registry_url,
Some(format!("{}/{}/", scope, package_name)),
None,
)
.map_err(|e| {
error!("failed to generate docs: {}", e);
Expand Down Expand Up @@ -1575,9 +1576,6 @@ pub async fn get_source_handler(

Ok(ApiPackageVersionSource {
version: ApiPackageVersion::from(version),
css: Cow::Borrowed(deno_doc::html::STYLESHEET),
comrak_css: Cow::Borrowed(deno_doc::html::comrak::COMRAK_STYLESHEET),
script: Cow::Borrowed(deno_doc::html::SCRIPT_JS),
source,
})
}
Expand Down
100 changes: 97 additions & 3 deletions api/src/api/scope.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
// Copyright 2024 the JSR authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::sync::OnceLock;

use crate::api::package::package_router;
use crate::emails::EmailArgs;
use crate::emails::EmailSender;
use crate::iam::ReqIamExt;
use crate::RegistryUrl;
use anyhow::Context;
use hyper::Body;
use hyper::Request;
use hyper::Response;
use hyper::StatusCode;
use routerify::ext::RequestExt;
use routerify::Router;
use std::borrow::Cow;
use std::sync::OnceLock;
use tracing::error;
use tracing::field;
use tracing::instrument;
use tracing::Span;
Expand All @@ -23,10 +24,15 @@ use super::types::*;

use crate::auth::lookup_user_by_github_login;
use crate::auth::GithubOauth2Client;
use crate::buckets::Buckets;
use crate::db::*;
use crate::docs::DocNodesByUrl;
use crate::docs::DocsRequest;
use crate::docs::GeneratedDocsOutput;
use crate::util;
use crate::util::decode_json;
use crate::util::ApiResult;
use crate::util::CacheDuration;
use crate::util::RequestIdExt;

pub fn scope_router() -> Router<Body, ApiError> {
Expand Down Expand Up @@ -54,6 +60,13 @@ pub fn scope_router() -> Router<Body, ApiError> {
"/:scope/invites/:user_id",
util::auth(delete_invite_handler),
)
.get(
"/:scope/search_html",
util::cache(
CacheDuration::ONE_MINUTE,
util::json(get_docs_search_html_handler),
),
)
.build()
.unwrap()
}
Expand Down Expand Up @@ -490,6 +503,87 @@ pub async fn delete_invite_handler(
Ok(resp)
}

#[instrument(
name = "GET /api/scopes/:scope/search_html",
skip(req),
err,
fields(scope, user_id)
)]
pub async fn get_docs_search_html_handler(
req: Request<Body>,
) -> ApiResult<String> {
let scope = req.param_scope()?;
Span::current().record("scope", field::display(&scope));

let db = req.data::<Database>().unwrap();
let buckets = req.data::<Buckets>().unwrap();

let (_, packages) = db.list_packages_by_scope(&scope, false, 0, 100).await?;

let registry_url = req.data::<RegistryUrl>().unwrap().0.to_string();

let mut outsearch = String::new();
for (package, _, _) in packages {
let (package, repo, _) = db
.get_package(&scope, &package.name)
.await?
.ok_or(ApiError::PackageNotFound)?;

let Some(version) = db
.get_latest_unyanked_version_for_package(&scope, &package.name)
.await?
else {
continue;
};

let docs_path =
crate::gcs_paths::docs_v1_path(&scope, &package.name, &version.version);
let docs = buckets.docs_bucket.download(docs_path.into()).await?;
let docs = docs.ok_or_else(|| {
error!(
"docs not found for {}/{}/{}",
scope, package.name, version.version
);
ApiError::InternalServerError
})?;

let doc_nodes: DocNodesByUrl =
serde_json::from_slice(&docs).context("failed to parse doc nodes")?;
let docs_info = crate::docs::get_docs_info(&version.exports, None);

let docs = crate::docs::generate_docs_html(
doc_nodes,
docs_info.main_entrypoint,
docs_info.rewrite_map,
DocsRequest::AllSymbols,
scope.clone(),
package.name.clone(),
version.version.clone(),
true,
repo,
None,
package.runtime_compat,
registry_url.clone(),
Some(format!("{}/{}/", scope, package.name)),
Some(format!("@{}/{}/", scope, package.name)),
)
.map_err(|e| {
error!("failed to generate docs: {}", e);
ApiError::InternalServerError
})?
.unwrap();

let search = match docs {
GeneratedDocsOutput::Docs(docs) => docs.main,
GeneratedDocsOutput::Redirect(_) => unreachable!(),
};

outsearch.push_str(&search);
}

Ok(outsearch)
}

#[cfg(test)]
pub mod tests {
use super::*;
Expand Down
8 changes: 0 additions & 8 deletions api/src/api/types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// Copyright 2024 the JSR authors. All rights reserved. MIT license.
use std::borrow::Cow;

use crate::db::*;
use crate::ids::PackageName;
use crate::ids::PackagePath;
Expand Down Expand Up @@ -597,9 +595,6 @@ pub enum ApiPackageVersionDocs {
#[serde(rename_all = "camelCase")]
Content {
version: ApiPackageVersion,
css: Cow<'static, str>,
comrak_css: Cow<'static, str>,
script: Cow<'static, str>,
breadcrumbs: Option<String>,
toc: Option<String>,
main: String,
Expand Down Expand Up @@ -653,9 +648,6 @@ pub enum ApiSource {
#[serde(rename_all = "camelCase")]
pub struct ApiPackageVersionSource {
pub version: ApiPackageVersion,
pub css: Cow<'static, str>,
pub comrak_css: Cow<'static, str>,
pub script: Cow<'static, str>,
pub source: ApiSource,
}

Expand Down
Loading
Loading