read file in swc when no loaders follow the next-swc-loader (#31682)
Fixes #31685 ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` Co-authored-by: Tim Neutkens <6324199+timneutkens@users.noreply.github.com>
This commit is contained in:
parent
b71f9d9475
commit
a4159321b2
3 changed files with 87 additions and 39 deletions
|
@ -31,8 +31,9 @@ use crate::{
|
|||
util::{deserialize_json, CtxtExt, MapErr},
|
||||
};
|
||||
use next_swc::{custom_before_pass, TransformOptions};
|
||||
use anyhow::{anyhow, Context as _, Error};
|
||||
use anyhow::{anyhow, bail, Context as _, Error};
|
||||
use napi::{CallContext, Env, JsBoolean, JsBuffer, JsObject, JsString, JsUnknown, Status, Task};
|
||||
use std::fs::read_to_string;
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
panic::{catch_unwind, AssertUnwindSafe},
|
||||
|
@ -48,6 +49,8 @@ use swc_ecmascript::transforms::pass::noop;
|
|||
pub enum Input {
|
||||
/// Raw source code.
|
||||
Source { src: String },
|
||||
/// Get source code from filename in options
|
||||
FromFilename,
|
||||
}
|
||||
|
||||
pub struct TransformTask {
|
||||
|
@ -63,30 +66,43 @@ impl Task for TransformTask {
|
|||
fn compute(&mut self) -> napi::Result<Self::Output> {
|
||||
let res = catch_unwind(AssertUnwindSafe(|| {
|
||||
try_with_handler(self.c.cm.clone(), true, |handler| {
|
||||
self.c.run(|| match &self.input {
|
||||
Input::Source { src } => {
|
||||
let options: TransformOptions = deserialize_json(&self.options)?;
|
||||
self.c.run(|| {
|
||||
let options: TransformOptions = deserialize_json(&self.options)?;
|
||||
let fm = match &self.input {
|
||||
Input::Source { src } => {
|
||||
let filename = if options.swc.filename.is_empty() {
|
||||
FileName::Anon
|
||||
} else {
|
||||
FileName::Real(options.swc.filename.clone().into())
|
||||
};
|
||||
|
||||
let filename = if options.swc.filename.is_empty() {
|
||||
FileName::Anon
|
||||
} else {
|
||||
FileName::Real(options.swc.filename.clone().into())
|
||||
};
|
||||
self.c.cm.new_source_file(filename, src.to_string())
|
||||
}
|
||||
Input::FromFilename => {
|
||||
let filename = &options.swc.filename;
|
||||
if filename.is_empty() {
|
||||
bail!("no filename is provided via options");
|
||||
}
|
||||
|
||||
let fm = self.c.cm.new_source_file(filename, src.to_string());
|
||||
self.c.cm.new_source_file(
|
||||
FileName::Real(filename.into()),
|
||||
read_to_string(filename).with_context(|| {
|
||||
format!("Failed to read source code from {}", filename)
|
||||
})?,
|
||||
)
|
||||
}
|
||||
};
|
||||
let options = options.patch(&fm);
|
||||
|
||||
let options = options.patch(&fm);
|
||||
|
||||
let before_pass = custom_before_pass(fm.clone(), &options);
|
||||
self.c.process_js_with_custom_pass(
|
||||
fm.clone(),
|
||||
None,
|
||||
&handler,
|
||||
&options.swc,
|
||||
|_| before_pass,
|
||||
|_| noop(),
|
||||
)
|
||||
}
|
||||
let before_pass = custom_before_pass(fm.clone(), &options);
|
||||
self.c.process_js_with_custom_pass(
|
||||
fm.clone(),
|
||||
None,
|
||||
&handler,
|
||||
&options.swc,
|
||||
|_| before_pass,
|
||||
|_| noop(),
|
||||
)
|
||||
})
|
||||
})
|
||||
}))
|
||||
|
@ -115,22 +131,23 @@ impl Task for TransformTask {
|
|||
/// returns `compiler, (src / path), options, plugin, callback`
|
||||
pub fn schedule_transform<F>(cx: CallContext, op: F) -> napi::Result<JsObject>
|
||||
where
|
||||
F: FnOnce(&Arc<Compiler>, String, bool, String) -> TransformTask,
|
||||
F: FnOnce(&Arc<Compiler>, Input, bool, String) -> TransformTask,
|
||||
{
|
||||
let c = get_compiler(&cx);
|
||||
|
||||
let unknown_src = cx.get::<JsUnknown>(0)?;
|
||||
let src = match unknown_src.get_type()? {
|
||||
napi::ValueType::String => napi::Result::Ok(
|
||||
JsString::try_from(unknown_src)?
|
||||
napi::ValueType::String => napi::Result::Ok(Input::Source {
|
||||
src: JsString::try_from(unknown_src)?
|
||||
.into_utf8()?
|
||||
.as_str()?
|
||||
.to_owned(),
|
||||
),
|
||||
napi::ValueType::Object => napi::Result::Ok(
|
||||
String::from_utf8_lossy(JsBuffer::try_from(unknown_src)?.into_value()?.as_ref())
|
||||
}),
|
||||
napi::ValueType::Object => napi::Result::Ok(Input::Source {
|
||||
src: String::from_utf8_lossy(JsBuffer::try_from(unknown_src)?.into_value()?.as_ref())
|
||||
.to_string(),
|
||||
),
|
||||
}),
|
||||
napi::ValueType::Undefined => napi::Result::Ok(Input::FromFilename),
|
||||
_ => Err(napi::Error::new(
|
||||
Status::GenericFailure,
|
||||
"first argument must be a String or Buffer".to_string(),
|
||||
|
@ -175,14 +192,10 @@ where
|
|||
|
||||
#[js_function(4)]
|
||||
pub fn transform(cx: CallContext) -> napi::Result<JsObject> {
|
||||
schedule_transform(cx, |c, src, _, options| {
|
||||
let input = Input::Source { src };
|
||||
|
||||
TransformTask {
|
||||
c: c.clone(),
|
||||
input,
|
||||
options,
|
||||
}
|
||||
schedule_transform(cx, |c, input, _, options| TransformTask {
|
||||
c: c.clone(),
|
||||
input,
|
||||
options,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,10 @@ function loadNative() {
|
|||
if (bindings) {
|
||||
return {
|
||||
transform(src, options) {
|
||||
const isModule = typeof src !== 'string' && !Buffer.isBuffer(src)
|
||||
const isModule =
|
||||
typeof src !== undefined &&
|
||||
typeof src !== 'string' &&
|
||||
!Buffer.isBuffer(src)
|
||||
options = options || {}
|
||||
|
||||
if (options?.jsc?.parser) {
|
||||
|
@ -77,7 +80,16 @@ function loadNative() {
|
|||
},
|
||||
|
||||
transformSync(src, options) {
|
||||
const isModule = typeof src !== 'string' && !Buffer.isBuffer(src)
|
||||
if (typeof src === undefined) {
|
||||
throw new Error(
|
||||
"transformSync doesn't implement reading the file from filesystem"
|
||||
)
|
||||
} else if (Buffer.isBuffer(src)) {
|
||||
throw new Error(
|
||||
"transformSync doesn't implement taking the source code as Buffer"
|
||||
)
|
||||
}
|
||||
const isModule = typeof src !== 'string'
|
||||
options = options || {}
|
||||
|
||||
if (options?.jsc?.parser) {
|
||||
|
|
|
@ -28,6 +28,7 @@ DEALINGS IN THE SOFTWARE.
|
|||
|
||||
import { transform } from '../../swc'
|
||||
import { getLoaderSWCOptions } from '../../swc/options'
|
||||
import { isAbsolute } from 'path'
|
||||
|
||||
async function loaderTransform(parentTrace, source, inputSourceMap) {
|
||||
// Make the loader async
|
||||
|
@ -92,6 +93,28 @@ async function loaderTransform(parentTrace, source, inputSourceMap) {
|
|||
)
|
||||
}
|
||||
|
||||
export function pitch() {
|
||||
if (
|
||||
this.loaders.length - 1 === this.loaderIndex &&
|
||||
isAbsolute(this.resourcePath)
|
||||
) {
|
||||
const loaderSpan = this.currentTraceSpan.traceChild('next-swc-loader')
|
||||
const callback = this.async()
|
||||
loaderSpan
|
||||
.traceAsyncFn(() => loaderTransform.call(this, loaderSpan))
|
||||
.then(
|
||||
([transformedSource, outputSourceMap]) => {
|
||||
this.addDependency(this.resourcePath)
|
||||
callback(null, transformedSource, outputSourceMap)
|
||||
},
|
||||
(err) => {
|
||||
this.addDependency(this.resourcePath)
|
||||
callback(err)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default function swcLoader(inputSource, inputSourceMap) {
|
||||
const loaderSpan = this.currentTraceSpan.traceChild('next-swc-loader')
|
||||
const callback = this.async()
|
||||
|
|
Loading…
Reference in a new issue