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},
|
util::{deserialize_json, CtxtExt, MapErr},
|
||||||
};
|
};
|
||||||
use next_swc::{custom_before_pass, TransformOptions};
|
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 napi::{CallContext, Env, JsBoolean, JsBuffer, JsObject, JsString, JsUnknown, Status, Task};
|
||||||
|
use std::fs::read_to_string;
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
panic::{catch_unwind, AssertUnwindSafe},
|
panic::{catch_unwind, AssertUnwindSafe},
|
||||||
|
@ -48,6 +49,8 @@ use swc_ecmascript::transforms::pass::noop;
|
||||||
pub enum Input {
|
pub enum Input {
|
||||||
/// Raw source code.
|
/// Raw source code.
|
||||||
Source { src: String },
|
Source { src: String },
|
||||||
|
/// Get source code from filename in options
|
||||||
|
FromFilename,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TransformTask {
|
pub struct TransformTask {
|
||||||
|
@ -63,30 +66,43 @@ impl Task for TransformTask {
|
||||||
fn compute(&mut self) -> napi::Result<Self::Output> {
|
fn compute(&mut self) -> napi::Result<Self::Output> {
|
||||||
let res = catch_unwind(AssertUnwindSafe(|| {
|
let res = catch_unwind(AssertUnwindSafe(|| {
|
||||||
try_with_handler(self.c.cm.clone(), true, |handler| {
|
try_with_handler(self.c.cm.clone(), true, |handler| {
|
||||||
self.c.run(|| match &self.input {
|
self.c.run(|| {
|
||||||
Input::Source { src } => {
|
let options: TransformOptions = deserialize_json(&self.options)?;
|
||||||
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() {
|
self.c.cm.new_source_file(filename, src.to_string())
|
||||||
FileName::Anon
|
}
|
||||||
} else {
|
Input::FromFilename => {
|
||||||
FileName::Real(options.swc.filename.clone().into())
|
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(
|
||||||
let before_pass = custom_before_pass(fm.clone(), &options);
|
fm.clone(),
|
||||||
self.c.process_js_with_custom_pass(
|
None,
|
||||||
fm.clone(),
|
&handler,
|
||||||
None,
|
&options.swc,
|
||||||
&handler,
|
|_| before_pass,
|
||||||
&options.swc,
|
|_| noop(),
|
||||||
|_| before_pass,
|
)
|
||||||
|_| noop(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
@ -115,22 +131,23 @@ impl Task for TransformTask {
|
||||||
/// returns `compiler, (src / path), options, plugin, callback`
|
/// returns `compiler, (src / path), options, plugin, callback`
|
||||||
pub fn schedule_transform<F>(cx: CallContext, op: F) -> napi::Result<JsObject>
|
pub fn schedule_transform<F>(cx: CallContext, op: F) -> napi::Result<JsObject>
|
||||||
where
|
where
|
||||||
F: FnOnce(&Arc<Compiler>, String, bool, String) -> TransformTask,
|
F: FnOnce(&Arc<Compiler>, Input, bool, String) -> TransformTask,
|
||||||
{
|
{
|
||||||
let c = get_compiler(&cx);
|
let c = get_compiler(&cx);
|
||||||
|
|
||||||
let unknown_src = cx.get::<JsUnknown>(0)?;
|
let unknown_src = cx.get::<JsUnknown>(0)?;
|
||||||
let src = match unknown_src.get_type()? {
|
let src = match unknown_src.get_type()? {
|
||||||
napi::ValueType::String => napi::Result::Ok(
|
napi::ValueType::String => napi::Result::Ok(Input::Source {
|
||||||
JsString::try_from(unknown_src)?
|
src: JsString::try_from(unknown_src)?
|
||||||
.into_utf8()?
|
.into_utf8()?
|
||||||
.as_str()?
|
.as_str()?
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
),
|
}),
|
||||||
napi::ValueType::Object => napi::Result::Ok(
|
napi::ValueType::Object => napi::Result::Ok(Input::Source {
|
||||||
String::from_utf8_lossy(JsBuffer::try_from(unknown_src)?.into_value()?.as_ref())
|
src: String::from_utf8_lossy(JsBuffer::try_from(unknown_src)?.into_value()?.as_ref())
|
||||||
.to_string(),
|
.to_string(),
|
||||||
),
|
}),
|
||||||
|
napi::ValueType::Undefined => napi::Result::Ok(Input::FromFilename),
|
||||||
_ => Err(napi::Error::new(
|
_ => Err(napi::Error::new(
|
||||||
Status::GenericFailure,
|
Status::GenericFailure,
|
||||||
"first argument must be a String or Buffer".to_string(),
|
"first argument must be a String or Buffer".to_string(),
|
||||||
|
@ -175,14 +192,10 @@ where
|
||||||
|
|
||||||
#[js_function(4)]
|
#[js_function(4)]
|
||||||
pub fn transform(cx: CallContext) -> napi::Result<JsObject> {
|
pub fn transform(cx: CallContext) -> napi::Result<JsObject> {
|
||||||
schedule_transform(cx, |c, src, _, options| {
|
schedule_transform(cx, |c, input, _, options| TransformTask {
|
||||||
let input = Input::Source { src };
|
c: c.clone(),
|
||||||
|
input,
|
||||||
TransformTask {
|
options,
|
||||||
c: c.clone(),
|
|
||||||
input,
|
|
||||||
options,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,10 @@ function loadNative() {
|
||||||
if (bindings) {
|
if (bindings) {
|
||||||
return {
|
return {
|
||||||
transform(src, options) {
|
transform(src, options) {
|
||||||
const isModule = typeof src !== 'string' && !Buffer.isBuffer(src)
|
const isModule =
|
||||||
|
typeof src !== undefined &&
|
||||||
|
typeof src !== 'string' &&
|
||||||
|
!Buffer.isBuffer(src)
|
||||||
options = options || {}
|
options = options || {}
|
||||||
|
|
||||||
if (options?.jsc?.parser) {
|
if (options?.jsc?.parser) {
|
||||||
|
@ -77,7 +80,16 @@ function loadNative() {
|
||||||
},
|
},
|
||||||
|
|
||||||
transformSync(src, options) {
|
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 || {}
|
options = options || {}
|
||||||
|
|
||||||
if (options?.jsc?.parser) {
|
if (options?.jsc?.parser) {
|
||||||
|
|
|
@ -28,6 +28,7 @@ DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
import { transform } from '../../swc'
|
import { transform } from '../../swc'
|
||||||
import { getLoaderSWCOptions } from '../../swc/options'
|
import { getLoaderSWCOptions } from '../../swc/options'
|
||||||
|
import { isAbsolute } from 'path'
|
||||||
|
|
||||||
async function loaderTransform(parentTrace, source, inputSourceMap) {
|
async function loaderTransform(parentTrace, source, inputSourceMap) {
|
||||||
// Make the loader async
|
// 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) {
|
export default function swcLoader(inputSource, inputSourceMap) {
|
||||||
const loaderSpan = this.currentTraceSpan.traceChild('next-swc-loader')
|
const loaderSpan = this.currentTraceSpan.traceChild('next-swc-loader')
|
||||||
const callback = this.async()
|
const callback = this.async()
|
||||||
|
|
Loading…
Reference in a new issue