Skip to content
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

feat: use Program instead of Module more #281

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
6 changes: 5 additions & 1 deletion src/cjs_parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::swc::visit::noop_visit_type;
use crate::swc::visit::Visit;
use crate::swc::visit::VisitWith;
use crate::ParsedSource;
use crate::ProgramRef;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CjsAnalysis {
Expand All @@ -30,7 +31,10 @@ impl ParsedSource {
}

let mut visitor = CjsVisitor::default();
visitor.visit_script(self.script());
match self.program_ref() {
ProgramRef::Module(n) => visitor.visit_module(n),
ProgramRef::Script(n) => visitor.visit_script(n),
};
visitor.take_result()
}
}
Expand Down
24 changes: 12 additions & 12 deletions src/dep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
use std::collections::HashMap;

use ast::BinExpr;
use ast::Module;
use serde::Deserialize;
use serde::Serialize;

Expand All @@ -16,31 +15,32 @@ use crate::swc::visit::Visit;
use crate::swc::visit::VisitWith;
use crate::MultiThreadedComments;
use crate::ParsedSource;
use crate::ProgramRef;
use crate::SourcePos;
use crate::SourceRange;
use crate::SourceRangedForSpanned;

impl ParsedSource {
/// Analyzes the module for a list of its imports and exports.
/// Analyzes the module for a list of its static and dynamic imports.
///
/// Note: This will also include `require` calls as dynamic imports.
pub fn analyze_dependencies(&self) -> Vec<DependencyDescriptor> {
match self.program_ref() {
ast::Program::Module(module) => {
analyze_module_dependencies(module, self.comments())
}
ast::Program::Script(_) => vec![],
}
analyze_program_dependencies(self.program_ref(), self.comments())
}
}

pub fn analyze_module_dependencies(
module: &Module,
pub fn analyze_program_dependencies(
program: ProgramRef,
comments: &MultiThreadedComments,
) -> Vec<DependencyDescriptor> {
let mut v = DependencyCollector {
comments,
items: vec![],
};
module.visit_with(&mut v);
match program {
ProgramRef::Module(n) => n.visit_with(&mut v),
ProgramRef::Script(n) => n.visit_with(&mut v),
}
v.items
}

Expand Down Expand Up @@ -567,7 +567,7 @@ mod tests {
maybe_syntax: None,
})
.unwrap();
(source.module().start(), source.analyze_dependencies())
(source.program_ref().start(), source.analyze_dependencies())
}

#[test]
Expand Down
15 changes: 10 additions & 5 deletions src/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ use std::string::FromUtf8Error;
use base64::Engine;
use thiserror::Error;

use crate::swc::ast::Program;
use crate::swc::codegen::text_writer::JsWriter;
use crate::swc::codegen::Node;
use crate::swc::common::FileName;
use crate::ModuleSpecifier;
use crate::ProgramRef;
use crate::SourceMap;

#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -92,7 +92,7 @@ pub enum EmitError {
/// Emits the program as a string of JavaScript code, possibly with the passed
/// comments, and optionally also a source map.
pub fn emit(
program: &Program,
program: ProgramRef,
comments: &dyn crate::swc::common::comments::Comments,
source_map: &SourceMap,
emit_options: &EmitOptions,
Expand All @@ -119,9 +119,14 @@ pub fn emit(
cm: source_map.clone(),
wr: writer,
};
program
.emit_with(&mut emitter)
.map_err(EmitError::SwcEmit)?;
match program {
ProgramRef::Module(n) => {
n.emit_with(&mut emitter).map_err(EmitError::SwcEmit)?;
}
ProgramRef::Script(n) => {
n.emit_with(&mut emitter).map_err(EmitError::SwcEmit)?;
}
}
}

let mut map: Option<Vec<u8>> = None;
Expand Down
159 changes: 134 additions & 25 deletions src/parsed_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ use dprint_swc_ext::common::SourceTextInfo;
use dprint_swc_ext::common::SourceTextProvider;
use dprint_swc_ext::common::StartSourcePos;
use swc_common::Mark;
use swc_ecma_ast::ModuleDecl;
use swc_ecma_ast::ModuleItem;
use swc_ecma_ast::Stmt;

