Skip to content

Commit

Permalink
perf: make module analysis async (#477)
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed May 16, 2024
1 parent fe62ad3 commit 67cdf5d
Show file tree
Hide file tree
Showing 9 changed files with 472 additions and 394 deletions.
4 changes: 2 additions & 2 deletions js/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export function parseModule(
specifier: string,
content: Uint8Array,
options: ParseModuleOptions = {},
): ModuleJson {
): Promise<ModuleJson> {
const {
headers,
defaultJsxImportSource,
Expand All @@ -257,5 +257,5 @@ export function parseModule(
content,
resolve,
resolveTypes,
) as ModuleJson;
) as Promise<ModuleJson>;
}
28 changes: 14 additions & 14 deletions js/test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright 2018-2024 the Deno authors. MIT license.

import { assert, assertEquals, assertRejects, assertThrows } from "@std/assert";
import { assert, assertEquals, assertRejects } from "@std/assert";
import type { LoadResponseModule } from "./types.ts";
import {
createGraph,
Expand Down Expand Up @@ -535,7 +535,7 @@ Deno.test({
name: "parseModule()",
async fn() {
await init();
const module = parseModule(
const module = await parseModule(
"file:///a/test01.js",
new TextEncoder().encode(`
/// <reference types="./test01.d.ts" />
Expand Down Expand Up @@ -606,7 +606,7 @@ Deno.test({
name: "parseModule() - with headers",
async fn() {
await init();
const module = parseModule(
const module = await parseModule(
`https://example.com/a`,
new TextEncoder().encode(`declare interface A {
a: string;
Expand All @@ -625,7 +625,7 @@ Deno.test({
name: "parseModule() - with jsxImportSource pragma",
async fn() {
await init();
const module = parseModule(
const module = await parseModule(
`file:///a/test01.tsx`,
new TextEncoder().encode(`/* @jsxImportSource http://example.com/preact */
export function A() {
Expand All @@ -648,7 +648,7 @@ Deno.test({
name: "parseModule() - with defaultJsxImportSource",
async fn() {
await init();
const module = parseModule(
const module = await parseModule(
`file:///a/test01.tsx`,
new TextEncoder().encode(`
export function A() {
Expand All @@ -670,7 +670,7 @@ Deno.test({
name: "parseModule() - with defaultJsxImportSourceTypes",
async fn() {
await init();
const module = parseModule(
const module = await parseModule(
`file:///a/test01.tsx`,
new TextEncoder().encode(`
export function A() {
Expand Down Expand Up @@ -698,9 +698,9 @@ Deno.test({
name: "parseModule() - invalid URL",
async fn() {
await init();
assertThrows(
() => {
parseModule(
await assertRejects(
async () => {
await parseModule(
"./bad.ts",
new TextEncoder().encode(`console.log("hello");`),
);
Expand All @@ -715,9 +715,9 @@ Deno.test({
name: "parseModule() - syntax error",
async fn() {
await init();
assertThrows(
() => {
parseModule(
await assertRejects(
async () => {
await parseModule(
"file:///a/test.md",
new TextEncoder().encode(`# Some Markdown\n\n**bold**`),
);
Expand All @@ -732,7 +732,7 @@ Deno.test({
name: "parseModule() - import attributes",
async fn() {
await init();
const module = parseModule(
const module = await parseModule(
"file:///a/test01.js",
new TextEncoder().encode(`
import a from "./a.json" with { type: "json" };
Expand Down Expand Up @@ -789,7 +789,7 @@ Deno.test({
name: "parseModule() - triple slash directives in typescript",
async fn() {
await init();
const module = parseModule(
const module = await parseModule(
"file:///a/foo.ts",
new TextEncoder().encode(`
/// <reference path="./a.d.ts" />
Expand Down
8 changes: 5 additions & 3 deletions lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ pub async fn js_create_graph(

#[allow(clippy::too_many_arguments)]
#[wasm_bindgen(js_name = parseModule)]
pub fn js_parse_module(
pub async fn js_parse_module(
specifier: String,
maybe_headers: JsValue,
maybe_default_jsx_import_source: Option<String>,
Expand Down Expand Up @@ -327,14 +327,16 @@ pub fn js_parse_module(
match deno_graph::parse_module(deno_graph::ParseModuleOptions {
graph_kind: GraphKind::All,
specifier,
maybe_headers: maybe_headers.as_ref(),
maybe_headers,
content: content.into(),
file_system: &NullFileSystem,
jsr_url_provider: Default::default(),
maybe_resolver: maybe_resolver.as_ref().map(|r| r as &dyn Resolver),
module_analyzer: Default::default(),
maybe_npm_resolver: None,
}) {
})
.await
{
Ok(module) => {
let serializer =
serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true);
Expand Down
3 changes: 2 additions & 1 deletion src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,9 +308,10 @@ pub fn module_graph_1_to_2(module_info: &mut serde_json::Value) {
///
/// It can be assumed that the source has not changed since
/// it was loaded by deno_graph.
#[async_trait::async_trait(?Send)]
pub trait ModuleAnalyzer {
/// Analyzes the module.
fn analyze(
async fn analyze(
&self,
specifier: &ModuleSpecifier,
source: Arc<str>,
Expand Down
45 changes: 30 additions & 15 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,17 @@ impl<'a> ModuleParser for CapturingModuleParser<'a> {
#[derive(Default)]
pub struct DefaultModuleAnalyzer;

#[async_trait::async_trait(?Send)]
impl ModuleAnalyzer for DefaultModuleAnalyzer {
fn analyze(
async fn analyze(
&self,
specifier: &deno_ast::ModuleSpecifier,
source: Arc<str>,
media_type: MediaType,
) -> Result<ModuleInfo, ParseDiagnostic> {
ParserModuleAnalyzer::default().analyze(specifier, source, media_type)
ParserModuleAnalyzer::default()
.analyze(specifier, source, media_type)
.await
}
}

Expand Down Expand Up @@ -312,6 +315,22 @@ impl<'a> ParserModuleAnalyzer<'a> {
jsdoc_imports: analyze_jsdoc_imports(media_type, text_info, comments),
}
}

pub fn analyze_sync(
&self,
specifier: &deno_ast::ModuleSpecifier,
source: Arc<str>,
media_type: MediaType,
) -> Result<ModuleInfo, ParseDiagnostic> {
let parsed_source = self.parser.parse_module(ParseOptions {
specifier,
source,
media_type,
// scope analysis is not necessary for module parsing
scope_analysis: false,
})?;
Ok(ParserModuleAnalyzer::module_info(&parsed_source))
}
}

impl<'a> Default for ParserModuleAnalyzer<'a> {
Expand All @@ -322,21 +341,15 @@ impl<'a> Default for ParserModuleAnalyzer<'a> {
}
}

#[async_trait::async_trait(?Send)]
impl<'a> ModuleAnalyzer for ParserModuleAnalyzer<'a> {
fn analyze(
async fn analyze(
&self,
specifier: &deno_ast::ModuleSpecifier,
source: Arc<str>,
media_type: MediaType,
) -> Result<ModuleInfo, ParseDiagnostic> {
let parsed_source = self.parser.parse_module(ParseOptions {
specifier,
source,
media_type,
// scope analysis is not necessary for module parsing
scope_analysis: false,
})?;
Ok(ParserModuleAnalyzer::module_info(&parsed_source))
self.analyze_sync(specifier, source, media_type)
}
}

Expand Down Expand Up @@ -372,16 +385,17 @@ impl CapturingModuleAnalyzer {
}
}

#[async_trait::async_trait(?Send)]
impl ModuleAnalyzer for CapturingModuleAnalyzer {
fn analyze(
async fn analyze(
&self,
specifier: &deno_ast::ModuleSpecifier,
source: Arc<str>,
media_type: MediaType,
) -> Result<ModuleInfo, ParseDiagnostic> {
let capturing_parser = self.as_capturing_parser();
let module_analyzer = ParserModuleAnalyzer::new(&capturing_parser);
module_analyzer.analyze(specifier, source, media_type)
module_analyzer.analyze(specifier, source, media_type).await
}
}

Expand Down Expand Up @@ -1076,8 +1090,8 @@ const f = new Set();
);
}

#[test]
fn test_analyze_ts_references_and_jsx_import_source_with_shebang() {
#[tokio::test]
async fn test_analyze_ts_references_and_jsx_import_source_with_shebang() {
let specifier = ModuleSpecifier::parse("file:///a/test.tsx").unwrap();
let source = r#"#!/usr/bin/env -S deno run
/// <reference path="./ref.d.ts" />
Expand All @@ -1086,6 +1100,7 @@ export {};
"#;
let module_info = DefaultModuleAnalyzer
.analyze(&specifier, source.into(), MediaType::Tsx)
.await
.unwrap();
assert_eq!(
module_info,
Expand Down

0 comments on commit 67cdf5d

Please sign in to comment.