next-swc
: Add .bundle()
(#30935)
This commit is contained in:
parent
5684edb36f
commit
e8e4210f9f
5 changed files with 297 additions and 9 deletions
76
packages/next/build/swc/Cargo.lock
generated
76
packages/next/build/swc/Cargo.lock
generated
|
@ -159,6 +159,12 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "build_const"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
@ -214,6 +220,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
|
||||
dependencies = [
|
||||
"build_const",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.1"
|
||||
|
@ -364,6 +379,12 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -506,6 +527,7 @@ checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
|
|||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"hashbrown",
|
||||
"rayon",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -728,6 +750,7 @@ dependencies = [
|
|||
"napi",
|
||||
"napi-build",
|
||||
"napi-derive",
|
||||
"once_cell",
|
||||
"path-clean",
|
||||
"pathdiff",
|
||||
"regex",
|
||||
|
@ -737,8 +760,10 @@ dependencies = [
|
|||
"serde_json",
|
||||
"swc",
|
||||
"swc_atoms",
|
||||
"swc_bundler",
|
||||
"swc_common",
|
||||
"swc_css",
|
||||
"swc_ecma_loader",
|
||||
"swc_ecma_preset_env",
|
||||
"swc_ecma_transforms_testing",
|
||||
"swc_ecmascript",
|
||||
|
@ -913,6 +938,16 @@ version = "2.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
|
@ -1046,6 +1081,12 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radix_fmt"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.6.5"
|
||||
|
@ -1631,6 +1672,38 @@ dependencies = [
|
|||
"string_cache_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_bundler"
|
||||
version = "0.79.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52993195a4365d22ec362b26b6d5f64d9f51ffe1a2bb93a6b68e64648b1193c6"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
"crc",
|
||||
"dashmap",
|
||||
"indexmap",
|
||||
"is-macro",
|
||||
"once_cell",
|
||||
"parking_lot 0.11.2",
|
||||
"petgraph",
|
||||
"radix_fmt",
|
||||
"rayon",
|
||||
"relative-path",
|
||||
"retain_mut",
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
"swc_ecma_codegen",
|
||||
"swc_ecma_loader",
|
||||
"swc_ecma_parser",
|
||||
"swc_ecma_transforms_base",
|
||||
"swc_ecma_transforms_optimization",
|
||||
"swc_ecma_utils",
|
||||
"swc_ecma_visit",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_common"
|
||||
version = "0.14.3"
|
||||
|
@ -1938,6 +2011,7 @@ checksum = "6d47323456ee73ecffa584a3964cc82dbf3daee2c7c72221d1f2130d3e42312d"
|
|||
dependencies = [
|
||||
"once_cell",
|
||||
"phf",
|
||||
"rayon",
|
||||
"scoped-tls",
|
||||
"smallvec 1.7.0",
|
||||
"swc_atoms",
|
||||
|
@ -2032,6 +2106,7 @@ dependencies = [
|
|||
"dashmap",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"retain_mut",
|
||||
"serde_json",
|
||||
"swc_atoms",
|
||||
|
@ -2137,6 +2212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8762b5fccb1bbb5f4bcb83eb6668a78b3861ae8df692c76e75cc33605d611480"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
|
|
|
@ -11,26 +11,28 @@ anyhow = "1.0"
|
|||
backtrace = "0.3"
|
||||
chrono = "0.4"
|
||||
easy-error = "1.0.0"
|
||||
napi = { version = "1", features = ["serde-json"] }
|
||||
fxhash = "0.2.1"
|
||||
napi = {version = "1", features = ["serde-json"]}
|
||||
napi-derive = "1"
|
||||
once_cell = "1.8.0"
|
||||
path-clean = "0.1"
|
||||
pathdiff = "0.2.0"
|
||||
regex = "1.5"
|
||||
retain_mut = "0.1.3"
|
||||
rustc-hash = "1.1.0"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
swc = "0.81.1"
|
||||
swc_atoms = "0.2.7"
|
||||
swc_common = { version = "0.14.2", features = ["concurrent", "sourcemap"] }
|
||||
swc_bundler = {version = "0.79.0", features = ["concurrent"]}
|
||||
swc_common = {version = "0.14.2", features = ["concurrent", "sourcemap"]}
|
||||
swc_css = "0.20.0"
|
||||
swc_ecmascript = { version = "0.84.1", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] }
|
||||
swc_ecma_loader = {version = "0.23.0", features = ["node", "lru"]}
|
||||
swc_ecma_preset_env = "0.63.1"
|
||||
swc_ecmascript = {version = "0.84.1", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"]}
|
||||
swc_node_base = "0.5.1"
|
||||
swc_stylis = "0.17.0"
|
||||
fxhash = "0.2.1"
|
||||
retain_mut = "0.1.3"
|
||||
pathdiff = "0.2.0"
|
||||
rustc-hash = "1.1.0"
|
||||
tracing = { version = "0.1.28", features = ["release_max_level_off"] }
|
||||
|
||||
tracing = {version = "0.1.28", features = ["release_max_level_off"]}
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "1"
|
||||
|
|
|
@ -91,6 +91,10 @@ export function minifySync(src, opts) {
|
|||
return bindings.minifySync(toBuffer(src), toBuffer(opts ?? {}))
|
||||
}
|
||||
|
||||
export async function bundle(options) {
|
||||
return bindings.bundle(toBuffer(options))
|
||||
}
|
||||
|
||||
module.exports.transform = transform
|
||||
module.exports.transformSync = transformSync
|
||||
module.exports.minify = minify
|
||||
|
|
203
packages/next/build/swc/src/bundle/mod.rs
Normal file
203
packages/next/build/swc/src/bundle/mod.rs
Normal file
|
@ -0,0 +1,203 @@
|
|||
use crate::{
|
||||
complete_output, get_compiler,
|
||||
util::{CtxtExt, MapErr},
|
||||
};
|
||||
use anyhow::{anyhow, bail, Context, Error};
|
||||
use napi::{CallContext, JsObject, Task};
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::Deserialize;
|
||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||
use swc::{
|
||||
config::{util::BoolOrObject, SourceMapsConfig},
|
||||
try_with_handler, TransformOutput,
|
||||
};
|
||||
use swc_atoms::JsWord;
|
||||
use swc_bundler::{Bundler, ModuleData, ModuleRecord};
|
||||
use swc_common::{
|
||||
collections::AHashMap, errors::Handler, BytePos, FileName, SourceMap, Span, DUMMY_SP,
|
||||
};
|
||||
use swc_ecma_loader::{
|
||||
resolvers::{lru::CachingResolver, node::NodeModulesResolver},
|
||||
NODE_BUILTINS,
|
||||
};
|
||||
use swc_ecmascript::{
|
||||
ast::*,
|
||||
parser::{lexer::Lexer, EsConfig, Parser, StringInput, Syntax},
|
||||
visit::{noop_visit_type, Node, Visit, VisitWith},
|
||||
};
|
||||
|
||||
#[js_function(1)]
|
||||
pub fn bundle(cx: CallContext) -> napi::Result<JsObject> {
|
||||
let option = cx.get_buffer_as_string(0)?;
|
||||
|
||||
let task = BundleTask {
|
||||
c: get_compiler(&cx),
|
||||
config: option,
|
||||
};
|
||||
|
||||
cx.env.spawn(task).map(|t| t.promise_object())
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||
struct BundleOption {
|
||||
entry: PathBuf,
|
||||
}
|
||||
|
||||
struct BundleTask {
|
||||
c: Arc<swc::Compiler>,
|
||||
config: String,
|
||||
}
|
||||
|
||||
impl Task for BundleTask {
|
||||
type Output = TransformOutput;
|
||||
|
||||
type JsValue = JsObject;
|
||||
|
||||
fn compute(&mut self) -> napi::Result<Self::Output> {
|
||||
let option: BundleOption = crate::util::deserialize_json(&self.config).convert_err()?;
|
||||
|
||||
try_with_handler(self.c.cm.clone(), true, |handler| {
|
||||
let builtins = NODE_BUILTINS
|
||||
.to_vec()
|
||||
.into_iter()
|
||||
.map(JsWord::from)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
//
|
||||
let mut bundler = Bundler::new(
|
||||
&self.c.globals(),
|
||||
self.c.cm.clone(),
|
||||
CustomLoader {
|
||||
cm: self.c.cm.clone(),
|
||||
handler: &handler,
|
||||
},
|
||||
make_resolver(),
|
||||
swc_bundler::Config {
|
||||
require: false,
|
||||
disable_inliner: false,
|
||||
external_modules: builtins,
|
||||
module: swc_bundler::ModuleType::Es,
|
||||
},
|
||||
Box::new(CustomHook),
|
||||
);
|
||||
|
||||
let mut entries = HashMap::default();
|
||||
let path: PathBuf = option.entry.into();
|
||||
let path = path
|
||||
.canonicalize()
|
||||
.context("failed to canonicalize entry file")?;
|
||||
entries.insert("main".to_string(), FileName::Real(path));
|
||||
let outputs = bundler.bundle(entries)?;
|
||||
|
||||
let output = outputs
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("swc_bundler::Bundle::bundle returned empty result"))?;
|
||||
|
||||
let source_map_names = {
|
||||
let mut v = SourceMapIdentCollector {
|
||||
names: Default::default(),
|
||||
};
|
||||
|
||||
output
|
||||
.module
|
||||
.visit_with(&Invalid { span: DUMMY_SP }, &mut v);
|
||||
|
||||
v.names
|
||||
};
|
||||
|
||||
let code = self.c.print(
|
||||
&output.module,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
EsVersion::Es5,
|
||||
SourceMapsConfig::Bool(true),
|
||||
&source_map_names,
|
||||
None,
|
||||
false,
|
||||
Some(BoolOrObject::Bool(true)),
|
||||
)?;
|
||||
|
||||
Ok(code)
|
||||
})
|
||||
.convert_err()
|
||||
}
|
||||
|
||||
fn resolve(self, env: napi::Env, output: Self::Output) -> napi::Result<Self::JsValue> {
|
||||
complete_output(&env, output)
|
||||
}
|
||||
}
|
||||
|
||||
type Resolver = Arc<CachingResolver<NodeModulesResolver>>;
|
||||
|
||||
fn make_resolver() -> Resolver {
|
||||
static CACHE: Lazy<Resolver> = Lazy::new(|| {
|
||||
// TODO: Make target env and alias configurable
|
||||
let r = NodeModulesResolver::new(TargetEnv::Node, Default::default());
|
||||
let r = CachingResolver::new(256, r);
|
||||
Arc::new(r)
|
||||
});
|
||||
|
||||
(*CACHE).clone()
|
||||
}
|
||||
|
||||
struct CustomLoader<'a> {
|
||||
handler: &'a Handler,
|
||||
cm: Arc<SourceMap>,
|
||||
}
|
||||
|
||||
impl swc_bundler::Load for CustomLoader<'_> {
|
||||
fn load(&self, f: &FileName) -> Result<ModuleData, Error> {
|
||||
let fm = match f {
|
||||
FileName::Real(path) => self.cm.load_file(&path)?,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let lexer = Lexer::new(
|
||||
Syntax::Es(EsConfig {
|
||||
..Default::default()
|
||||
}),
|
||||
EsVersion::Es2020,
|
||||
StringInput::from(&*fm),
|
||||
None,
|
||||
);
|
||||
|
||||
let mut parser = Parser::new_from(lexer);
|
||||
let module = parser.parse_module().map_err(|err| {
|
||||
err.into_diagnostic(&self.handler).emit();
|
||||
anyhow!("failed to parse")
|
||||
})?;
|
||||
|
||||
Ok(ModuleData {
|
||||
fm,
|
||||
module,
|
||||
helpers: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct CustomHook;
|
||||
|
||||
impl swc_bundler::Hook for CustomHook {
|
||||
fn get_import_meta_props(
|
||||
&self,
|
||||
_span: Span,
|
||||
_module_record: &ModuleRecord,
|
||||
) -> Result<Vec<KeyValueProp>, Error> {
|
||||
bail!("`import.meta` is not supported yet")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SourceMapIdentCollector {
|
||||
names: AHashMap<BytePos, JsWord>,
|
||||
}
|
||||
|
||||
impl Visit for SourceMapIdentCollector {
|
||||
noop_visit_type!();
|
||||
|
||||
fn visit_ident(&mut self, ident: &Ident, _: &dyn Node) {
|
||||
self.names.insert(ident.span.lo, ident.sym.clone());
|
||||
}
|
||||
}
|
|
@ -50,6 +50,7 @@ use swc_ecmascript::{
|
|||
|
||||
pub mod amp_attributes;
|
||||
mod auto_cjs;
|
||||
mod bundle;
|
||||
pub mod hook_optimizer;
|
||||
pub mod minify;
|
||||
pub mod next_dynamic;
|
||||
|
@ -110,6 +111,8 @@ fn init(mut exports: JsObject) -> napi::Result<()> {
|
|||
}));
|
||||
}
|
||||
|
||||
exports.create_named_method("bundle", bundle::bundle)?;
|
||||
|
||||
exports.create_named_method("transform", transform::transform)?;
|
||||
exports.create_named_method("transformSync", transform::transform_sync)?;
|
||||
|
||||
|
|
Loading…
Reference in a new issue