Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## Unreleased

### Features

- Handle getters, setters, non-identifier property names, and object literals in scope names. ([#13](https://github.com/getsentry/js-source-scopes/pull/13))

### Fixes

- `extract_scope_names` now returns an error if parsing fails, instead of an empty vector of scopes. ([#13](https://github.com/getsentry/js-source-scopes/pull/13))

### Internal

- Switch JS parser from rslint to swc, which is actively maintained. ([#13](https://github.com/getsentry/js-source-scopes/pull/13))

## 0.1.0

Inception
Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ homepage = "https://github.com/getsentry/js-source-scopes"
repository = "https://github.com/getsentry/js-source-scopes"

[dependencies]
#swc_ecma_parser = "0.103.0"
swc_common = "0.29.5"
swc_ecma_parser = "0.122.6"
swc_ecma_visit = { version = "0.80.5", features = ["path"] }
indexmap = "1.7.0"
rslint_parser = "0.3.1"
sourcemap = "6.0.2"
thiserror = "1.0.32"
tracing = "0.1.36"
60 changes: 36 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]

use std::fmt::Display;
use std::ops::Range;

mod name_resolver;
mod rslint;
mod scope_index;
mod scope_name;
mod source;
mod swc;

pub use name_resolver::NameResolver;
pub use scope_index::{ScopeIndex, ScopeIndexError, ScopeLookupResult};
pub use scope_name::{NameComponent, ScopeName};
pub use source::{SourceContext, SourceContextError, SourcePosition};
use swc_common::Spanned;

/// The Scopes extracted from a piece of JS Code.
pub type Scopes = Vec<(Range<u32>, Option<ScopeName>)>;

/// Extracts function scopes from the given JS-like `src`.
///
Expand All @@ -34,6 +39,7 @@ pub use source::{SourceContext, SourceContextError, SourcePosition};
/// let src = "const arrowFnExpr = (a) => a; function namedFnDecl() {}";
/// // arrowFnExpr -^------^ ^------namedFnDecl------^
/// let mut scopes: Vec<_> = js_source_scopes::extract_scope_names(src)
/// .unwrap()
/// .into_iter()
/// .map(|res| {
/// let components = res.1.map(|n| n.components().map(|c| {
Expand All @@ -50,31 +56,37 @@ pub use source::{SourceContext, SourceContextError, SourcePosition};
/// assert_eq!(scopes, expected);
/// ```
#[tracing::instrument(level = "trace", skip_all)]
pub fn extract_scope_names(src: &str) -> Vec<(Range<u32>, Option<ScopeName>)> {
rslint::parse_with_rslint(src)
}
pub fn extract_scope_names(src: &str) -> Result<Scopes, ParseError> {
let mut scopes = swc::parse_with_swc(src).map_err(|e| ParseError { inner: e })?;

// TODO: maybe see if swc makes scope extraction easier / faster ?
/*mod swc {
use swc_ecma_parser::lexer::Lexer;
use swc_ecma_parser::{Parser, StringInput, TsConfig};

pub fn parse_with_swc(src: &str) {
swc_ecma_parser::parse_file_as_module();
// filter out empty names
for scope in &mut scopes {
if let Some(ref name) = scope.1 {
if name.components.is_empty() {
scope.1 = None;
}
}
}

let source = SourceFile;
Ok(scopes)
}

let mut parser = Parser::new(
swc_ecma_parser::Syntax::Typescript(TsConfig {
tsx: true,
decorators: true,
dts: true,
no_early_errors: true,
}),
StringInput::from(src),
None,
);
/// An error parsing the JS Source provided to [`extract_scope_names`].
#[derive(Debug)]
pub struct ParseError {
inner: swc::ParseError,
}

let module = parser.parse_module().unwrap();
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let span = self.inner.span();
f.write_fmt(format_args!(
"{}:{}:{}",
span.lo.0,
span.hi.0,
self.inner.kind().msg()
))
}
}*/
}

impl std::error::Error for ParseError {}
31 changes: 11 additions & 20 deletions src/scope_name.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::borrow::Cow;
use std::collections::VecDeque;
use std::fmt::Display;
use std::ops::Range;

use rslint_parser::SyntaxToken;
use swc_ecma_visit::swc_ecma_ast as ast;

use crate::rslint::convert_text_range;
use crate::swc::convert_span;

/// An abstract scope name which can consist of multiple [`NameComponent`]s.
#[derive(Debug)]
Expand Down Expand Up @@ -45,8 +46,7 @@ impl NameComponent {
pub fn text(&self) -> &str {
match &self.inner {
NameComponentInner::Interpolation(s) => s,
NameComponentInner::SourceIdentifierToken(t) => t.text().as_str(),
NameComponentInner::SourcePunctuationToken(_) => "",
NameComponentInner::SourceIdentifierToken(t) => &t.sym,
}
}

Expand All @@ -56,34 +56,25 @@ impl NameComponent {
/// to a specific token inside the source text.
pub fn range(&self) -> Option<Range<u32>> {
match &self.inner {
NameComponentInner::SourceIdentifierToken(t)
| NameComponentInner::SourcePunctuationToken(t) => {
Some(convert_text_range(t.text_range()))
}
NameComponentInner::SourceIdentifierToken(t) => Some(convert_span(t.span)),
_ => None,
}
}

pub(crate) fn interp(s: &'static str) -> Self {
pub(crate) fn interp(s: impl Into<Cow<'static, str>>) -> Self {
Self {
inner: NameComponentInner::Interpolation(s),
inner: NameComponentInner::Interpolation(s.into()),
}
}
pub(crate) fn ident(token: SyntaxToken) -> Self {
pub(crate) fn ident(ident: ast::Ident) -> Self {
Self {
inner: NameComponentInner::SourceIdentifierToken(token),
}
}
pub(crate) fn punct(token: SyntaxToken) -> Self {
Self {
inner: NameComponentInner::SourcePunctuationToken(token),
inner: NameComponentInner::SourceIdentifierToken(ident),
}
}
}

#[derive(Debug)]
pub(crate) enum NameComponentInner {
Interpolation(&'static str),
SourceIdentifierToken(SyntaxToken),
SourcePunctuationToken(SyntaxToken),
Interpolation(Cow<'static, str>),
SourceIdentifierToken(ast::Ident),
}
Loading