Skip to content

Commit

Permalink
enh: move define_emits and define_model to own files, support tes…
Browse files Browse the repository at this point in the history
…ting binding types
  • Loading branch information
phoenix-ru committed Nov 8, 2024
1 parent 8b69202 commit aea81d3
Show file tree
Hide file tree
Showing 11 changed files with 596 additions and 358 deletions.
6 changes: 5 additions & 1 deletion crates/fervid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ use errors::CompileError;
use fervid_codegen::CodegenContext;
pub use fervid_core::*;
use fervid_parser::SfcParser;
use fervid_transform::{style::should_transform_style_block, transform_sfc, TransformSfcOptions};
use fervid_transform::{
style::should_transform_style_block, transform_sfc, SetupBinding, TransformSfcOptions,
};
use fxhash::FxHasher32;
use std::{
borrow::Cow,
Expand Down Expand Up @@ -105,6 +107,7 @@ pub struct CompileResult {
pub styles: Vec<CompileEmittedStyle>,
pub other_assets: Vec<CompileEmittedAsset>,
pub source_map: Option<String>,
pub setup_bindings: Vec<SetupBinding>,
}

pub struct CompileEmittedStyle {
Expand Down Expand Up @@ -209,6 +212,7 @@ pub fn compile(source: &str, options: CompileOptions) -> Result<CompileResult, C
styles,
other_assets,
source_map,
setup_bindings: ctx.bindings_helper.setup_bindings,
})
}

Expand Down
13 changes: 7 additions & 6 deletions crates/fervid_napi/__tests__/defineEmits.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
// import { BindingTypes } from '@vue/compiler-core'
import { BindingTypes } from '..'
import { assertCode, compile } from './utils'
import { describe, expect, test } from 'vitest'

