Skip to content

Commit

Permalink
Macro errors -> v2 (#9501)
Browse files Browse the repository at this point in the history
* Improve macro error handling

* flow
  • Loading branch information
devongovett authored Jan 21, 2024
1 parent e21b186 commit a5ff7e7
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 60 deletions.
23 changes: 15 additions & 8 deletions crates/node-bindings/src/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ mod native_only {
threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunctionCallMode},
JsBoolean, JsFunction, JsNumber, JsString, ValueType,
};
use parcel_js_swc_core::{JsValue, SourceLocation};
use parcel_js_swc_core::{JsValue, MacroError, SourceLocation};
use std::sync::Arc;

// Allocate a single channel per thread to communicate with the JS thread.
thread_local! {
static CHANNEL: (Sender<Result<JsValue, String>>, Receiver<Result<JsValue, String>>) = crossbeam_channel::unbounded();
static CHANNEL: (Sender<Result<JsValue, MacroError>>, Receiver<Result<JsValue, MacroError>>) = crossbeam_channel::unbounded();
}

struct CallMacroMessage {
Expand All @@ -33,6 +33,12 @@ mod native_only {
loc: SourceLocation,
}

#[napi(object)]
struct JsMacroError {
pub kind: u32,
pub message: String,
}

#[napi]
pub fn transform_async(opts: JsObject, env: Env) -> napi::Result<JsObject> {
let call_macro_tsfn = if opts.has_named_property("callMacro")? {
Expand Down Expand Up @@ -203,7 +209,7 @@ mod native_only {
fn await_promise(
env: Env,
result: JsUnknown,
tx: Sender<Result<JsValue, String>>,
tx: Sender<Result<JsValue, MacroError>>,
) -> napi::Result<()> {
// If the result is a promise, wait for it to resolve, and send the result to the channel.
// Otherwise, send the result immediately.
Expand All @@ -217,12 +223,13 @@ mod native_only {
ctx.env.get_undefined()
})?;
let eb = env.create_function_from_closure("error_callback", move |ctx| {
let res = ctx.get::<JsUnknown>(0)?;
let message = match napi_to_js_value(res, env)? {
JsValue::String(s) => s,
_ => "Unknown error".into(),
let res = ctx.get::<JsMacroError>(0)?;
let err = match res.kind {
1 => MacroError::LoadError(res.message),
2 => MacroError::ExecutionError(res.message),
_ => MacroError::LoadError("Invalid error kind".into()),
};
tx2.send(Err(message)).expect("send failure");
tx2.send(Err(err)).expect("send failure");
ctx.env.get_undefined()
})?;
then.call(Some(&result), &[cb, eb])?;
Expand Down
52 changes: 28 additions & 24 deletions packages/core/core/src/Transformation.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,24 +264,24 @@ export default class Transformation {
initialAsset: UncommittedAsset,
): Promise<Array<UncommittedAsset>> {
let initialType = initialAsset.value.type;
let assets: Array<UncommittedAsset> = await this.runPipeline(
pipeline,
initialAsset,
);

// Add dev dep requests for each transformer
for (let transformer of pipeline.transformers) {
await this.addDevDependency({
specifier: transformer.name,
resolveFrom: transformer.resolveFrom,
range: transformer.range,
});
}
let assets: Array<UncommittedAsset>;
try {
assets = await this.runPipeline(pipeline, initialAsset);
} finally {
// Add dev dep requests for each transformer
for (let transformer of pipeline.transformers) {
await this.addDevDependency({
specifier: transformer.name,
resolveFrom: transformer.resolveFrom,
range: transformer.range,
});
}

// Add dev dep requests for dependencies of transformer plugins
// (via proxied packageManager.require calls).
for (let devDep of this.pluginDevDeps) {
await this.addDevDependency(devDep);
// Add dev dep requests for dependencies of transformer plugins
// (via proxied packageManager.require calls).
for (let devDep of this.pluginDevDeps) {
await this.addDevDependency(devDep);
}
}

let finalAssets: Array<UncommittedAsset> = [];
Expand Down Expand Up @@ -324,13 +324,17 @@ export default class Transformation {
}

// Ensure that the package manager has an entry for this resolution.
await this.options.packageManager.resolve(
specifier,
fromProjectPath(this.options.projectRoot, opts.resolveFrom),
{
range,
},
);
try {
await this.options.packageManager.resolve(
specifier,
fromProjectPath(this.options.projectRoot, opts.resolveFrom),
{
range,
},
);
} catch (err) {
// ignore
}

let devDepRequest = await createDevDependency(
opts,
Expand Down
7 changes: 6 additions & 1 deletion packages/core/core/src/requests/DevDepRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ export async function createDevDependency(
let resolveFromAbsolute = fromProjectPath(options.projectRoot, resolveFrom);

// Ensure that the package manager has an entry for this resolution.
await options.packageManager.resolve(specifier, resolveFromAbsolute);
try {
await options.packageManager.resolve(specifier, resolveFromAbsolute);
} catch (err) {
// ignore
}

let invalidations = options.packageManager.getInvalidations(
specifier,
resolveFromAbsolute,
Expand Down
89 changes: 83 additions & 6 deletions packages/core/integration-tests/test/macros.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
// @flow strict-local
import assert from 'assert';
import invariant from 'assert';
import path from 'path';
import {bundle, run, overlayFS, fsFixture} from '@parcel/test-utils';
import {
bundle,
bundler,
run,
overlayFS,
fsFixture,
getNextBuild,
} from '@parcel/test-utils';

describe('macros', function () {
let count = 0;
Expand Down Expand Up @@ -368,7 +376,7 @@ describe('macros', function () {
} catch (err) {
assert.deepEqual(err.diagnostics, [
{
message: `Error evaluating macro: Could not resolve module "./macro.js" from "${path.join(
message: `Error loading macro: Could not resolve module "./macro.js" from "${path.join(
dir,
'index.js',
)}"`,
Expand All @@ -380,12 +388,12 @@ describe('macros', function () {
{
message: undefined,
start: {
line: 2,
column: 10,
line: 1,
column: 1,
},
end: {
line: 2,
column: 19,
line: 1,
column: 57,
},
},
],
Expand Down Expand Up @@ -561,6 +569,75 @@ describe('macros', function () {
assert.notEqual(match[1], match2[1]);
});

it('should only error once if a macro errors during loading', async function () {
await fsFixture(overlayFS, dir)`
index.js:
import { test } from "./macro.js" with { type: "macro" };
output = test(1, 2);
output2 = test(1, 3);
macro.js:
export function test() {
return Date.now(
}
`;

try {
await bundle(path.join(dir, '/index.js'), {
inputFS: overlayFS,
mode: 'production',
});
} catch (err) {
assert.equal(err.diagnostics.length, 1);
}
});

it('should rebuild in watch mode after fixing an error', async function () {
await fsFixture(overlayFS, dir)`
index.js:
import { test } from "./macro.ts" with { type: "macro" };
output = test('test.txt');
macro.ts:
export function test() {
return Date.now(
}
`;

let b = await bundler(path.join(dir, '/index.js'), {
inputFS: overlayFS,
mode: 'production',
shouldDisableCache: false,
});

let subscription;
try {
subscription = await b.watch();
let buildEvent = await getNextBuild(b);
assert.equal(buildEvent.type, 'buildFailure');

await fsFixture(overlayFS, dir)`
macro.ts:
export function test() {
return Date.now();
}
`;

buildEvent = await getNextBuild(b);
assert.equal(buildEvent.type, 'buildSuccess');
invariant(buildEvent.type === 'buildSuccess'); // flow

let res = await overlayFS.readFile(
buildEvent.bundleGraph.getBundles()[0].filePath,
'utf8',
);
let match = res.match(/output=(\d+)/);
assert(match);
} finally {
await subscription?.unsubscribe();
}
});

it('should support evaluating constants', async function () {
await fsFixture(overlayFS, dir)`
index.js:
Expand Down
2 changes: 1 addition & 1 deletion packages/transformers/js/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ use node_replacer::NodeReplacer;
use typeof_replacer::*;
use utils::{error_buffer_to_diagnostics, Diagnostic, DiagnosticSeverity, ErrorBuffer, SourceType};

pub use crate::macros::JsValue;
use crate::macros::Macros;
pub use crate::macros::{JsValue, MacroError};
pub use utils::SourceLocation;

type SourceMapBuffer = Vec<(swc_core::common::BytePos, swc_core::common::LineCol)>;
Expand Down
Loading

0 comments on commit a5ff7e7

Please sign in to comment.