dumpling 0.1.0

A fast JavaScript runtime and bundler in Rust
Documentation
use boa_engine::{Context, JsResult};

pub struct ES202XFeatures;

impl ES202XFeatures {
    /// Initialize all ES202X features in the context
    pub fn init(context: &mut Context) -> JsResult<()> {
        Self::init_optional_chaining(context)?;
        Self::init_nullish_coalescing(context)?;
        Self::init_private_class_fields(context)?;
        Self::init_array_findlast(context)?;
        Self::init_regexp_match_indices(context)?;
        Self::init_temporal_api(context)?;

        Ok(())
    }

    /// Initialize optional chaining (?.) support
    fn init_optional_chaining(_context: &mut Context) -> JsResult<()> {
        // The Boa engine already supports optional chaining syntax
        // No additional polyfills needed
        Ok(())
    }

    /// Initialize nullish coalescing (??) support
    fn init_nullish_coalescing(_context: &mut Context) -> JsResult<()> {
        // The Boa engine already supports nullish coalescing syntax
        // No additional polyfills needed
        Ok(())
    }

    /// Initialize private class fields support
    fn init_private_class_fields(_context: &mut Context) -> JsResult<()> {
        // The Boa engine already supports private class fields syntax
        // No additional polyfills needed
        Ok(())
    }

    /// Initialize Array.findLast and Array.findLastIndex
    fn init_array_findlast(context: &mut Context) -> JsResult<()> {
        // Array.prototype.findLast polyfill
        let findlast_code = r#"
        if (!Array.prototype.findLast) {
            Array.prototype.findLast = function(predicate, thisArg) {
                if (this == null) {
                    throw new TypeError('Array.prototype.findLast called on null or undefined');
                }
                if (typeof predicate !== 'function') {
                    throw new TypeError('predicate must be a function');
                }
                var list = Object(this);
                var length = list.length >>> 0;
                for (var i = length - 1; i >= 0; i--) {
                    if (i in list) {
                        var value = list[i];
                        if (predicate.call(thisArg, value, i, list)) {
                            return value;
                        }
                    }
                }
                return undefined;
            };
        }
        "#;

        context.eval(boa_engine::Source::from_bytes(findlast_code.as_bytes()))?;

        // Array.prototype.findLastIndex polyfill
        let findlastindex_code = r#"
        if (!Array.prototype.findLastIndex) {
            Array.prototype.findLastIndex = function(predicate, thisArg) {
                if (this == null) {
                    throw new TypeError('Array.prototype.findLastIndex called on null or undefined');
                }
                if (typeof predicate !== 'function') {
                    throw new TypeError('predicate must be a function');
                }
                var list = Object(this);
                var length = list.length >>> 0;
                for (var i = length - 1; i >= 0; i--) {
                    if (i in list) {
                        var value = list[i];
                        if (predicate.call(thisArg, value, i, list)) {
                            return i;
                        }
                    }
                }
                return -1;
            };
        }
        "#;

        context.eval(boa_engine::Source::from_bytes(findlastindex_code.as_bytes()))?;

        Ok(())
    }

    /// Initialize RegExp match indices (/d flag) support
    fn init_regexp_match_indices(context: &mut Context) -> JsResult<()> {
        // Polyfill for RegExp match indices
        let regexp_code = r#"
        (function() {
            // Check if the d flag is supported
            var hasIndicesSupport = (() => {
                try {
                    new RegExp('', 'd');
                    return true;
                } catch (_) {
                    return false;
                }
            })();

            if (!hasIndicesSupport) {
                // Override RegExp constructor to add d flag support
                var OriginalRegExp = window.RegExp;
                var RegExp = function(pattern, flags) {
                    var hasIndices = flags && flags.includes('d');
                    if (hasIndices) {
                        // Remove the d flag for the native RegExp
                        flags = flags.replace('d', '');
                    }
                    var regex = new OriginalRegExp(pattern, flags);
                    if (hasIndices) {
                        // Add custom handling for match indices
                        regex.hasIndices = true;
                        var originalExec = regex.exec;
                        regex.exec = function(str) {
                            var match = originalExec.call(this, str);
                            if (match && this.hasIndices) {
                                match.indices = this.getIndices(match);
                            }
                            return match;
                        };
                        regex.getIndices = function(match) {
                            var indices = [];
                            for (var i = 0; i < match.length; i++) {
                                if (match[i] !== undefined) {
                                    indices.push([match.index, match.index + match[i].length]);
                                } else {
                                    indices.push(undefined);
                                }
                            }
                            return indices;
                        };
                    }
                    return regex;
                };
                RegExp.prototype = OriginalRegExp.prototype;
                RegExp.prototype.constructor = RegExp;
                window.RegExp = RegExp;
            }
        })();
        "#;

        context.eval(boa_engine::Source::from_bytes(regexp_code.as_bytes()))?;

        Ok(())
    }

