diff --git a/cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.md b/cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.md index b0004c24..787c9714 100644 --- a/cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.md +++ b/cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.md @@ -197,8 +197,8 @@ Generated by [AVA](https://avajs.dev). ␊ export declare class Fib3 {␊ current: number␊ - next: number␊ - constructor(current: number, next: number)␊ + nextNum: number␊ + constructor(current: number, nextNum: number)␊ [Symbol.iterator](): Iterator␊ }␊ ␊ diff --git a/cli/src/utils/typegen.ts b/cli/src/utils/typegen.ts index 21b3139b..27e614fd 100644 --- a/cli/src/utils/typegen.ts +++ b/cli/src/utils/typegen.ts @@ -15,6 +15,7 @@ enum TypeDefKind { Type = 'type', Fn = 'fn', Struct = 'struct', + Extends = 'extends', Impl = 'impl', } @@ -23,6 +24,7 @@ interface TypeDefLine { name: string original_name?: string def: string + extends?: string js_doc?: string js_mod?: string } @@ -57,7 +59,8 @@ function prettyPrint( break case TypeDefKind.Struct: - s += `${exportDeclare(ambient)} class ${line.name} {\n${line.def}\n}` + const extendsDef = line.extends ? ` extends ${line.extends}` : '' + s += `${exportDeclare(ambient)} class ${line.name}${extendsDef} {\n${line.def}\n}` if (line.original_name && line.original_name !== line.name) { s += `\nexport type ${line.original_name} = ${line.name}` } @@ -185,6 +188,11 @@ function preprocessTypeDef(defs: TypeDefLine[]): Map { if (def.kind === TypeDefKind.Struct) { group.push(def) classDefs.set(def.name, def) + } else if (def.kind === TypeDefKind.Extends) { + const classDef = classDefs.get(def.name) + if (classDef) { + classDef.extends = def.def + } } else if (def.kind === TypeDefKind.Impl) { // merge `impl` into class definition const classDef = classDefs.get(def.name) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index c6893f58..03b93159 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -87,6 +87,7 @@ pub struct NapiStruct { pub register_name: Ident, pub kind: NapiStructKind, pub has_lifetime: bool, + pub is_generator: bool, } #[derive(Debug, Clone)] diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 5b8abd23..46971b2e 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -20,7 +20,7 @@ pub struct TypeDef { pub original_name: Option, pub def: String, pub js_mod: Option, - pub js_doc: String, + pub js_doc: JSDoc, } thread_local! { @@ -33,25 +33,6 @@ fn add_alias(name: String, alias: String) { }); } -pub fn js_doc_from_comments(comments: &[String]) -> String { - if comments.is_empty() { - return "".to_owned(); - } - - if comments.len() == 1 { - return format!("/**{} */\n", comments[0]); - } - - format!( - "/**\n{} */\n", - comments - .iter() - .map(|c| format!(" *{c}\n")) - .collect::>() - .join("") - ) -} - fn escape_json(src: &str) -> String { use std::fmt::Write; let mut escaped = String::with_capacity(src.len()); @@ -80,6 +61,83 @@ fn escape_json(src: &str) -> String { escaped } +#[derive(Default, Debug)] +pub struct JSDoc { + blocks: Vec>, +} + +impl JSDoc { + pub fn new(initial_lines: I) -> JSDoc + where + I: IntoIterator, + S: Into, + { + let block = Self::cleanup_lines(initial_lines); + if block.is_empty() { + return Self { blocks: vec![] }; + } + + Self { + blocks: vec![block], + } + } + + pub fn add_block(&mut self, lines: I) + where + I: IntoIterator, + S: Into, + { + let v: Vec = Self::cleanup_lines(lines); + + if !v.is_empty() { + self.blocks.push(v); + } + } + + fn cleanup_lines(lines: I) -> Vec + where + I: IntoIterator, + S: Into, + { + let raw: Vec = lines.into_iter().map(Into::into).collect(); + + if let (Some(first_non_blank), Some(last_non_blank)) = ( + raw.iter().position(|l| !l.trim().is_empty()), + raw.iter().rposition(|l| !l.trim().is_empty()), + ) { + raw[first_non_blank..=last_non_blank] + .iter() + .map(|l| l.trim().to_owned()) + .collect() + } else { + Vec::new() + } + } +} + +impl Display for JSDoc { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if self.blocks.is_empty() { + return Ok(()); + } + + if self.blocks.len() == 1 && self.blocks[0].len() == 1 { + return writeln!(f, "/** {} */", self.blocks[0][0]); + } + + writeln!(f, "/**")?; + for (i, block) in self.blocks.iter().enumerate() { + for line in block { + writeln!(f, " * {line}")?; + } + if i + 1 != self.blocks.len() { + writeln!(f, " *")?; + } + } + writeln!(f, " */") + } +} + impl Display for TypeDef { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let js_mod = if let Some(js_mod) = &self.js_mod { @@ -98,7 +156,7 @@ impl Display for TypeDef { r#"{{"kind": "{}", "name": "{}", "js_doc": "{}", "def": "{}"{}{}}}"#, self.kind, self.name, - escape_json(&self.js_doc), + escape_json(&self.js_doc.to_string()), escape_json(&self.def), original_name, js_mod, diff --git a/crates/backend/src/typegen/const.rs b/crates/backend/src/typegen/const.rs index e3933d4e..9338cabb 100644 --- a/crates/backend/src/typegen/const.rs +++ b/crates/backend/src/typegen/const.rs @@ -1,6 +1,10 @@ use super::{ToTypeDef, TypeDef}; -use crate::{js_doc_from_comments, ty_to_ts_type, typegen::add_alias, NapiConst}; +use crate::{ + ty_to_ts_type, + typegen::{add_alias, JSDoc}, + NapiConst, +}; impl ToTypeDef for NapiConst { fn to_type_def(&self) -> Option { @@ -20,7 +24,7 @@ impl ToTypeDef for NapiConst { ty_to_ts_type(&self.type_name, false, false, false).0 ), js_mod: self.js_mod.to_owned(), - js_doc: js_doc_from_comments(&self.comments), + js_doc: JSDoc::new(&self.comments), }) } } diff --git a/crates/backend/src/typegen/enum.rs b/crates/backend/src/typegen/enum.rs index 8345f2c8..df8e718d 100644 --- a/crates/backend/src/typegen/enum.rs +++ b/crates/backend/src/typegen/enum.rs @@ -1,5 +1,5 @@ use super::{add_alias, ToTypeDef, TypeDef}; -use crate::{js_doc_from_comments, NapiEnum, NapiEnumValue}; +use crate::{typegen::JSDoc, NapiEnum, NapiEnumValue}; impl ToTypeDef for NapiEnum { fn to_type_def(&self) -> Option { @@ -18,7 +18,7 @@ impl ToTypeDef for NapiEnum { name: self.js_name.to_owned(), original_name: Some(self.name.to_string()), def: self.gen_ts_variants(), - js_doc: js_doc_from_comments(&self.comments), + js_doc: JSDoc::new(&self.comments), js_mod: self.js_mod.to_owned(), }) } @@ -34,7 +34,7 @@ impl NapiEnum { NapiEnumValue::Number(num) => format!("{num}"), NapiEnumValue::String(string) => format!("'{string}'"), }; - format!("{}{} = {}", js_doc_from_comments(&v.comments), v.name, val) + format!("{}{} = {}", JSDoc::new(&v.comments), v.name, val) }) .collect::>() .join(",\n ") diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index 3cd64ef5..00c04957 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -4,7 +4,7 @@ use std::fmt::{Display, Formatter}; use syn::{Member, Pat, PathArguments, PathSegment}; use super::{r#struct::CLASS_STRUCTS, ty_to_ts_type, ToTypeDef, TypeDef}; -use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn}; +use crate::{typegen::JSDoc, CallbackArg, FnKind, NapiFn}; pub(crate) struct FnArg { pub(crate) arg: String, @@ -100,7 +100,7 @@ impl ToTypeDef for NapiFn { original_name: None, def, js_mod: self.js_mod.to_owned(), - js_doc: js_doc_from_comments(&self.comments), + js_doc: JSDoc::new(&self.comments), }) } } @@ -329,7 +329,6 @@ impl NapiFn { } else { "void".to_owned() }; - if self.is_async { format!(": Promise<{ret}>") } else { diff --git a/crates/backend/src/typegen/struct.rs b/crates/backend/src/typegen/struct.rs index 06789989..cb37f3b4 100644 --- a/crates/backend/src/typegen/struct.rs +++ b/crates/backend/src/typegen/struct.rs @@ -1,10 +1,11 @@ use std::collections::HashMap; +use std::vec::Vec; use std::{cell::RefCell, iter}; use super::{add_alias, ToTypeDef, TypeDef}; +use crate::typegen::JSDoc; use crate::{ - format_js_property_name, js_doc_from_comments, ty_to_ts_type, NapiImpl, NapiStruct, - NapiStructField, NapiStructKind, + format_js_property_name, ty_to_ts_type, NapiImpl, NapiStruct, NapiStructField, NapiStructKind, }; thread_local! { @@ -20,6 +21,17 @@ impl ToTypeDef for NapiStruct { }); add_alias(self.name.to_string(), self.js_name.to_string()); + let mut js_doc = JSDoc::new(&self.comments); + if self.is_generator { + let generator_doc =[ +"This type extends JavaScript's `Iterator`, and so has the iterator helper", +"methods. It may extend the upcoming TypeScript `Iterator` class in the future.", +"", +"@see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods", +"@see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods", ]; + js_doc.add_block(generator_doc) + } + Some(TypeDef { kind: String::from(match self.kind { NapiStructKind::Transparent(_) => "type", @@ -32,7 +44,7 @@ impl ToTypeDef for NapiStruct { original_name: Some(self.name.to_string()), def: self.gen_ts_class(), js_mod: self.js_mod.to_owned(), - js_doc: js_doc_from_comments(&self.comments), + js_doc, }) } } @@ -67,17 +79,17 @@ impl ToTypeDef for NapiImpl { "void".to_owned() }; Some(TypeDef { - kind: "impl".to_owned(), + kind: "extends".to_owned(), name: self.js_name.to_owned(), original_name: None, def: format!( - "[Symbol.iterator](): Iterator<{}, {}, {}>", + "Iterator<{}, {}, {}>", ty_to_ts_type(output_type, false, true, false).0, return_type, next_type, ), js_mod: self.js_mod.to_owned(), - js_doc: "".to_string(), + js_doc: JSDoc::new::, String>(Vec::default()), }) } else { Some(TypeDef { @@ -93,7 +105,7 @@ impl ToTypeDef for NapiImpl { } else { Some(format!( "{}{}", - js_doc_from_comments(&f.comments), + JSDoc::new(&f.comments), f.to_type_def() .map_or(String::default(), |type_def| type_def.def) )) @@ -102,7 +114,7 @@ impl ToTypeDef for NapiImpl { .collect::>() .join("\\n"), js_mod: self.js_mod.to_owned(), - js_doc: "".to_string(), + js_doc: JSDoc::new::, String>(Vec::default()), }) } } @@ -117,7 +129,7 @@ impl NapiStruct { let mut field_str = String::from(""); if !f.comments.is_empty() { - field_str.push_str(&js_doc_from_comments(&f.comments)) + field_str.push_str(&format!("{}", JSDoc::new(&f.comments))) } if !f.setter { diff --git a/crates/backend/src/typegen/type.rs b/crates/backend/src/typegen/type.rs index 48808f88..74bc4c12 100644 --- a/crates/backend/src/typegen/type.rs +++ b/crates/backend/src/typegen/type.rs @@ -1,6 +1,10 @@ use super::{ToTypeDef, TypeDef}; -use crate::{js_doc_from_comments, ty_to_ts_type, typegen::add_alias, NapiType}; +use crate::{ + ty_to_ts_type, + typegen::{add_alias, JSDoc}, + NapiType, +}; impl ToTypeDef for NapiType { fn to_type_def(&self) -> Option { @@ -16,7 +20,7 @@ impl ToTypeDef for NapiType { original_name: Some(self.name.to_string()), def: ty_to_ts_type(&self.value, false, false, false).0, js_mod: self.js_mod.to_owned(), - js_doc: js_doc_from_comments(&self.comments), + js_doc: JSDoc::new(&self.comments), }) } } diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index 3d60f016..908d40c5 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -1270,6 +1270,22 @@ impl ConvertToAST for syn::ItemStruct { record_struct(&rust_struct_ident, final_js_name_for_struct.clone(), opts); let namespace = opts.namespace().map(|(m, _)| m.to_owned()); let implement_iterator = opts.iterator().is_some(); + + if implement_iterator + && self + .fields + .iter() + .filter(|f| matches!(f.vis, Visibility::Public(_))) + .filter_map(|f| f.ident.clone()) + .map(|ident| ident.to_string()) + .any(|field_name| field_name == "next" || field_name == "throw" || field_name == "return") + { + bail_span!( + self, + "Generator structs cannot have public fields named `next`, `throw`, or `return`." + ); + } + let generator_struct = GENERATOR_STRUCT.get_or_init(|| Mutex::new(HashMap::new())); let mut generator_struct = generator_struct .lock() @@ -1393,6 +1409,7 @@ impl ConvertToAST for syn::ItemStruct { register_name: get_register_ident(format!("{rust_struct_ident}_struct").as_str()), comments: extract_doc_comments(&self.attrs), has_lifetime: lifetime.is_some(), + is_generator: implement_iterator, }), }) } @@ -1556,6 +1573,7 @@ impl ConvertToAST for syn::ItemEnum { object_to_js: opts.object_to_js(), }), has_lifetime: false, + is_generator: false, }), }); } diff --git a/crates/napi/src/bindgen_runtime/iterator.rs b/crates/napi/src/bindgen_runtime/iterator.rs index d28d6265..c2dec99f 100644 --- a/crates/napi/src/bindgen_runtime/iterator.rs +++ b/crates/napi/src/bindgen_runtime/iterator.rs @@ -48,50 +48,193 @@ pub unsafe fn create_iterator( let mut global = ptr::null_mut(); check_status_or_throw!( env, - unsafe { sys::napi_get_global(env, &mut global) }, + sys::napi_get_global(env, &mut global), "Get global object failed", ); + let mut symbol_object = ptr::null_mut(); check_status_or_throw!( env, - unsafe { - sys::napi_get_named_property(env, global, c"Symbol".as_ptr().cast(), &mut symbol_object) - }, + sys::napi_get_named_property(env, global, c"Symbol".as_ptr().cast(), &mut symbol_object), "Get global object failed", ); + let mut iterator_symbol = ptr::null_mut(); check_status_or_throw!( env, - unsafe { - sys::napi_get_named_property( - env, - symbol_object, - c"iterator".as_ptr().cast(), - &mut iterator_symbol, - ) - }, + sys::napi_get_named_property( + env, + symbol_object, + c"iterator".as_ptr().cast(), + &mut iterator_symbol, + ), "Get Symbol.iterator failed", ); + + let mut next_function = ptr::null_mut(); + check_status_or_throw!( + env, + sys::napi_create_function( + env, + c"next".as_ptr().cast(), + 4, + Some(generator_next::), + generator_ptr as *mut c_void, + &mut next_function, + ), + "Create next function failed" + ); + + let mut return_function = ptr::null_mut(); + check_status_or_throw!( + env, + sys::napi_create_function( + env, + c"return".as_ptr().cast(), + 6, + Some(generator_return::), + generator_ptr as *mut c_void, + &mut return_function, + ), + "Create return function failed" + ); + + let mut throw_function = ptr::null_mut(); + check_status_or_throw!( + env, + sys::napi_create_function( + env, + c"throw".as_ptr().cast(), + 5, + Some(generator_throw::), + generator_ptr as *mut c_void, + &mut throw_function, + ), + "Create throw function failed" + ); + + check_status_or_throw!( + env, + sys::napi_set_named_property(env, instance, c"next".as_ptr().cast(), next_function,), + "Set next function on Generator object failed" + ); + + check_status_or_throw!( + env, + sys::napi_set_named_property(env, instance, c"return".as_ptr().cast(), return_function), + "Set return function on Generator object failed" + ); + + check_status_or_throw!( + env, + sys::napi_set_named_property(env, instance, c"throw".as_ptr().cast(), throw_function), + "Set throw function on Generator object failed" + ); + + let mut generator_state = ptr::null_mut(); + check_status_or_throw!( + env, + sys::napi_get_boolean(env, false, &mut generator_state), + "Create generator state failed" + ); + + let properties = [sys::napi_property_descriptor { + utf8name: GENERATOR_STATE_KEY.as_ptr().cast(), + name: ptr::null_mut(), + method: None, + getter: None, + setter: None, + value: generator_state, + attributes: sys::PropertyAttributes::writable, + data: ptr::null_mut(), + }]; + + check_status_or_throw!( + env, + sys::napi_define_properties(env, instance, 1, properties.as_ptr()), + "Define properties on Generator object failed" + ); + let mut generator_function = ptr::null_mut(); check_status_or_throw!( env, - unsafe { - sys::napi_create_function( - env, - c"Iterator".as_ptr().cast(), - 8, - Some(symbol_generator::), - generator_ptr as *mut c_void, - &mut generator_function, - ) - }, + sys::napi_create_function( + env, + c"Iterator".as_ptr().cast(), + 8, + Some(symbol_generator::), + generator_ptr as *mut c_void, + &mut generator_function, + ), "Create iterator function failed", ); + check_status_or_throw!( env, - unsafe { sys::napi_set_property(env, instance, iterator_symbol, generator_function) }, + sys::napi_set_property(env, instance, iterator_symbol, generator_function), "Failed to set Symbol.iterator on class instance", ); + + let mut iterator_ctor = ptr::null_mut(); + check_status_or_throw!( + env, + sys::napi_get_named_property(env, global, c"Iterator".as_ptr().cast(), &mut iterator_ctor,), + "Get Global.Iterator failed", + ); + + let mut iterator_ctor_type = 0; + check_status_or_throw!( + env, + sys::napi_typeof(env, iterator_ctor, &mut iterator_ctor_type), + "Get Global.Iterator type failed", + ); + + if iterator_ctor_type == sys::ValueType::napi_function { + let mut iterator_proto = ptr::null_mut(); + check_status_or_throw!( + env, + sys::napi_get_named_property( + env, + iterator_ctor, + c"prototype".as_ptr().cast(), + &mut iterator_proto, + ), + "Failed to get Iterator.prototype", + ); + + let mut object_ctor = ptr::null_mut(); + check_status_or_throw!( + env, + sys::napi_get_named_property(env, global, c"Object".as_ptr().cast(), &mut object_ctor), + "Failed to get Object constructor" + ); + + let mut set_prototype_function = ptr::null_mut(); + check_status_or_throw!( + env, + sys::napi_get_named_property( + env, + object_ctor, + c"setPrototypeOf".as_ptr().cast(), + &mut set_prototype_function, + ), + "Failed to get Object.setPrototypeOf" + ); + + let mut argv = [instance, iterator_proto]; + check_status_or_throw!( + env, + sys::napi_call_function( + env, + object_ctor, + set_prototype_function, + 2, + argv.as_mut_ptr(), + ptr::null_mut(), + ), + "Failed to set prototype on object" + ); + } } #[doc(hidden)] @@ -117,122 +260,8 @@ pub unsafe extern "C" fn symbol_generator( }, "Get callback info from generator function failed" ); - let mut generator_object = ptr::null_mut(); - check_status_or_throw!( - env, - unsafe { sys::napi_create_object(env, &mut generator_object) }, - "Create Generator object failed" - ); - let mut next_function = ptr::null_mut(); - check_status_or_throw!( - env, - unsafe { - sys::napi_create_function( - env, - c"next".as_ptr().cast(), - 4, - Some(generator_next::), - generator_ptr, - &mut next_function, - ) - }, - "Create next function failed" - ); - let mut return_function = ptr::null_mut(); - check_status_or_throw!( - env, - unsafe { - sys::napi_create_function( - env, - c"return".as_ptr().cast(), - 6, - Some(generator_return::), - generator_ptr, - &mut return_function, - ) - }, - "Create next function failed" - ); - let mut throw_function = ptr::null_mut(); - check_status_or_throw!( - env, - unsafe { - sys::napi_create_function( - env, - c"throw".as_ptr().cast(), - 5, - Some(generator_throw::), - generator_ptr, - &mut throw_function, - ) - }, - "Create next function failed" - ); - check_status_or_throw!( - env, - unsafe { - sys::napi_set_named_property( - env, - generator_object, - c"next".as_ptr().cast(), - next_function, - ) - }, - "Set next function on Generator object failed" - ); - - check_status_or_throw!( - env, - unsafe { - sys::napi_set_named_property( - env, - generator_object, - c"return".as_ptr().cast(), - return_function, - ) - }, - "Set return function on Generator object failed" - ); - - check_status_or_throw!( - env, - unsafe { - sys::napi_set_named_property( - env, - generator_object, - c"throw".as_ptr().cast(), - throw_function, - ) - }, - "Set throw function on Generator object failed" - ); - - let mut generator_state = ptr::null_mut(); - check_status_or_throw!( - env, - unsafe { sys::napi_get_boolean(env, false, &mut generator_state) }, - "Create generator state failed" - ); - - let properties = [sys::napi_property_descriptor { - utf8name: GENERATOR_STATE_KEY.as_ptr().cast(), - name: ptr::null_mut(), - method: None, - getter: None, - setter: None, - value: generator_state, - attributes: sys::PropertyAttributes::writable, - data: ptr::null_mut(), - }]; - - check_status_or_throw!( - env, - unsafe { sys::napi_define_properties(env, generator_object, 1, properties.as_ptr()) }, - "Define properties on Generator object failed" - ); - - generator_object + this } extern "C" fn generator_next( diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.md b/examples/napi/__tests__/__snapshots__/values.spec.ts.md index 74bd810e..5f550d1a 100644 --- a/examples/napi/__tests__/__snapshots__/values.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/values.spec.ts.md @@ -214,21 +214,39 @@ Generated by [AVA](https://avajs.dev). constructor(name: string)␊ }␊ ␊ - export declare class Fib {␊ - [Symbol.iterator](): Iterator␊ + /**␊ + * This type extends JavaScript's \`Iterator\`, and so has the iterator helper␊ + * methods. It may extend the upcoming TypeScript \`Iterator\` class in the future.␊ + *␊ + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods␊ + * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods␊ + */␊ + export declare class Fib extends Iterator {␊ constructor()␊ }␊ ␊ - export declare class Fib2 {␊ - [Symbol.iterator](): Iterator␊ + /**␊ + * This type extends JavaScript's \`Iterator\`, and so has the iterator helper␊ + * methods. It may extend the upcoming TypeScript \`Iterator\` class in the future.␊ + *␊ + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods␊ + * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods␊ + */␊ + export declare class Fib2 extends Iterator {␊ static create(seed: number): Fib2␊ }␊ ␊ - export declare class Fib3 {␊ + /**␊ + * This type extends JavaScript's \`Iterator\`, and so has the iterator helper␊ + * methods. It may extend the upcoming TypeScript \`Iterator\` class in the future.␊ + *␊ + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods␊ + * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods␊ + */␊ + export declare class Fib3 extends Iterator {␊ current: number␊ - next: number␊ - constructor(current: number, next: number)␊ - [Symbol.iterator](): Iterator␊ + nextNum: number␊ + constructor(current: number, nextNum: number)␊ }␊ ␊ export declare class GetterSetterWithClosures {␊ diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.snap b/examples/napi/__tests__/__snapshots__/values.spec.ts.snap index 83179e04..a6b84ba6 100644 Binary files a/examples/napi/__tests__/__snapshots__/values.spec.ts.snap and b/examples/napi/__tests__/__snapshots__/values.spec.ts.snap differ diff --git a/examples/napi/__tests__/generator.spec.ts b/examples/napi/__tests__/generator.spec.ts index c7c52c94..a63d8f61 100644 --- a/examples/napi/__tests__/generator.spec.ts +++ b/examples/napi/__tests__/generator.spec.ts @@ -12,10 +12,10 @@ for (const [index, factory] of [ () => new Fib3(0, 1), ].entries()) { test(`should be able to stop a generator #${index}`, (t) => { - const fib = factory() - const gen = fib[Symbol.iterator] - t.is(typeof gen, 'function') - const iterator = gen() + let iterator = factory() + if (typeof Iterator === 'undefined') { + iterator = iterator[Symbol.iterator]() + } t.deepEqual(iterator.next(), { done: false, value: 1, @@ -37,10 +37,7 @@ for (const [index, factory] of [ }) test(`should be able to throw to generator #${index}`, (t) => { - const fib = factory() - const gen = fib[Symbol.iterator] - t.is(typeof gen, 'function') - const iterator = gen() + const iterator = factory() t.deepEqual(iterator.next(), { done: false, value: 1, @@ -58,4 +55,21 @@ for (const [index, factory] of [ done: true, }) }) + + test(`should be an Iterator and have the Iterator Helper methods #${index}`, (t) => { + if (typeof Iterator === 'undefined') { + t.pass('Iterator is not existing, skipping test') + return + } + const iterator = factory() + + t.true(Object.getPrototypeOf(iterator) === Iterator.prototype) + let arr = [ + ...iterator + .drop(3) + .filter((x: number) => x % 2 == 0) + .take(5), + ] + t.deepEqual(arr, [8, 34, 144, 610, 2584]) + }) } diff --git a/examples/napi/index.d.cts b/examples/napi/index.d.cts index e725b2f1..6a916a57 100644 --- a/examples/napi/index.d.cts +++ b/examples/napi/index.d.cts @@ -175,21 +175,39 @@ export declare class Dog { constructor(name: string) } -export declare class Fib { - [Symbol.iterator](): Iterator +/** + * This type extends JavaScript's `Iterator`, and so has the iterator helper + * methods. It may extend the upcoming TypeScript `Iterator` class in the future. + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods + * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods + */ +export declare class Fib extends Iterator { constructor() } -export declare class Fib2 { - [Symbol.iterator](): Iterator +/** + * This type extends JavaScript's `Iterator`, and so has the iterator helper + * methods. It may extend the upcoming TypeScript `Iterator` class in the future. + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods + * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods + */ +export declare class Fib2 extends Iterator { static create(seed: number): Fib2 } -export declare class Fib3 { +/** + * This type extends JavaScript's `Iterator`, and so has the iterator helper + * methods. It may extend the upcoming TypeScript `Iterator` class in the future. + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods + * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods + */ +export declare class Fib3 extends Iterator { current: number - next: number - constructor(current: number, next: number) - [Symbol.iterator](): Iterator + nextNum: number + constructor(current: number, nextNum: number) } export declare class GetterSetterWithClosures { diff --git a/examples/napi/src/generator.rs b/examples/napi/src/generator.rs index b3b0ca0e..0ed81b6f 100644 --- a/examples/napi/src/generator.rs +++ b/examples/napi/src/generator.rs @@ -84,7 +84,7 @@ impl Fib2 { #[napi(iterator, constructor)] pub struct Fib3 { pub current: u32, - pub next: u32, + pub next_num: u32, } #[napi] @@ -97,13 +97,13 @@ impl Generator for Fib3 { match value { Some(n) => { self.current = n as u32; - self.next = n as u32 + 1; + self.next_num = n as u32 + 1; } None => { - let next = self.next; + let next = self.next_num; let current = self.current; self.current = next; - self.next = current + next; + self.next_num = current + next; } }; Some(self.current) diff --git a/tsconfig.json b/tsconfig.json index e093de87..c43da4e3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,7 +22,7 @@ "resolveJsonModule": true, "importsNotUsedAsValues": "remove", "outDir": "scripts", - "lib": ["ES2022"] + "lib": ["ESNext"] }, "include": [], "references": [