use crate::comments::MultiThreadedComments;
use crate::scope_analysis_transform;
Expand Down Expand Up @@ -59,6 +62,129 @@ impl Globals {
}
}

/// A reference to a Program.
///
/// It is generally preferable for functions to accept this over `&Program`
/// because it doesn't require cloning when only owning a `Module` or `Script`.
#[derive(Debug, Clone, Copy)]
pub enum ProgramRef<'a> {
Module(&'a Module),
Script(&'a Script),
}

impl<'a> ProgramRef<'a> {
pub fn unwrap_module(&self) -> &Module {
match self {
ProgramRef::Module(m) => m,
ProgramRef::Script(_) => {
panic!("Cannot get a module when the source was a script.")
}
}
}

pub fn unwrap_script(&self) -> &Script {
match self {
ProgramRef::Module(_) => {
panic!("Cannot get a script when the source was a module.")
}
ProgramRef::Script(s) => s,
}
}

pub fn shebang(&self) -> Option<&swc_atoms::Atom> {
match self {
ProgramRef::Module(m) => m.shebang.as_ref(),
ProgramRef::Script(s) => s.shebang.as_ref(),
}
}

pub fn body(&self) -> impl Iterator<Item = ModuleItemRef<'a>> {
match self {
ProgramRef::Module(m) => Box::new(m.body.iter().map(|n| n.into()))
as Box<dyn Iterator<Item = ModuleItemRef<'a>>>,
ProgramRef::Script(s) => Box::new(s.body.iter().map(ModuleItemRef::Stmt)),
}
}

pub fn to_owned(&self) -> Program {
match self {
ProgramRef::Module(m) => Program::Module((*m).clone()),
ProgramRef::Script(s) => Program::Script((*s).clone()),
}
}
}

impl<'a> From<&'a Program> for ProgramRef<'a> {
fn from(program: &'a Program) -> Self {
match program {
Program::Module(module) => ProgramRef::Module(module),
Program::Script(script) => ProgramRef::Script(script),
}
}
}

#[cfg(feature = "visit")]
impl<'a, T: swc_ecma_visit::Visit> swc_ecma_visit::VisitWith<T>
for ProgramRef<'a>
{
fn visit_with(&self, visitor: &mut T) {
match self {
ProgramRef::Module(n) => n.visit_with(visitor),
ProgramRef::Script(n) => n.visit_with(visitor),
}
}

fn visit_children_with(&self, visitor: &mut T) {
match self {
ProgramRef::Module(n) => n.visit_children_with(visitor),
ProgramRef::Script(n) => n.visit_children_with(visitor),
}
}
}

impl swc_common::Spanned for ProgramRef<'_> {
// ok because we're implementing Spanned
#[allow(clippy::disallowed_methods)]
#[allow(clippy::disallowed_types)]
fn span(&self) -> swc_common::Span {
match self {
Self::Module(m) => m.span,
Self::Script(s) => s.span,
}
}
}

/// Reference to a ModuleDecl or Stmt in a Program.
///
/// This is used to allow using the same API for the top level
/// statements when working with a ProgramRef.
#[derive(Debug, Clone, Copy)]
pub enum ModuleItemRef<'a> {
ModuleDecl(&'a ModuleDecl),
Stmt(&'a Stmt),
}

impl swc_common::Spanned for ModuleItemRef<'_> {
// ok because we're implementing Spanned
#[allow(clippy::disallowed_methods)]
#[allow(clippy::disallowed_types)]
fn span(&self) -> swc_common::Span {
match self {
Self::ModuleDecl(n) => n.span(),
Self::Stmt(n) => n.span(),
}
}
}

impl<'a> From<&'a ModuleItem> for ModuleItemRef<'a> {
fn from(item: &'a ModuleItem) -> Self {
match item {
ModuleItem::ModuleDecl(n) => ModuleItemRef::ModuleDecl(n),
ModuleItem::Stmt(n) => ModuleItemRef::Stmt(n),
}
}
}

