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",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "build_const"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.3"
|
version = "1.4.3"
|
||||||
|
@ -214,6 +220,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
|
||||||
|
dependencies = [
|
||||||
|
"build_const",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -364,6 +379,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixedbitset"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
@ -506,6 +527,7 @@ checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.1",
|
"autocfg 1.0.1",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -728,6 +750,7 @@ dependencies = [
|
||||||
"napi",
|
"napi",
|
||||||
"napi-build",
|
"napi-build",
|
||||||
"napi-derive",
|
"napi-derive",
|
||||||
|
"once_cell",
|
||||||
"path-clean",
|
"path-clean",
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -737,8 +760,10 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"swc",
|
"swc",
|
||||||
"swc_atoms",
|
"swc_atoms",
|
||||||
|
"swc_bundler",
|
||||||
"swc_common",
|
"swc_common",
|
||||||
"swc_css",
|
"swc_css",
|
||||||
|
"swc_ecma_loader",
|
||||||
"swc_ecma_preset_env",
|
"swc_ecma_preset_env",
|
||||||
"swc_ecma_transforms_testing",
|
"swc_ecma_transforms_testing",
|
||||||
"swc_ecmascript",
|
"swc_ecmascript",
|
||||||
|
@ -913,6 +938,16 @@ version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
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]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
@ -1046,6 +1081,12 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radix_fmt"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
|
@ -1631,6 +1672,38 @@ dependencies = [
|
||||||
"string_cache_codegen",
|
"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]]
|
[[package]]
|
||||||
name = "swc_common"
|
name = "swc_common"
|
||||||
version = "0.14.3"
|
version = "0.14.3"
|
||||||
|
@ -1938,6 +2011,7 @@ checksum = "6d47323456ee73ecffa584a3964cc82dbf3daee2c7c72221d1f2130d3e42312d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"phf",
|
"phf",
|
||||||
|
"rayon",
|
||||||
"scoped-tls",
|
"scoped-tls",
|
||||||
"smallvec 1.7.0",
|
"smallvec 1.7.0",
|
||||||
"swc_atoms",
|
"swc_atoms",
|
||||||
|
@ -2032,6 +2106,7 @@ dependencies = [
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"rayon",
|
||||||
"retain_mut",
|
"retain_mut",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"swc_atoms",
|
"swc_atoms",
|
||||||
|
@ -2137,6 +2212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8762b5fccb1bbb5f4bcb83eb6668a78b3861ae8df692c76e75cc33605d611480"
|
checksum = "8762b5fccb1bbb5f4bcb83eb6668a78b3861ae8df692c76e75cc33605d611480"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"rayon",
|
||||||
"swc_atoms",
|
"swc_atoms",
|
||||||
"swc_common",
|
"swc_common",
|
||||||
"swc_ecma_ast",
|
"swc_ecma_ast",
|
||||||
|
|
|
@ -11,26 +11,28 @@ anyhow = "1.0"
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
easy-error = "1.0.0"
|
easy-error = "1.0.0"
|
||||||
napi = { version = "1", features = ["serde-json"] }
|
fxhash = "0.2.1"
|
||||||
|
napi = {version = "1", features = ["serde-json"]}
|
||||||
napi-derive = "1"
|
napi-derive = "1"
|
||||||
|
once_cell = "1.8.0"
|
||||||
path-clean = "0.1"
|
path-clean = "0.1"
|
||||||
|
pathdiff = "0.2.0"
|
||||||
regex = "1.5"
|
regex = "1.5"
|
||||||
|
retain_mut = "0.1.3"
|
||||||
|
rustc-hash = "1.1.0"
|
||||||
serde = "1"
|
serde = "1"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
swc = "0.81.1"
|
swc = "0.81.1"
|
||||||
swc_atoms = "0.2.7"
|
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_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_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_node_base = "0.5.1"
|
||||||
swc_stylis = "0.17.0"
|
swc_stylis = "0.17.0"
|
||||||
fxhash = "0.2.1"
|
tracing = {version = "0.1.28", features = ["release_max_level_off"]}
|
||||||
retain_mut = "0.1.3"
|
|
||||||
pathdiff = "0.2.0"
|
|
||||||
rustc-hash = "1.1.0"
|
|
||||||
tracing = { version = "0.1.28", features = ["release_max_level_off"] }
|
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
napi-build = "1"
|
napi-build = "1"
|
||||||
|
|
|
@ -91,6 +91,10 @@ export function minifySync(src, opts) {
|
||||||
return bindings.minifySync(toBuffer(src), toBuffer(opts ?? {}))
|
return bindings.minifySync(toBuffer(src), toBuffer(opts ?? {}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function bundle(options) {
|
||||||
|
return bindings.bundle(toBuffer(options))
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.transform = transform
|
module.exports.transform = transform
|
||||||
module.exports.transformSync = transformSync
|
module.exports.transformSync = transformSync
|
||||||
module.exports.minify = minify
|
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;
|
pub mod amp_attributes;
|
||||||
mod auto_cjs;
|
mod auto_cjs;
|
||||||
|
mod bundle;
|
||||||
pub mod hook_optimizer;
|
pub mod hook_optimizer;
|
||||||
pub mod minify;
|
pub mod minify;
|
||||||
pub mod next_dynamic;
|
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("transform", transform::transform)?;
|
||||||
exports.create_named_method("transformSync", transform::transform_sync)?;
|
exports.create_named_method("transformSync", transform::transform_sync)?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue