Skip to content
Open
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
29 changes: 11 additions & 18 deletions bindings/binding_core_node/src/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ use napi::{
};
use path_clean::clean;
use swc_core::{
base::{config::Options, Compiler, TransformOutput},
common::{comments::SingleThreadedComments, errors::Handler, FileName},
ecma::ast::noop_pass,
base::{config::Options, CompileInput, Compiler, TransformOutput},
common::{errors::Handler, FileName, Spanned},
node::{get_deserialized, MapErr},
};

Expand Down Expand Up @@ -55,25 +54,19 @@ fn process_program_input(
program_input: ProgramInput,
options: &Options,
) -> Result<TransformOutput, Error> {
match program_input {
let (fm, program) = match program_input {
ProgramInput::WithContext {
program,
source_context,
} => {
let (fm, program) = prepare_program_with_context(c, program, source_context)?;

c.process_js_with_custom_pass(
fm,
Some(program),
handler,
options,
SingleThreadedComments::default(),
|_| noop_pass(),
|_| noop_pass(),
)
} => prepare_program_with_context(c, program, source_context)?,
ProgramInput::Raw(program) => {
let fm = c.cm.lookup_char_pos(program.span().lo()).file;
(fm, program)
}
ProgramInput::Raw(program) => c.process_js(handler, program, options),
}
};

c.compile(handler, CompileInput::program(fm, program), options)
.codegen()
}

#[napi]
Expand Down
98 changes: 22 additions & 76 deletions bindings/binding_core_wasm/__tests__/simple.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,51 +88,34 @@ describe("transform", () => {
});
});

function expectParsedClass(output) {
expect(output.type).toBe("Module");
expect(output.body).toHaveLength(1);

const declaration = output.body[0];
expect(declaration).toMatchObject({
type: "ClassDeclaration",
body: [],
identifier: {
type: "Identifier",
value: "Foo",
},
});
expect(declaration.span.end - declaration.span.start).toBe(12);
expect(
declaration.identifier.span.end - declaration.identifier.span.start
).toBe(3);
expect(output.span).toEqual(declaration.span);
}

describe("parse", () => {
it("should work", () => {
const output = swc.parseSync("class Foo {}", {
syntax: "typescript",
target: "es2021",
});

expect(output).toMatchInlineSnapshot(`
{
"body": [
{
"body": [],
"ctxt": 0,
"declare": false,
"decorators": [],
"identifier": {
"ctxt": 2,
"optional": false,
"span": {
"end": 254,
"start": 251,
},
"type": "Identifier",
"value": "Foo",
},
"implements": [],
"isAbstract": false,
"span": {
"end": 257,
"start": 245,
},
"superClass": null,
"superTypeParams": null,
"type": "ClassDeclaration",
"typeParams": null,
},
],
"interpreter": null,
"span": {
"end": 257,
"start": 245,
},
"type": "Module",
}
`);
expectParsedClass(output);
});

it("should work with async facade", async () => {
Expand All @@ -141,44 +124,7 @@ describe("parse", () => {
target: "es2021",
});

expect(output).toMatchInlineSnapshot(`
{
"body": [
{
"body": [],
"ctxt": 0,
"declare": false,
"decorators": [],
"identifier": {
"ctxt": 2,
"optional": false,
"span": {
"end": 267,
"start": 264,
},
"type": "Identifier",
"value": "Foo",
},
"implements": [],
"isAbstract": false,
"span": {
"end": 270,
"start": 258,
},
"superClass": null,
"superTypeParams": null,
"type": "ClassDeclaration",
"typeParams": null,
},
],
"interpreter": null,
"span": {
"end": 270,
"start": 258,
},
"type": "Module",
}
`);
expectParsedClass(output);
});
});

