Skip to content

Commit

Permalink
read file in swc when no loaders follow the next-swc-loader (vercel#3…
Browse files Browse the repository at this point in the history
…1682)

Fixes vercel#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 <[email protected]>
  • Loading branch information
sokra and timneutkens authored Nov 25, 2021
1 parent b71f9d9 commit a415932
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 42 deletions.
93 changes: 53 additions & 40 deletions packages/next-swc/crates/napi/src/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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 {
Expand All @@ -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)?;

let filename = if options.swc.filename.is_empty() {
FileName::Anon
} else {
FileName::Real(options.swc.filename.clone().into())
};

let fm = self.c.cm.new_source_file(filename, src.to_string());

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(),
)
}
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())
};

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");
}

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 before_pass = custom_before_pass(fm.clone(), &options);
self.c.process_js_with_custom_pass(
fm.clone(),
None,
&handler,
&options.swc,
|_| before_pass,
|_| noop(),
)
})
})
}))
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
})
}

Expand Down
16 changes: 14 additions & 2 deletions packages/next/build/swc/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
23 changes: 23 additions & 0 deletions packages/next/build/webpack/loaders/next-swc-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit a415932

Please sign in to comment.