#[derive(Clone)]
pub(crate) struct SyntaxContexts {
pub unresolved: SyntaxContext,
Expand Down Expand Up @@ -126,27 +252,10 @@ impl ParsedSource {
}

/// Gets the parsed program as a reference.
pub fn program_ref(&self) -> &Program {
&self.0.program
}

/// Gets the parsed module.
///
/// This will panic if the source is not a module.
pub fn module(&self) -> &Module {
match self.program_ref() {
Program::Module(module) => module,
Program::Script(_) => panic!("Cannot get a module when the source was a script. Use `.program()` instead."),
}
}

/// Gets the parsed script.
///
/// This will panic if the source is not a script.
pub fn script(&self) -> &Script {
match self.program_ref() {
Program::Script(script) => script,
Program::Module(_) => panic!("Cannot get a script when the source was a module. Use `.program()` instead."),
pub fn program_ref(&self) -> ProgramRef<'_> {
match self.0.program.as_ref() {
Program::Module(module) => ProgramRef::Module(module),
Program::Script(script) => ProgramRef::Script(script),
}
}

Expand Down Expand Up @@ -245,12 +354,12 @@ impl ParsedSource {

/// Gets if this source is a module.
pub fn is_module(&self) -> bool {
matches!(self.program_ref(), Program::Module(_))
matches!(self.program_ref(), ProgramRef::Module(_))
}

/// Gets if this source is a script.
pub fn is_script(&self) -> bool {
matches!(self.program_ref(), Program::Script(_))
matches!(self.program_ref(), ProgramRef::Script(_))
}
}

Expand Down Expand Up @@ -298,8 +407,8 @@ impl ParsedSource {
) -> T {
let program_info = crate::view::ProgramInfo {
program: match self.program_ref() {
Program::Module(module) => crate::view::ProgramRef::Module(module),
Program::Script(script) => crate::view::ProgramRef::Script(script),
ProgramRef::Module(module) => crate::view::ProgramRef::Module(module),
ProgramRef::Script(script) => crate::view::ProgramRef::Script(script),
},
text_info: Some(self.text_info_lazy()),
tokens: self.0.tokens.as_ref().map(|t| t as &[TokenAndSpan]),
Expand Down
4 changes: 2 additions & 2 deletions src/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ mod test {
assert_eq!(program.text().as_ref(), "// 1\n1 + 1\n// 2");
assert_eq!(program.media_type(), MediaType::JavaScript);
assert!(matches!(
program.script().body[0],
program.program_ref().unwrap_script().body[0],
crate::swc::ast::Stmt::Expr(..)
));
assert_eq!(program.get_leading_comments().unwrap().len(), 1);
Expand All @@ -408,7 +408,7 @@ mod test {
})
.unwrap();
assert!(matches!(
program.module().body[0],
program.program_ref().unwrap_module().body[0],
crate::swc::ast::ModuleItem::Stmt(..)
));
}
Expand Down
7 changes: 4 additions & 3 deletions src/transpiling/jsx_precompile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,7 @@ mod tests {
use crate::swc::visit::FoldWith;
use crate::EmitOptions;
use crate::ModuleSpecifier;
use crate::ProgramRef;
use crate::SourceMap;
use pretty_assertions::assert_eq;
use swc_common::comments::SingleThreadedComments;
Expand Down Expand Up @@ -3018,7 +3019,7 @@ const a = _jsxTemplate($$_tpl_1, _jsxAttr("class", "foo"), _jsxAttr("className",
) {
let (source_map, module) = parse(src);
let mut transform_folder = as_folder(transform);
let output = print(&source_map, module.fold_with(&mut transform_folder));
let output = print(&source_map, &module.fold_with(&mut transform_folder));
assert_eq!(output, format!("{}\n", expected_output));
}

Expand All @@ -3037,9 +3038,9 @@ const a = _jsxTemplate($$_tpl_1, _jsxAttr("class", "foo"), _jsxAttr("className",
(source_map, parser.parse_module().unwrap())
}

fn print(source_map: &SourceMap, module: Module) -> String {
fn print(source_map: &SourceMap, module: &Module) -> String {
crate::emit::emit(
&Program::Module(module),
ProgramRef::Module(module),
&SingleThreadedComments::default(),
source_map,
&EmitOptions {
Expand Down
7 changes: 6 additions & 1 deletion src/transpiling/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,12 @@ fn transpile(
)
})?;

Ok(emit(&program, &comments, &source_map, emit_options)?)
Ok(emit(
(&program).into(),
&comments,
&source_map,
emit_options,
)?)
}

#[derive(Default, Clone)]
Expand Down