Expand Down
63 changes: 51 additions & 12 deletions crates/binding_macros/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ use once_cell::sync::Lazy;
#[doc(hidden)]
pub use serde_wasm_bindgen;
use serde_wasm_bindgen::Serializer;
#[doc(hidden)]
pub use swc::PrintArgs;
use swc::{config::ErrorFormat, Compiler, HandlerOpts};
#[doc(hidden)]
pub use swc::{
config::{Options, ParseOptions, SourceMapsConfig},
try_with_handler,
};
#[doc(hidden)]
pub use swc::{CompileInput, PrintArgs};
#[doc(hidden)]
pub use swc_common::{
comments::{self, SingleThreadedComments},
errors::Handler,
FileName, Mark, GLOBALS,
FileName, Mark, Spanned, GLOBALS,
};
use swc_common::{sync::Lrc, FilePathMapping, SourceMap};
#[doc(hidden)]
Expand Down Expand Up @@ -296,13 +296,37 @@ macro_rules! build_print {

#[macro_export]
macro_rules! build_transform_sync {
// Source input uses the direct pipeline by default and the frozen legacy
// path for custom-pass overloads. Program input remains direct in both.
($(#[$m:meta])*) => {
build_transform_sync!($(#[$m])*, |_| $crate::wasm::noop_pass(), |_| $crate::wasm::noop_pass(), Default::default());
$crate::build_transform_sync!(@impl [$(#[$m])*] pipeline, |_| $crate::wasm::noop_pass(), |_| $crate::wasm::noop_pass(), Default::default());
};
($(#[$m:meta])*, $before_pass: expr, $after_pass: expr) => {
build_transform_sync!($(#[$m])*, $before_pass, $after_pass, Default::default());
$crate::build_transform_sync!(@impl [$(#[$m])*] custom, $before_pass, $after_pass, Default::default());
};
($(#[$m:meta])*, $before_pass: expr, $after_pass: expr, $opt: expr) => {
$crate::build_transform_sync!(@impl [$(#[$m])*] custom, $before_pass, $after_pass, $opt);
};
(@transform_source pipeline, $c:ident, $handler:ident, $fm:ident, $comments:ident, $opts:ident, $before_pass:expr, $after_pass:expr) => {
$c.compile(
$handler,
$crate::wasm::CompileInput::source($fm).with_comments($comments),
&$opts,
)
.codegen()
};
(@transform_source custom, $c:ident, $handler:ident, $fm:ident, $comments:ident, $opts:ident, $before_pass:expr, $after_pass:expr) => {
$c.process_js_with_custom_pass(
$fm,
None,
$handler,
&$opts,
$comments,
$before_pass,
$after_pass,
)
};
(@impl [$(#[$m:meta])*] $mode:ident, $before_pass:expr, $after_pass:expr, $opt:expr) => {
$(#[$m])*
#[allow(unused_variables)]
pub fn transform_sync(
Expand Down Expand Up @@ -385,18 +409,33 @@ macro_rules! build_transform_sync {
let file = fm.clone();
let comments = $crate::wasm::SingleThreadedComments::default();
$crate::wasm::anyhow::Context::context(
c.process_js_with_custom_pass(
fm,
None,
$crate::build_transform_sync!(
@transform_source $mode,
c,
handler,
&opts,
fm,
comments,
opts,
$before_pass,
$after_pass,
), "failed to process js file"
$after_pass
),
"failed to process js file"
)?
}
Err(v) => unsafe { c.process_js(handler, $crate::wasm::serde_wasm_bindgen::from_value(v).expect(""), &opts)? },
Err(v) => {
let program: $crate::wasm::Program =
$crate::wasm::serde_wasm_bindgen::from_value(v).expect("");
let fm = c
.cm
.lookup_char_pos($crate::wasm::Spanned::span(&program).lo())
.file;
c.compile(
handler,
$crate::wasm::CompileInput::program(fm, program),
&opts,
)
.codegen()?
}
};

out
Expand Down
97 changes: 97 additions & 0 deletions crates/swc/src/codegen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//! Code generation for programs produced by the direct compilation pipeline.

use std::{path::PathBuf, sync::Arc};

use anyhow::Error;
use rustc_hash::FxHashMap;
use swc_atoms::Atom;
use swc_common::{comments::SingleThreadedComments, BytePos, SourceMap};
use swc_compiler_base::{PrintArgs, SourceMapsConfig, TransformOutput};
use swc_config::file_pattern::FilePattern;
use swc_ecma_ast::{EsVersion, Program};

/// Metadata captured after resolver hooks and before runtime plugins and
/// built-in transforms run.
#[derive(Default)]
pub(super) struct CodegenMetadata {
pub(super) source_map_names: FxHashMap<BytePos, Atom>,
pub(super) original_source_map: Option<crate::sourcemap::SourceMap>,
}

/// Configuration required by the code-generation terminal.
pub(super) struct CodegenOptions {
pub(super) source_maps: SourceMapsConfig,
pub(super) output_path: Option<PathBuf>,
pub(super) source_root: Option<String>,
pub(super) source_file_name: Option<String>,
pub(super) source_map_ignore_list: Option<FilePattern>,
pub(super) inline_sources_content: bool,
pub(super) emit_source_map_columns: bool,
pub(super) preamble: String,
pub(super) source_map_url: Option<String>,
pub(super) ascii_only: bool,
pub(super) minify: bool,
pub(super) emit_assert_for_import_attributes: bool,
pub(super) emit_source_map_scopes: bool,
pub(super) inline_script: bool,
}

/// A fully transformed program together with metadata prepared for emit.
pub(super) struct CodegenInput {
pub(super) source_map: Arc<SourceMap>,
pub(super) program: Program,
pub(super) comments: SingleThreadedComments,
pub(super) metadata: CodegenMetadata,
pub(super) transform_output: FxHashMap<String, String>,
pub(super) target: EsVersion,
pub(super) options: CodegenOptions,
}

impl CodegenInput {
/// Generates code from the prepared emit state.
pub(super) fn codegen(self) -> Result<TransformOutput, Error> {
let Self {
source_map,
program,
comments,
metadata,
transform_output,
target,
options,
} = self;
let transform_output = if transform_output.is_empty() {
None
} else {
Some(transform_output)
};

swc_compiler_base::print(
source_map,
&program,
PrintArgs {
source_root: options.source_root.as_deref(),
source_file_name: options.source_file_name.as_deref(),
source_map_ignore_list: options.source_map_ignore_list,
output_path: options.output_path,
inline_sources_content: options.inline_sources_content,
source_map: options.source_maps,
source_map_names: &metadata.source_map_names,
orig: metadata.original_source_map,
comments: Some(&comments),
emit_source_map_columns: options.emit_source_map_columns,
emit_source_map_scopes: options.emit_source_map_scopes,
preamble: &options.preamble,
codegen_config: swc_ecma_codegen::Config::default()
.with_target(target)
.with_minify(options.minify)
.with_ascii_only(options.ascii_only)
.with_emit_assert_for_import_attributes(
options.emit_assert_for_import_attributes,
)
.with_inline_script(options.inline_script),
output: transform_output,
source_map_url: options.source_map_url.as_deref(),
},
)
}
}
Loading
Loading