    /// Initialize Temporal API support
    fn init_temporal_api(context: &mut Context) -> JsResult<()> {
        // Simplified Temporal API polyfill
        let temporal_code = r#"
        // Simplified Temporal API implementation
        if (typeof globalThis.Temporal === 'undefined') {
            globalThis.Temporal = {};

            // Temporal.Now
            Temporal.Now = {
                // Simplified implementation - in a real polyfill, this would use timezone data
                instant: function() {
                    return {
                        epochMilliseconds: Date.now(),
                        epochNanoseconds: Date.now() * 1000000n,
                        toString: function() {
                            return new Date(this.epochMilliseconds).toISOString();
                        }
                    };
                },
                // Simplified implementation without timezone support
                timeZone: function() {
                    return {
                        toString: function() {
                            return Intl.DateTimeFormat().resolvedOptions().timeZone;
                        }
                    };
                },
                // Simplified date implementation
                plainDate: function(calendar) {
                    var now = new Date();
                    return {
                        year: now.getFullYear(),
                        month: now.getMonth() + 1,
                        day: now.getDate(),
                        calendar: calendar || 'iso8601',
                        toString: function() {
                            return `${this.year}-${this.month.toString().padStart(2, '0')}-${this.day.toString().padStart(2, '0')}`;
                        }
                    };
                },
                // Simplified time implementation
                plainTime: function() {
                    var now = new Date();
                    return {
                        hour: now.getHours(),
                        minute: now.getMinutes(),
                        second: now.getSeconds(),
                        millisecond: now.getMilliseconds(),
                        toString: function() {
                            return `${this.hour.toString().padStart(2, '0')}:${this.minute.toString().padStart(2, '0')}:${this.second.toString().padStart(2, '0')}`;
                        }
                    };
                }
            };

            // Temporal.PlainDate
            Temporal.PlainDate = function(year, month, day, calendar) {
                return {
                    year: year,
                    month: month,
                    day: day,
                    calendar: calendar || 'iso8601',
                    toString: function() {
                        return `${this.year}-${this.month.toString().padStart(2, '0')}-${this.day.toString().padStart(2, '0')}`;
                    }
                };
            };

            // Temporal.PlainTime
            Temporal.PlainTime = function(hour, minute, second, millisecond) {
                return {
                    hour: hour || 0,
                    minute: minute || 0,
                    second: second || 0,
                    millisecond: millisecond || 0,
                    toString: function() {
                        return `${this.hour.toString().padStart(2, '0')}:${this.minute.toString().padStart(2, '0')}:${this.second.toString().padStart(2, '0')}`;
                    }
                };
            };

            // Temporal.Duration
            Temporal.Duration = function(years, months, weeks, days, hours, minutes, seconds, milliseconds) {
                return {
                    years: years || 0,
                    months: months || 0,
                    weeks: weeks || 0,
                    days: days || 0,
                    hours: hours || 0,
                    minutes: minutes || 0,
                    seconds: seconds || 0,
                    milliseconds: milliseconds || 0,
                    toString: function() {
                        let result = 'P';
                        if (this.years) result += `${this.years}Y`;
                        if (this.months) result += `${this.months}M`;
                        if (this.weeks) result += `${this.weeks}W`;
                        if (this.days) result += `${this.days}D`;
                        if (this.hours || this.minutes || this.seconds) {
                            result += 'T';
                            if (this.hours) result += `${this.hours}H`;
                            if (this.minutes) result += `${this.minutes}M`;
                            if (this.seconds) result += `${this.seconds}S`;
                        }
                        return result;
                    }
                };
            };
        }
        "#;

        context.eval(boa_engine::Source::from_bytes(temporal_code.as_bytes()))?;

        Ok(())
    }
}