describe('defineEmits', () => {
test('basic usage', () => {
const { content } = compile(`
const { content, bindings } = compile(`
<script setup>
const myEmit = defineEmits(['foo', 'bar'])
</script>
`)
`, { outputSetupBindings: true })

assertCode(content)
// expect(bindings).toStrictEqual({
// myEmit: BindingTypes.SETUP_CONST,
// })
expect(bindings).toStrictEqual({
myEmit: BindingTypes.SetupConst,
})

// should remove defineEmits import and call
expect(content).not.toMatch('defineEmits')
Expand Down
5 changes: 3 additions & 2 deletions crates/fervid_napi/__tests__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ export function compile(src: string, options?: Partial<FervidCompileOptions>, lo
if (result.errors.length && logErrors) {
console.warn(result.errors[0])
}

return {
content: result.code,
errors: result.errors
errors: result.errors,
bindings: result.setupBindings,
}
}

Expand Down
50 changes: 50 additions & 0 deletions crates/fervid_napi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,16 @@ export interface FervidCompileOptions {
filename: string
/** Generate a const instead of default export */
genDefaultAs?: string
/** Whether setup bindings need to be serialized */
outputSetupBindings?: boolean
}
export interface CompileResult {
code: string
styles: Array<Style>
errors: Array<SerializedError>
customBlocks: Array<CustomBlock>
sourceMap?: string
setupBindings?: Record<string, BindingTypes> | undefined
}
export interface Style {
code: string
Expand All @@ -82,6 +85,53 @@ export interface SerializedError {
hi: number
message: string
}
/**
* This is a copied enum from `fervid_core` with `napi` implementation to avoid littering the core crate.
*
* The type of a binding (or identifier) which is used to show where this binding came from,
* e.g. `Data` is for Options API `data()`, `SetupRef` if for `ref`s and `computed`s in Composition API.
*
* <https://github.com/vuejs/core/blob/020851e57d9a9f727c6ea07e9c1575430af02b73/packages/compiler-core/src/options.ts#L76>
*/
export const enum BindingTypes {
/** returned from data() */
Data = 0,
/** declared as a prop */
Props = 1,
/**
* a local alias of a `<script setup>` destructured prop.
* the original is stored in __propsAliases of the bindingMetadata object.
*/
PropsAliased = 2,
/** a let binding (may or may not be a ref) */
SetupLet = 3,
/**
* a const binding that can never be a ref.
* these bindings don't need `unref()` calls when processed in inlined
* template expressions.
*/
SetupConst = 4,
/** a const binding that does not need `unref()`, but may be mutated. */
SetupReactiveConst = 5,
/** a const binding that may be a ref */
SetupMaybeRef = 6,
/** bindings that are guaranteed to be refs */
SetupRef = 7,
/** declared by other options, e.g. computed, inject */
Options = 8,
/** a literal constant, e.g. 'foo', 1, true */
LiteralConst = 9,
/** a `.vue` import or `defineComponent` call */
Component = 10,
/** an import which is not a `.vue` or `from 'vue'` */
Imported = 11,
/** a variable from the template */
TemplateLocal = 12,
/** a variable in the global Javascript context, e.g. `Array` or `undefined` */
JsGlobal = 13,
/** a non-resolved variable, presumably from the global Vue context */
Unresolved = 14,
}
export type FervidJsCompiler = Compiler
/** Fervid: a compiler for Vue.js written in Rust */
export declare class Compiler {
Expand Down
3 changes: 2 additions & 1 deletion crates/fervid_napi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { Compiler } = nativeBinding
const { Compiler, BindingTypes } = nativeBinding

module.exports.Compiler = Compiler
module.exports.BindingTypes = BindingTypes
70 changes: 47 additions & 23 deletions crates/fervid_napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use napi::bindgen_prelude::*;
use napi_derive::napi;

use fervid::{compile, CompileOptions};
use structs::{CompileResult, FervidCompileOptions, FervidJsCompiler, FervidJsCompilerOptions};
use structs::{
BindingTypes, CompileResult, FervidCompileOptions, FervidJsCompiler, FervidJsCompilerOptions,
};

mod structs;

Expand All @@ -25,10 +27,12 @@ impl FervidJsCompiler {
#[napi]
pub fn compile_sync(
&self,
env: Env,
source: String,
options: FervidCompileOptions,
) -> Result<CompileResult> {
self.compile_and_convert(&source, &options)
let compiled = self.compile_impl(&source, &options)?;
Ok(self.convert(env, compiled, &options))
}

#[napi]
Expand All @@ -46,43 +50,63 @@ impl FervidJsCompiler {
AsyncTask::with_optional_signal(task, signal)
}

fn compile_and_convert(
fn compile_impl(
&self,
source: &str,
options: &FervidCompileOptions,
) -> Result<CompileResult> {
) -> Result<fervid::CompileResult> {
// Normalize options to the ones defined in fervid
let compile_options = CompileOptions {
filename: Cow::Borrowed(&options.filename),
id: Cow::Borrowed(&options.id),
is_prod: self.options.is_production,
ssr: self.options.ssr,
gen_default_as: options.gen_default_as.as_ref().map(|v| Cow::Borrowed(v.as_str())),
source_map: self.options.source_map
gen_default_as: options
.gen_default_as
.as_ref()
.map(|v| Cow::Borrowed(v.as_str())),
source_map: self.options.source_map,
};

let native_compile_result =
compile(source, compile_options).map_err(|e| Error::from_reason(e.to_string()))?;
compile(source, compile_options).map_err(|e| Error::from_reason(e.to_string()))
}

fn convert(
&self,
env: Env,
mut result: fervid::CompileResult,
options: &FervidCompileOptions,
) -> CompileResult {
// Serialize bindings if requested
let setup_bindings = if matches!(options.output_setup_bindings, Some(true)) {
env.create_object()
.map(|mut obj| {
for binding in result.setup_bindings.drain(..) {
let _ = obj.set(binding.0.as_str(), BindingTypes::from(binding.1));
}
obj
})
.ok()
} else {
None
};

Ok(CompileResult {
code: native_compile_result.code,
source_map: native_compile_result.source_map,
custom_blocks: native_compile_result
CompileResult {
code: result.code,
source_map: result.source_map,
custom_blocks: result
.other_assets
.into_iter()
.map(|asset| asset.into())
.collect(),
errors: native_compile_result
.errors
.into_iter()
.map(|e| e.into())
.collect(),
styles: native_compile_result
errors: result.errors.into_iter().map(|e| e.into()).collect(),
styles: result
.styles
.into_iter()
.map(|style| style.into())
.collect(),
})
setup_bindings,
}
}
}

Expand All @@ -95,13 +119,13 @@ pub struct CompileTask {
#[napi]
impl Task for CompileTask {
type JsValue = CompileResult;
type Output = CompileResult;
type Output = fervid::CompileResult;

fn compute(&mut self) -> napi::Result<Self::Output> {
self.compiler.compile_and_convert(&self.input, &self.options)
self.compiler.compile_impl(&self.input, &self.options)
}

fn resolve(&mut self, _env: Env, result: Self::Output) -> napi::Result<Self::JsValue> {
Ok(result)
fn resolve(&mut self, env: Env, result: Self::Output) -> napi::Result<Self::JsValue> {
Ok(self.compiler.convert(env, result, &self.options))
}
}
76 changes: 74 additions & 2 deletions crates/fervid_napi/src/structs.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use napi::JsObject;
use napi_derive::napi;
use swc_core::common::Spanned;

Expand Down Expand Up @@ -60,7 +61,7 @@ pub struct FervidJsCompilerOptionsScript {
/// Default: true
pub hoist_static: Option<bool>,
/// Produce source maps
pub source_map: Option<bool>
pub source_map: Option<bool>,
}

#[napi(object)]
Expand All @@ -79,6 +80,8 @@ pub struct FervidCompileOptions {
pub filename: String,
/// Generate a const instead of default export
pub gen_default_as: Option<String>,
/// Whether setup bindings need to be serialized
pub output_setup_bindings: Option<bool>,
}

#[napi(object)]
Expand All @@ -87,7 +90,9 @@ pub struct CompileResult {
pub styles: Vec<Style>,
pub errors: Vec<SerializedError>,
pub custom_blocks: Vec<CustomBlock>,
pub source_map: Option<String>
pub source_map: Option<String>,
#[napi(ts_type = "Record<string, BindingTypes> | undefined")]
pub setup_bindings: Option<JsObject>,
}

#[napi(object)]
Expand All @@ -113,6 +118,73 @@ pub struct SerializedError {
pub message: String,
}

/// This is a copied enum from `fervid_core` with `napi` implementation to avoid littering the core crate.
///
/// The type of a binding (or identifier) which is used to show where this binding came from,
/// e.g. `Data` is for Options API `data()`, `SetupRef` if for `ref`s and `computed`s in Composition API.
///
/// <https://github.com/vuejs/core/blob/020851e57d9a9f727c6ea07e9c1575430af02b73/packages/compiler-core/src/options.ts#L76>
#[napi]
pub enum BindingTypes {
/// returned from data()
Data,
/// declared as a prop
Props,
/// a local alias of a `<script setup>` destructured prop.
/// the original is stored in __propsAliases of the bindingMetadata object.
PropsAliased,
/// a let binding (may or may not be a ref)
SetupLet,
/// a const binding that can never be a ref.
/// these bindings don't need `unref()` calls when processed in inlined
/// template expressions.
SetupConst,
/// a const binding that does not need `unref()`, but may be mutated.
SetupReactiveConst,
/// a const binding that may be a ref
SetupMaybeRef,
/// bindings that are guaranteed to be refs
SetupRef,
/// declared by other options, e.g. computed, inject
Options,
/// a literal constant, e.g. 'foo', 1, true
LiteralConst,

// Introduced by fervid:
/// a `.vue` import or `defineComponent` call
Component,
/// an import which is not a `.vue` or `from 'vue'`
Imported,
/// a variable from the template
TemplateLocal,
/// a variable in the global Javascript context, e.g. `Array` or `undefined`
JsGlobal,
/// a non-resolved variable, presumably from the global Vue context
Unresolved,
}

impl From<fervid::BindingTypes> for BindingTypes {
fn from(value: fervid::BindingTypes) -> Self {
match value {
fervid::BindingTypes::Data => BindingTypes::Data,
fervid::BindingTypes::Props => BindingTypes::Props,
fervid::BindingTypes::PropsAliased => BindingTypes::PropsAliased,
fervid::BindingTypes::SetupLet => BindingTypes::SetupLet,
fervid::BindingTypes::SetupConst => BindingTypes::SetupConst,
fervid::BindingTypes::SetupReactiveConst => BindingTypes::SetupReactiveConst,
fervid::BindingTypes::SetupMaybeRef => BindingTypes::SetupMaybeRef,
fervid::BindingTypes::SetupRef => BindingTypes::SetupRef,
fervid::BindingTypes::Options => BindingTypes::Options,
fervid::BindingTypes::LiteralConst => BindingTypes::LiteralConst,
fervid::BindingTypes::Component => BindingTypes::Component,
fervid::BindingTypes::Imported => BindingTypes::Imported,
fervid::BindingTypes::TemplateLocal => BindingTypes::TemplateLocal,
fervid::BindingTypes::JsGlobal => BindingTypes::JsGlobal,
fervid::BindingTypes::Unresolved => BindingTypes::Unresolved,
}
}
}

impl From<fervid::CompileEmittedStyle> for Style {
fn from(value: fervid::CompileEmittedStyle) -> Self {
Self {
Expand Down
Loading

0 comments on commit aea81d3

Please sign in to comment.