Compare commits

...

3 Commits

Author SHA1 Message Date
Kazem Alghasi
991e117eb5 refactor(ui): remove unused motayeb_fa_count function from front-page.php 2026-09-23 00:32:51 +03:30
Kazem Alghasi
c16648678d - install local Tailwind
- fix more problems
2026-09-23 00:32:16 +03:30
Kazem Alghasi
5d0accae73 style(css): add dedicated WooCommerce stylesheets
Introduce a new dedicated stylesheet for WooCommerce components to manage product grids, single product layouts, and cart/checkout styling. This centralizes WooCommerce-specific CSS and improves maintainability.
2026-09-20 17:13:50 +03:30
1416 changed files with 253668 additions and 1097 deletions

View File

@ -1,79 +1,278 @@
<?php
/**
* صفحه بلاگ (آرشیو پست‌ها) - مطابق با UI Kolli.html
* صفحه فروشگاه (آرشیو محصولات) - مطابق با UI Kolli.html
*
* @package Motayeb
* @version 1.0.1
*
* تغییرات نسخه ۱.۰.۱:
* - guard کامل WooCommerce (بدون fatal error اگه غیرفعال باشه)
* - رفع term_description با wp_kses_post
* - افزودن دکمه toggle فیلتر در موبایل
* - افزودن نمایش فیلترهای فعال
* - استفاده از motayeb_count_category_products (شامل زیردسته‌ها)
* - Breadcrumb Schema برای صفحات دسته‌بندی
* - aria-label روی sectionها
* - width/height + fetchpriority روی بنر
* - رفع sticky همپوشانی با هدر
* - استایل Tailwind-friendly برای pagination و ordering
* - اعتبارسنجی فیلتر قیمت
*/
get_header();
// guard: اگه ووکامرس غیرفعال باشه
if ( ! class_exists( 'WooCommerce' ) ) {
echo '<main class="pt-16 md:pt-20"><div class="max-w-7xl mx-auto px-4 py-20 text-center">';
echo '<h1 class="text-2xl font-bold mb-4">فروشگاه فعال نیست</h1>';
echo '<p class="text-cream-500">برای مشاهده محصولات، افزونه ووکامرس را فعال کنید.</p>';
echo '</div></main>';
get_footer();
return;
}
// داده‌های صفحه
$banner_img = get_option( 'motayeb_shop_banner', '' );
$banner_img = $banner_img ? $banner_img : 'https://picsum.photos/seed/forest-shop/1600/600';
$is_category = is_product_category();
$current_term = $is_category ? get_queried_object() : null;
// بررسی اینکه آیا سایدبار فیلتر داره یا خیر
$has_shop_sidebar = is_active_sidebar( 'sidebar-shop' );
?>
<!-- ===== BLOG HEADER ===== -->
<section class="relative bg-forest-500 dark:bg-forest-800 py-12 md:py-16 overflow-hidden">
<div class="absolute inset-0 opacity-10 honeycomb-bg"></div>
<div class="max-w-7xl mx-auto px-4 md:px-6 relative">
<h1 class="text-3xl md:text-4xl lg:text-5xl font-extrabold text-white text-center">مجله سلامت مطیب</h1>
<p class="text-white/70 text-center mt-3 max-w-2xl mx-auto">جدیدترین مقالات و مطالب آموزشی در زمینه سلامت، تغذیه و سبک زندگی ارگانیک</p>
<!-- ===== SHOP HEADER ===== -->
<section class="relative py-20 md:py-28 overflow-hidden" aria-label="عنوان فروشگاه">
<div class="absolute inset-0">
<img src="<?php echo esc_url( $banner_img ); ?>"
alt="<?php echo esc_attr( $is_category && $current_term ? $current_term->name : 'فروشگاه مطیب' ); ?>"
width="1600" height="600"
fetchpriority="high"
decoding="async"
class="w-full h-full object-cover">
</div>
<div class="absolute inset-0 bg-gradient-to-t from-forest-900/90 via-forest-700/70 to-forest-500/40" aria-hidden="true"></div>
<div class="relative max-w-7xl mx-auto px-4 md:px-6 text-center">
<h1 class="text-3xl md:text-4xl lg:text-5xl font-extrabold text-white drop-shadow-md">
<?php woocommerce_page_title(); ?>
</h1>
<?php if ( $is_category && $current_term && $current_term->description ) : ?>
<p class="text-white/90 text-center mt-3 max-w-2xl mx-auto drop-shadow-md text-sm md:text-base">
<?php echo wp_kses_post( wpautop( $current_term->description ) ); ?>
</p>
<?php endif; ?>
</div>
</section>
<!-- ===== BLOG CONTENT ===== -->
<section class="py-12 md:py-20 bg-cream-100 dark:bg-dark-bg">
<div class="max-w-7xl mx-auto px-4 md:px-6">
<div class="grid md:grid-cols-3 gap-6">
<?php if ( have_posts() ) : ?>
<?php while ( have_posts() ) : the_post(); ?>
<?php
$categories = get_the_category();
$cat_name = ! empty( $categories ) ? $categories[0]->name : 'سلامت';
$cat_color = 'bg-forest-50 dark:bg-forest-900/30 text-forest-500 dark:text-forest-300';
if ( $cat_name === 'دستور پخت' || $cat_name === 'غذا' ) {
$cat_color = 'bg-honey-50 dark:bg-honey-900/30 text-honey-400';
} elseif ( $cat_name === 'سبک زندگی' ) {
$cat_color = 'bg-emerald-50 dark:bg-emerald-900/30 text-emerald-500';
}
$image = wp_get_attachment_image_src( get_post_thumbnail_id(), 'medium' );
$img_src = $image ? $image[0] : 'https://picsum.photos/seed/' . get_the_ID() . '/600/400.jpg';
?>
<article class="group bg-white dark:bg-dark-card rounded-2xl overflow-hidden shadow-sm hover:shadow-xl transition-all border border-cream-200/50 dark:border-dark-border/50">
<a href="<?php the_permalink(); ?>">
<div class="overflow-hidden h-48">
<img src="<?php echo esc_url( $img_src ); ?>" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" alt="<?php the_title_attribute(); ?>">
</div>
</a>
<div class="p-5">
<div class="flex items-center gap-2 mb-3">
<span class="px-2.5 py-0.5 <?php echo esc_attr( $cat_color ); ?> text-xs rounded-full font-medium"><?php echo esc_html( $cat_name ); ?></span>
<span class="text-xs text-cream-500 dark:text-dark-muted"><?php echo get_the_date( 'j F Y' ); ?></span>
</div>
<h3 class="font-bold text-lg mb-2 group-hover:text-forest-500 dark:group-hover:text-forest-300 transition">
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</h3>
<p class="text-sm text-cream-500 dark:text-dark-muted line-clamp-2"><?php echo wp_trim_words( get_the_excerpt(), 15, '...' ); ?></p>
<a href="<?php the_permalink(); ?>" class="inline-flex items-center gap-1 mt-4 text-sm font-medium text-forest-500 dark:text-forest-300 hover:gap-2 transition-all">
<span>بیشتر بخوانید</span>
<iconify-icon icon="lucide:arrow-left" width="16"></iconify-icon>
</a>
</div>
</article>
<?php endwhile; ?>
<!-- ===== FEATURES ===== -->
<div class="relative z-10 -mt-8 lg:-mt-12 max-w-7xl mx-auto px-4 md:px-6 mb-8">
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 md:gap-4 bg-white/90 dark:bg-dark-card/90 backdrop-blur-lg p-4 md:p-5 rounded-2xl shadow-lg border border-cream-200/50 dark:border-dark-border/50">
<?php for ( $i = 1; $i <= 4; $i++ ) : ?>
<?php
$default_icons = array( 1 => 'lucide:leaf', 2 => 'lucide:truck', 3 => 'lucide:shield-check', 4 => 'lucide:package' );
$icon = get_option( 'motayeb_feature_' . $i . '_icon', $default_icons[ $i ] );
$text = get_option( 'motayeb_feature_' . $i . '_text', 'ویژگی ' . $i );
?>
<div class="flex items-center gap-3 justify-center">
<iconify-icon icon="<?php echo esc_attr( $icon ); ?>" width="22" class="text-forest-500 dark:text-forest-300 flex-shrink-0"></iconify-icon>
<span class="text-xs md:text-sm font-medium text-earth-400 dark:text-dark-text"><?php echo esc_html( $text ); ?></span>
</div>
<?php endfor; ?>
</div>
</div>
<!-- ===== Breadcrumb (برای صفحات دسته‌بندی) ===== -->
<?php if ( $is_category && $current_term ) :
// ساخت BreadcrumbList Schema
$crumbs = array(
array( 'name' => 'خانه', 'url' => home_url( '/' ) ),
array( 'name' => 'فروشگاه', 'url' => get_permalink( wc_get_page_id( 'shop' ) ) ),
);
// افزودن دسته‌بندی‌های والد
$parents = get_ancestors( $current_term->term_id, 'product_cat' );
$parents = array_reverse( $parents );
foreach ( $parents as $parent_id ) {
$parent = get_term( $parent_id, 'product_cat' );
if ( $parent && ! is_wp_error( $parent ) ) {
$crumbs[] = array( 'name' => $parent->name, 'url' => get_term_link( $parent ) );
}
}
$crumbs[] = array( 'name' => $current_term->name, 'url' => get_term_link( $current_term ) );
$breadcrumb_schema = array(
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => array(),
);
foreach ( $crumbs as $index => $crumb ) {
$breadcrumb_schema['itemListElement'][] = array(
'@type' => 'ListItem',
'position' => $index + 1,
'name' => $crumb['name'],
'item' => $crumb['url'],
);
}
?>
<nav class="max-w-7xl mx-auto px-4 md:px-6 mb-4 text-xs text-cream-500 dark:text-dark-muted flex items-center gap-2 flex-wrap" aria-label="مسیر صفحه">
<?php foreach ( $crumbs as $index => $crumb ) :
$is_last = ( $index === count( $crumbs ) - 1 );
?>
<?php if ( ! $is_last ) : ?>
<a href="<?php echo esc_url( $crumb['url'] ); ?>" class="hover:text-forest-500 dark:hover:text-forest-300 transition"><?php echo esc_html( $crumb['name'] ); ?></a>
<iconify-icon icon="lucide:chevron-left" width="14"></iconify-icon>
<?php else : ?>
<p class="text-center text-cream-500 dark:text-dark-muted col-span-3">هنوز مطلبی منتشر نشده است.</p>
<span class="text-earth-400 dark:text-dark-text font-medium"><?php echo esc_html( $crumb['name'] ); ?></span>
<?php endif; ?>
</div>
<?php endforeach; ?>
</nav>
<script type="application/ld+json"><?php echo wp_json_encode( $breadcrumb_schema ); ?></script>
<?php endif; ?>
<!-- Pagination -->
<div class="mt-10">
<?php the_posts_pagination( array(
'mid_size' => 2,
'prev_text' => '<iconify-icon icon="lucide:chevron-right" width="18"></iconify-icon>',
'next_text' => '<iconify-icon icon="lucide:chevron-left" width="18"></iconify-icon>',
'screen_reader_text' => ' ',
'class' => 'flex justify-center gap-2',
) ); ?>
<!-- ===== SHOP CONTENT ===== -->
<section class="pb-8 md:pb-12 bg-cream-100 dark:bg-dark-bg" aria-label="محصولات فروشگاه">
<div class="max-w-7xl mx-auto px-4 md:px-6">
<!-- دکمه toggle فیلتر در موبایل -->
<button id="filter-toggle"
class="lg:hidden w-full mb-4 flex items-center justify-center gap-2 py-3 bg-white dark:bg-dark-card rounded-xl border border-cream-200 dark:border-dark-border text-sm font-medium hover:bg-cream-50 dark:hover:bg-dark-bg transition"
aria-label="نمایش فیلترها"
aria-expanded="false"
aria-controls="shop-sidebar">
<iconify-icon icon="lucide:sliders-horizontal" width="18"></iconify-icon>
<span>فیلترها</span>
<iconify-icon icon="lucide:chevron-down" width="16" class="transition-transform" id="filter-toggle-icon"></iconify-icon>
</button>
<div class="flex flex-col lg:flex-row gap-8">
<!-- Sidebar -->
<aside id="shop-sidebar"
class="lg:w-1/4 xl:w-1/5 flex-shrink-0 <?php echo $has_shop_sidebar ? '' : 'hidden lg:block'; ?> lg:!block"
aria-label="فیلترهای فروشگاه">
<div class="bg-white dark:bg-dark-card rounded-2xl p-5 border border-cream-200/50 dark:border-dark-border/50 lg:sticky lg:top-28">
<?php if ( $has_shop_sidebar ) : ?>
<?php dynamic_sidebar( 'sidebar-shop' ); ?>
<?php else : ?>
<!-- ===== پیش‌فرض: دسته‌بندی‌ها ===== -->
<div class="mb-6">
<h3 class="font-bold text-sm mb-3 text-earth-400 dark:text-dark-text">دسته‌بندی محصولات</h3>
<ul class="space-y-2 text-sm">
<?php
$product_categories = get_terms( array(
'taxonomy' => 'product_cat',
'hide_empty' => true,
'parent' => 0,
) );
if ( ! empty( $product_categories ) && ! is_wp_error( $product_categories ) ) {
foreach ( $product_categories as $cat ) {
$count = motayeb_count_category_products( $cat->term_id );
$is_active = ( $is_category && $current_term && $current_term->term_id === $cat->term_id );
echo '<li><a href="' . esc_url( get_term_link( $cat ) ) . '" class="text-cream-500 dark:text-dark-muted hover:text-forest-500 dark:hover:text-forest-300 transition flex items-center justify-between py-1.5 px-2 rounded-lg ' . ( $is_active ? 'bg-forest-50 dark:bg-forest-900/30 text-forest-500 dark:text-forest-300 font-medium' : '' ) . '">';
echo '<span>' . esc_html( $cat->name ) . '</span>';
echo '<span class="text-xs bg-cream-100 dark:bg-dark-bg px-2 py-0.5 rounded-full">' . esc_html( motayeb_fa_count( $count ) ) . '</span>';
echo '</a></li>';
}
} else {
echo '<li class="text-cream-400 dark:text-dark-muted text-xs">هیچ دسته‌بندی وجود ندارد.</li>';
}
?>
</ul>
</div>
<!-- ===== فیلتر قیمت ===== -->
<div>
<h3 class="font-bold text-sm mb-3 text-earth-400 dark:text-dark-text">فیلتر قیمت</h3>
<form method="get" action="<?php echo esc_url( wc_get_page_permalink( 'shop' ) ); ?>">
<div class="flex items-center gap-2 mb-3">
<input type="number" name="min_price" placeholder="حداقل" min="0"
value="<?php echo esc_attr( isset( $_GET['min_price'] ) ? intval( $_GET['min_price'] ) : '' ); ?>"
class="w-1/2 px-3 py-2 bg-cream-50 dark:bg-dark-bg border border-cream-200 dark:border-dark-border rounded-xl text-sm outline-none focus:border-forest-500 dark:focus:border-forest-300 transition placeholder:text-cream-400 dark:placeholder:text-dark-muted">
<span class="text-cream-400 dark:text-dark-muted">تا</span>
<input type="number" name="max_price" placeholder="حداکثر" min="0"
value="<?php echo esc_attr( isset( $_GET['max_price'] ) ? intval( $_GET['max_price'] ) : '' ); ?>"
class="w-1/2 px-3 py-2 bg-cream-50 dark:bg-dark-bg border border-cream-200 dark:border-dark-border rounded-xl text-sm outline-none focus:border-forest-500 dark:focus:border-forest-300 transition placeholder:text-cream-400 dark:placeholder:text-dark-muted">
</div>
<button type="submit" class="w-full py-2 bg-forest-500 text-white rounded-xl text-sm font-medium hover:bg-forest-600 transition">اعمال فیلتر</button>
</form>
</div>
<?php endif; ?>
</div>
</aside>
<!-- Product Grid -->
<div class="lg:w-3/4 xl:w-4/5">
<!-- Toolbar -->
<div class="flex flex-wrap items-center justify-between gap-4 mb-6 bg-white dark:bg-dark-card rounded-2xl p-4 border border-cream-200/50 dark:border-dark-border/50 motayeb-shop-toolbar">
<div class="flex items-center gap-3 text-sm text-cream-500 dark:text-dark-muted">
<span class="motayeb-result-count"><?php woocommerce_result_count(); ?></span>
</div>
<div class="flex items-center gap-3 motayeb-ordering">
<?php woocommerce_catalog_ordering(); ?>
</div>
</div>
<!-- نمایش فیلترهای فعال -->
<?php
// نمایش فیلترهای فعال (اگه ووکامرس ساپورت کنه)
if ( function_exists( 'woocommerce_output_active_filters' ) ) {
echo '<div class="mb-4 motayeb-active-filters">';
the_widget( 'WC_Widget_Price_Filter' );
echo '</div>';
}
?>
<!-- Products -->
<?php if ( woocommerce_product_loop() ) : ?>
<div class="grid grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6 motayeb-products-grid">
<?php while ( have_posts() ) : the_post(); ?>
<?php wc_get_template_part( 'content', 'product' ); ?>
<?php endwhile; ?>
</div>
<div class="mt-10 motayeb-pagination">
<?php woocommerce_pagination(); ?>
</div>
<?php else : ?>
<div class="text-center py-16">
<iconify-icon icon="lucide:package-x" width="64" class="text-cream-400 dark:text-dark-muted mx-auto mb-4"></iconify-icon>
<p class="text-cream-500 dark:text-dark-muted mb-4">محصولی یافت نشد.</p>
<a href="<?php echo esc_url( get_permalink( wc_get_page_id( 'shop' ) ) ); ?>" class="inline-flex items-center gap-2 px-6 py-2.5 bg-forest-500 text-white rounded-xl text-sm hover:bg-forest-600 transition">
<iconify-icon icon="lucide:rotate-ccw" width="16"></iconify-icon>
<span>نمایش همه محصولات</span>
</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
</section>
<script>
// toggle فیلتر در موبایل
(function() {
var btn = document.getElementById('filter-toggle');
var sidebar = document.getElementById('shop-sidebar');
var icon = document.getElementById('filter-toggle-icon');
if (!btn || !sidebar) return;
btn.addEventListener('click', function() {
var isHidden = sidebar.classList.contains('hidden');
if (isHidden) {
sidebar.classList.remove('hidden');
btn.setAttribute('aria-expanded', 'true');
if (icon) icon.style.transform = 'rotate(180deg)';
} else {
sidebar.classList.add('hidden');
btn.setAttribute('aria-expanded', 'false');
if (icon) icon.style.transform = '';
}
});
// روی دسکتاپ، همیشه visible
window.addEventListener('resize', function() {
if (window.innerWidth >= 1024) {
sidebar.classList.remove('hidden');
}
});
})();
</script>
<?php
get_footer();

2976
assets/css/tailwind.css Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,76 +1,153 @@
/**
* اسکریپت اصلی تم مطیب
*
* @package Motayeb
* @version 1.0.1
*
* تغییرات نسخه ۱.۰.۱:
* - persist شدن سبد خرید و علاقهمندیها در localStorage
* - persist شدن دارک مود در کوکی (هماهنگ با PHP body_class)
* - جلوگیری از XSS با escapeHtml روی همه ورودیهای داینامیک
* - guard کردن motayeb_ajax
* - بهبود مدیریت خطای fetch
* - رفع toggleWishlist و setRating
* - استفاده از کلاسهای Tailwind در renderStars
*/
// ============================================
// داده‌های محصولات (برای جستجو و سبد خرید)
// Guard متغیرهای سراسری
// ============================================
let productsData = [];
let cart = [];
const MOTAYEB_AJAX = (typeof motayeb_ajax !== 'undefined') ? motayeb_ajax : {
ajax_url: '/wp-admin/admin-ajax.php',
nonce: '',
home_url: '/',
checkout_url: '/checkout/',
cart_url: '/cart/'
};
// ============================================
// مدیریت State با localStorage
// ============================================
let cart = loadFromStorage('motayeb_cart', []);
let wishlist = loadFromStorage('motayeb_wishlist', []);
let selectedWeightPrice = 295000;
function loadFromStorage(key, fallback) {
try {
const saved = localStorage.getItem(key);
return saved ? JSON.parse(saved) : fallback;
} catch (e) {
return fallback;
}
}
function saveToStorage(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (e) {
// localStorage ممکنه private mode باشه — نادیده می‌گیریم
}
}
// ============================================
// ابزارهای کمکی
// ============================================
/**
* Escape کردن HTML برای جلوگیری از XSS
*/
function escapeHtml(str) {
if (str === null || str === undefined) return '';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
/**
* فرمت قیمت اعداد فارسی + نمایش رایگان برای قیمت صفر
*/
function formatPrice(n) {
n = Number(n) || 0;
if (n === 0) return 'رایگان';
return n.toLocaleString('fa-IR');
}
/**
* درخواست Ajax با مدیریت خطای کامل
*/
async function motayebFetch(action, params = {}) {
const url = new URL(MOTAYEB_AJAX.ajax_url, window.location.origin);
url.searchParams.set('action', action);
url.searchParams.set('nonce', MOTAYEB_AJAX.nonce);
Object.keys(params).forEach(k => url.searchParams.set(k, params[k]));
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
const data = await response.json();
return data;
}
// ============================================
// توابع سبد خرید
// ============================================
function quickAddToCart(id) {
// اگر محصول در دیتا نبود، از طریق Ajax واکشی کن
fetch(motayeb_ajax.ajax_url + '?action=get_product_data&id=' + id + '&nonce=' + motayeb_ajax.nonce)
.then(response => response.json())
.then(data => {
if (data.success) {
const p = data.data;
const existing = cart.find(x => x.id === p.id);
if (existing) { existing.qty++; }
else { cart.push({ ...p, qty: 1 }); }
updateCartUI();
showToast(p.name + ' به سبد خرید اضافه شد', 'success');
async function quickAddToCart(id) {
try {
const data = await motayebFetch('get_product_data', { id: id });
if (data.success) {
const p = data.data;
const existing = cart.find(x => x.id === p.id);
if (existing) {
existing.qty++;
} else {
cart.push({ ...p, qty: 1 });
}
})
.catch(() => {
// اگر Ajax کار نکرد، از داده‌های mock استفاده کن
const mockProduct = {
id: id,
name: 'محصول شماره ' + id,
price: 100000,
img: 'default-product'
};
const existing = cart.find(x => x.id === mockProduct.id);
if (existing) { existing.qty++; }
else { cart.push({ ...mockProduct, qty: 1 }); }
saveToStorage('motayeb_cart', cart);
updateCartUI();
showToast(mockProduct.name + ' به سبد خرید اضافه شد', 'success');
});
showToast(escapeHtml(p.name) + ' به سبد خرید اضافه شد', 'success');
} else {
showToast('محصول یافت نشد', 'error');
}
} catch (err) {
console.error('quickAddToCart:', err);
showToast('خطا در ارتباط با سرور', 'error');
}
}
function addToCartDetail(productId) {
const qty = parseInt(document.getElementById('product-qty')?.textContent || 1);
// از طریق Ajax محصول رو واکشی کن
fetch(motayeb_ajax.ajax_url + '?action=get_product_data&id=' + productId + '&nonce=' + motayeb_ajax.nonce)
.then(response => response.json())
.then(data => {
if (data.success) {
const p = data.data;
const existing = cart.find(x => x.id === p.id);
if (existing) { existing.qty += qty; }
else { cart.push({ ...p, qty: qty }); }
updateCartUI();
showToast(p.name + ' به سبد خرید اضافه شد', 'success');
async function addToCartDetail(productId) {
const qtyEl = document.getElementById('product-qty');
const qty = parseInt(qtyEl ? qtyEl.textContent : '1') || 1;
try {
const data = await motayebFetch('get_product_data', { id: productId });
if (data.success) {
const p = data.data;
const existing = cart.find(x => x.id === p.id);
if (existing) {
existing.qty += qty;
} else {
cart.push({ ...p, qty: qty });
}
})
.catch(() => {
// Mock
const mockProduct = {
id: productId,
name: 'محصول شماره ' + productId,
price: 100000,
img: 'default-product'
};
const existing = cart.find(x => x.id === mockProduct.id);
if (existing) { existing.qty += qty; }
else { cart.push({ ...mockProduct, qty: qty }); }
saveToStorage('motayeb_cart', cart);
updateCartUI();
showToast(mockProduct.name + ' به سبد خرید اضافه شد', 'success');
});
showToast(escapeHtml(p.name) + ' به سبد خرید اضافه شد', 'success');
} else {
showToast('محصول یافت نشد', 'error');
}
} catch (err) {
console.error('addToCartDetail:', err);
showToast('خطا در ارتباط با سرور', 'error');
}
}
function removeFromCart(id) {
cart = cart.filter(x => x.id !== id);
saveToStorage('motayeb_cart', cart);
updateCartUI();
}
@ -78,28 +155,35 @@ function updateCartQty(id, delta) {
const item = cart.find(x => x.id === id);
if (!item) return;
item.qty += delta;
if (item.qty <= 0) { removeFromCart(id); return; }
if (item.qty <= 0) {
removeFromCart(id);
return;
}
saveToStorage('motayeb_cart', cart);
updateCartUI();
}
function updateCartUI() {
const count = cart.reduce((s, i) => s + i.qty, 0);
const total = cart.reduce((s, i) => s + i.price * i.qty, 0);
const total = cart.reduce((s, i) => s + (Number(i.price) || 0) * i.qty, 0);
// Badge
// Badge روی آیکون سبد
['cart-count', 'mobile-cart-count', 'cart-drawer-count'].forEach(id => {
const el = document.getElementById(id);
if (el) {
if (id === 'cart-drawer-count') {
if (!el) return;
if (id === 'cart-drawer-count') {
el.textContent = count;
} else {
if (count > 0) {
el.textContent = count;
el.classList.remove('hidden');
} else {
if (count > 0) { el.textContent = count; el.classList.remove('hidden'); }
else { el.classList.add('hidden'); }
el.classList.add('hidden');
}
}
});
// Drawer items
// محتویات drawer
const listEl = document.getElementById('cart-items-list');
const emptyEl = document.getElementById('cart-empty');
const footerEl = document.getElementById('cart-drawer-footer');
@ -114,30 +198,30 @@ function updateCartUI() {
listEl.classList.remove('hidden');
if (emptyEl) emptyEl.classList.add('hidden');
if (footerEl) footerEl.classList.remove('hidden');
const totalEl = document.getElementById('cart-drawer-total');
if (totalEl) totalEl.textContent = formatPrice(total) + ' تومان';
listEl.innerHTML = cart.map(i => `
<div class="flex gap-3 bg-cream-50 dark:bg-dark-bg rounded-xl p-3">
<img src="https://picsum.photos/seed/${i.img || 'default'}/100/100.jpg" class="w-16 h-16 rounded-lg object-cover flex-shrink-0">
<img src="${escapeHtml(i.img && i.img !== 'default-product' ? i.img : 'https://picsum.photos/seed/' + i.id + '/100/100.jpg')}" alt="${escapeHtml(i.name)}" class="w-16 h-16 rounded-lg object-cover flex-shrink-0">
<div class="flex-1 min-w-0">
<h4 class="font-bold text-sm line-clamp-1">${i.name}</h4>
<h4 class="font-bold text-sm line-clamp-1">${escapeHtml(i.name)}</h4>
<div class="text-xs text-cream-500 dark:text-dark-muted mt-0.5">${formatPrice(i.price)} تومان</div>
<div class="flex items-center justify-between mt-2">
<div class="flex items-center border border-cream-200 dark:border-dark-border rounded-lg overflow-hidden">
<button onclick="updateCartQty(${i.id},-1)" class="w-7 h-7 flex items-center justify-center hover:bg-cream-200 dark:hover:bg-dark-card transition text-xs"><iconify-icon icon="lucide:minus" width="12"></iconify-icon></button>
<button onclick="updateCartQty(${i.id},-1)" class="w-7 h-7 flex items-center justify-center hover:bg-cream-200 dark:hover:bg-dark-card transition text-xs" aria-label="کاهش"><iconify-icon icon="lucide:minus" width="12"></iconify-icon></button>
<span class="w-8 h-7 flex items-center justify-center text-xs font-bold border-x border-cream-200 dark:border-dark-border">${i.qty}</span>
<button onclick="updateCartQty(${i.id},1)" class="w-7 h-7 flex items-center justify-center hover:bg-cream-200 dark:hover:bg-dark-card transition text-xs"><iconify-icon icon="lucide:plus" width="12"></iconify-icon></button>
<button onclick="updateCartQty(${i.id},1)" class="w-7 h-7 flex items-center justify-center hover:bg-cream-200 dark:hover:bg-dark-card transition text-xs" aria-label="افزایش"><iconify-icon icon="lucide:plus" width="12"></iconify-icon></button>
</div>
<button onclick="removeFromCart(${i.id})" class="text-red-400 hover:text-red-500 transition"><iconify-icon icon="lucide:trash-2" width="16"></iconify-icon></button>
<button onclick="removeFromCart(${i.id})" class="text-red-400 hover:text-red-500 transition" aria-label="حذف"><iconify-icon icon="lucide:trash-2" width="16"></iconify-icon></button>
</div>
</div>
</div>
`).join('');
}
// Checkout
// صفحه checkout
const checkoutItems = document.getElementById('checkout-items');
if (checkoutItems) {
if (cart.length === 0) {
@ -145,12 +229,12 @@ function updateCartUI() {
} else {
checkoutItems.innerHTML = cart.map(i => `
<div class="flex items-center gap-3">
<img src="https://picsum.photos/seed/${i.img || 'default'}/60/60.jpg" class="w-12 h-12 rounded-lg object-cover">
<img src="${escapeHtml(i.img && i.img !== 'default-product' ? i.img : 'https://picsum.photos/seed/' + i.id + '/60/60.jpg')}" alt="${escapeHtml(i.name)}" class="w-12 h-12 rounded-lg object-cover">
<div class="flex-1 min-w-0">
<div class="text-sm font-medium line-clamp-1">${i.name}</div>
<div class="text-sm font-medium line-clamp-1">${escapeHtml(i.name)}</div>
<div class="text-xs text-cream-500 dark:text-dark-muted">${i.qty} عدد</div>
</div>
<div class="text-sm font-bold whitespace-nowrap">${formatPrice(i.price * i.qty)}</div>
<div class="text-sm font-bold whitespace-nowrap">${formatPrice(Number(i.price) * i.qty)}</div>
</div>
`).join('');
}
@ -161,22 +245,18 @@ function updateCartUI() {
}
}
function formatPrice(n) {
return n.toLocaleString('fa-IR');
}
// ============================================
// توابع UI — جستجو، سبد، منو
// ============================================
// ============================================
// توابع UI
// ============================================
function openSearch() {
const overlay = document.getElementById('search-overlay');
if (overlay) {
overlay.classList.remove('hidden');
setTimeout(() => {
const input = document.getElementById('search-input');
if (input) input.focus();
}, 100);
}
if (!overlay) return;
overlay.classList.remove('hidden');
setTimeout(() => {
const input = document.getElementById('search-input');
if (input) input.focus();
}, 100);
}
function closeSearch() {
@ -210,29 +290,46 @@ function closeMobileMenu() {
if (menu) menu.classList.add('hidden');
}
function toggleDark() {
document.documentElement.classList.toggle('dark');
}
function toggleLang() {
showToast('نسخه انگلیسی به زودی اضافه می‌شود', 'info');
}
function goToCheckout() {
closeCart();
// هدایت به صفحه تسویه حساب ووکامرس
if (typeof motayeb_ajax !== 'undefined' && motayeb_ajax.checkout_url) {
window.location.href = motayeb_ajax.checkout_url;
if (MOTAYEB_AJAX.checkout_url) {
window.location.href = MOTAYEB_AJAX.checkout_url;
} else {
// Fallback: اگر متغیر وجود نداشت، به صفحه سبد خرید برود
window.location.href = '/cart/';
window.location.href = '/checkout/';
}
}
// ============================================
// جستجو
// دارک مود — هماهنگ با PHP body_class
// ============================================
function handleSearch(val) {
/**
* تغییر حالت تاریک.
* - کلاس dark روی <html> (برای Tailwind dark:)
* - کلاس dark-mode روی <body> (برای CSS variables قدیمی)
* - ذخیره در کوکی (برای خواندن سمت سرور در functions.php)
*/
function toggleDark() {
const html = document.documentElement;
const body = document.body;
const isDark = html.classList.toggle('dark');
body.classList.toggle('dark-mode', isDark);
// ذخیره در کوکی — ۱ سال اعتبار
const value = isDark ? 'on' : 'off';
document.cookie = 'motayeb_darkmode=' + value +
';path=/;max-age=31536000;SameSite=Lax';
}
// ============================================
// جستجوی زنده
// ============================================
async function handleSearch(val) {
const results = document.getElementById('search-results');
const suggestions = document.getElementById('search-suggestions');
if (!results) return;
@ -244,71 +341,63 @@ function handleSearch(val) {
}
if (suggestions) suggestions.classList.add('hidden');
// ارسال درخواست Ajax برای جستجو
fetch(motayeb_ajax.ajax_url + '?action=search_products&term=' + encodeURIComponent(val) + '&nonce=' + motayeb_ajax.nonce)
.then(response => response.json())
.then(data => {
if (data.success && data.data.length > 0) {
results.innerHTML = data.data.map(p => `
<button onclick="closeSearch()" class="flex items-center gap-3 w-full p-3 rounded-xl hover:bg-cream-50 dark:hover:bg-dark-bg transition text-right">
<img src="${p.image || 'https://picsum.photos/seed/' + p.id + '/60/60.jpg'}" class="w-12 h-12 rounded-lg object-cover">
<div class="flex-1">
<div class="font-bold text-sm">${p.name}</div>
<div class="text-xs text-cream-500 dark:text-dark-muted">${p.category || 'محصول'}</div>
try {
const data = await motayebFetch('search_products', { term: val });
if (data.success && data.data.length > 0) {
results.innerHTML = data.data.map(p => {
const imgSrc = p.image || p.img || ('https://picsum.photos/seed/' + p.id + '/60/60.jpg');
const permalink = p.permalink || '#';
return `
<a href="${escapeHtml(permalink)}" onclick="closeSearch()" class="flex items-center gap-3 w-full p-3 rounded-xl hover:bg-cream-50 dark:hover:bg-dark-bg transition text-right">
<img src="${escapeHtml(imgSrc)}" alt="${escapeHtml(p.name)}" class="w-12 h-12 rounded-lg object-cover">
<div class="flex-1 min-w-0">
<div class="font-bold text-sm line-clamp-1">${escapeHtml(p.name)}</div>
<div class="text-xs text-cream-500 dark:text-dark-muted">${escapeHtml(p.category || 'محصول')}</div>
</div>
<div class="text-sm font-bold text-forest-500 dark:text-forest-300">${formatPrice(p.price)}</div>
</button>
`).join('');
} else {
results.innerHTML = '<p class="text-sm text-cream-500 dark:text-dark-muted text-center py-6">محصولی یافت نشد</p>';
}
})
.catch(() => {
// Mock results
results.innerHTML = '<p class="text-sm text-cream-500 dark:text-dark-muted text-center py-6">خطا در جستجو. لطفاً دوباره تلاش کنید.</p>';
});
<div class="text-sm font-bold text-forest-500 dark:text-forest-300 whitespace-nowrap">${formatPrice(p.price)}</div>
</a>
`;
}).join('');
} else {
results.innerHTML = '<p class="text-sm text-cream-500 dark:text-dark-muted text-center py-6">محصولی یافت نشد</p>';
}
} catch (err) {
console.error('handleSearch:', err);
results.innerHTML = '<p class="text-sm text-cream-500 dark:text-dark-muted text-center py-6">خطا در جستجو. لطفاً دوباره تلاش کنید.</p>';
}
}
//زوم تصویر
// ============================================
// گالری محصول — زوم و تغییر تصویر
// ============================================
function changeImage(src, btn) {
const mainImg = document.getElementById('main-product-img');
if (!mainImg) return;
// 1. تغییر تصویر اصلی
mainImg.src = src;
// 2. آپدیت ویژگی‌های ضروری برای زوم و پاپ‌آپ ووکامرس
mainImg.setAttribute('data-large_image', src);
mainImg.setAttribute('data-src', src);
// 3. مهمترین بخش برای پاپ‌آپ: آپدیت لینک والد (<a> اطراف عکس)
// ووکامرس برای باز کردن پاپ‌آپ از روی href این لینک استفاده می‌کند
const parentLink = mainImg.closest('a');
if (parentLink) {
parentLink.href = src;
}
// 4. بروزرسانی کلاس فعال روی thumbnails
const parentLink = mainImg.closest('a');
if (parentLink) parentLink.href = src;
document.querySelectorAll('.gallery-thumb').forEach(t => t.classList.remove('active'));
if (btn) btn.classList.add('active');
// 5. بازسازی مجدد Zoom (با متد مقاوم‌تر)
// بازسازی زوم ووکامرس (اگه jQuery لود شده باشه)
if (typeof jQuery !== 'undefined' && typeof jQuery.fn.zoom !== 'undefined') {
const $mainImg = jQuery(mainImg);
// والد بلافصل عکس را هدف قرار میدهیم (معمولاً یک <div> یا <a> است)
const $wrapper = $mainImg.parent();
// اگر والد قبلاً زوم داشته باشد، آن را نابود کن
const $wrapper = $mainImg.parent();
$wrapper.trigger('zoom.destroy');
// زوم جدید را روی والد اعمال کن
$wrapper.zoom({
url: src,
touch: false
});
$wrapper.zoom({ url: src, touch: false });
}
}
// ============================================
// صفحه محصول — انتخاب وزن و تعداد
// ============================================
function selectWeight(btn, price) {
document.querySelectorAll('.weight-btn').forEach(b => {
b.classList.remove('active', 'border-forest-500', 'dark:border-forest-300', 'bg-forest-50', 'dark:bg-forest-900/30', 'text-forest-500', 'dark:text-forest-300');
@ -345,36 +434,60 @@ function switchTab(tabId, btn) {
}
// ============================================
// علاقه‌مندی‌ها
// علاقه‌مندی‌ها — با persist در localStorage
// ============================================
function toggleWishlist(btn) {
function toggleWishlist(btn, productId) {
if (!btn) return;
const icon = btn.querySelector('iconify-icon');
if (!icon) return;
const currentIcon = icon.getAttribute('icon');
if (currentIcon === 'lucide:heart') {
productId = productId || parseInt(btn.getAttribute('data-product-id')) || 0;
const index = wishlist.indexOf(productId);
if (index === -1) {
// افزودن به علاقه‌مندی‌ها
wishlist.push(productId);
icon.setAttribute('icon', 'lucide:heart');
icon.style.color = '#ef4444';
icon.style.fill = '#ef4444';
showToast('به علاقه‌مندی‌ها اضافه شد', 'info');
showToast('به علاقه‌مندی‌ها اضافه شد', 'success');
} else {
// حذف از علاقه‌مندی‌ها
wishlist.splice(index, 1);
icon.setAttribute('icon', 'lucide:heart');
icon.style.color = '';
icon.style.fill = '';
showToast('از علاقه‌مندی‌ها حذف شد', 'info');
}
saveToStorage('motayeb_wishlist', wishlist);
}
// ============================================
// Toast
// Toast نوتیفیکیشن
// ============================================
function showToast(msg, type = 'info') {
function showToast(msg, type) {
type = type || 'info';
const container = document.getElementById('toast-container');
if (!container) return;
const colors = { success: 'bg-forest-500', info: 'bg-earth-400', error: 'bg-red-500' };
const icons = { success: 'lucide:check-circle', info: 'lucide:info', error: 'lucide:alert-circle' };
const colors = {
success: 'bg-forest-500',
info: 'bg-earth-400',
error: 'bg-red-500'
};
const icons = {
success: 'lucide:check-circle',
info: 'lucide:info',
error: 'lucide:alert-circle'
};
const toast = document.createElement('div');
toast.className = `toast flex items-center gap-3 px-5 py-3 ${colors[type] || 'bg-earth-400'} text-white rounded-2xl shadow-xl text-sm font-medium`;
toast.innerHTML = `<iconify-icon icon="${icons[type] || 'lucide:info'}" width="20"></iconify-icon><span>${msg}</span>`;
toast.className = 'toast flex items-center gap-3 px-5 py-3 ' + (colors[type] || 'bg-earth-400') + ' text-white rounded-2xl shadow-xl text-sm font-medium';
toast.innerHTML = '<iconify-icon icon="' + (icons[type] || 'lucide:info') + '" width="20"></iconify-icon><span>' + escapeHtml(msg) + '</span>';
container.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transition = 'opacity 0.3s';
@ -385,30 +498,92 @@ function showToast(msg, type = 'info') {
// ============================================
// فرم‌ها
// ============================================
function handleNewsletter(e) {
e.preventDefault();
const email = document.getElementById('newsletter-email');
if (email) {
if (email && email.value) {
showToast(email.value + ' با موفقیت ثبت شد!', 'success');
email.value = '';
}
return false;
}
function handleContact(e) {
e.preventDefault();
showToast('پیام شما با موفقیت ارسال شد. به زودی پاسخ می‌دهیم.', 'success');
e.target.reset();
return false;
}
// ============================================
// سیستم امتیازدهی ستاره‌ای
// ============================================
let currentRating = 0;
/**
* تعیین امتیاز با کلیک روی ستاره
*/
function setRating(rating) {
currentRating = rating;
renderStars(rating);
// تنظیم فیلد مخفی برای ارسال با فرم
const hiddenInput = document.getElementById('rating-input');
if (hiddenInput) hiddenInput.value = rating;
}
/**
* نمایش ستارهها با امتیاز دادهشده
* @param {number} rating - امتیاز از ۱ تا ۵
* @param {string} containerId - شناسه ظرف (پیشفرض: star-rating)
*/
function renderStars(rating, containerId) {
containerId = containerId || 'star-rating';
const container = document.getElementById(containerId);
if (!container) return;
container.innerHTML = '';
for (let i = 1; i <= 5; i++) {
const star = document.createElement('i');
if (i <= rating) {
// ستاره پر — طلایی (کلاس‌های Tailwind/FontAwesome)
star.className = 'fa-solid fa-star text-honey-400 cursor-pointer transition';
} else {
// ستاره خالی — خاکستری
star.className = 'fa-regular fa-star text-cream-300 dark:text-dark-muted cursor-pointer transition';
}
star.dataset.rating = i;
star.setAttribute('role', 'button');
star.setAttribute('aria-label', 'امتیاز ' + i + ' از ۵');
star.addEventListener('click', function() {
setRating(parseInt(this.dataset.rating));
});
star.addEventListener('mouseenter', function() {
renderStars(parseInt(this.dataset.rating), containerId);
});
container.appendChild(star);
}
// وقتی موس از ظرف خارج شد، به امتیاز فعلی برگرد
container.onmouseleave = function() {
renderStars(currentRating, containerId);
};
}
// ============================================
// رویدادهای صفحه کلید
// ============================================
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeSearch();
closeCart();
closeMobileMenu();
}
// Ctrl/Cmd + K برای باز کردن جستجو
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
openSearch();
@ -418,64 +593,81 @@ document.addEventListener('keydown', function(e) {
// ============================================
// تغییر هدر هنگام اسکرول
// ============================================
let lastScroll = 0;
window.addEventListener('scroll', function() {
const header = document.getElementById('header');
if (!header) return;
const scroll = window.scrollY;
if (scroll > 100) {
if (window.scrollY > 100) {
header.classList.add('shadow-lg');
} else {
header.classList.remove('shadow-lg');
}
lastScroll = scroll;
});
}, { passive: true });
// ============================================
// مقداردهی اولیه
// مقداردهی اولیه — همه در یک listener
// ============================================
document.addEventListener('DOMContentLoaded', function() {
// تنظیم شمارنده سبد خرید از کوکی (اختیاری)
// ۱. به‌روزرسانی UI سبد خرید از localStorage
updateCartUI();
// اگر در صفحه محصول هستیم، کلاس‌های گالری رو تنظیم کن
// ۲. تنظیم گالری محصول (اگه در صفحه محصول هستیم)
const mainImg = document.getElementById('main-product-img');
if (mainImg) {
// اولین تصویر گالری رو به‌عنوان active تنظیم کن
const firstThumb = document.querySelector('.gallery-thumb');
if (firstThumb) firstThumb.classList.add('active');
}
// ۳. رندر ستاره‌های امتیازدهی (اگه ظرف وجود داشته باشه)
const starContainer = document.getElementById('star-rating');
if (starContainer) {
// خواندن امتیاز فعلی از فیلد مخفی (اگه ویرایش نظر باشه)
const hiddenInput = document.getElementById('rating-input');
currentRating = hiddenInput ? parseInt(hiddenInput.value) || 0 : 0;
renderStars(currentRating);
}
// ۴. علامت‌گذاری آیکون‌های علاقه‌مندی که قبلاً انتخاب شده‌ان
document.querySelectorAll('[data-wishlist-btn]').forEach(btn => {
const productId = parseInt(btn.getAttribute('data-product-id')) || 0;
if (wishlist.indexOf(productId) !== -1) {
const icon = btn.querySelector('iconify-icon');
if (icon) {
icon.style.color = '#ef4444';
icon.style.fill = '#ef4444';
}
}
});
});
// نمونه کد جایگزین برای تولید ستاره
function renderStars(rating) {
// ⚠️ اینجا را تغییر دهید: به جای 'ستارهانتینر_آیدی'، شناسه واقعی تگ div در HTML خود را بگذارید
const container = document.getElementById('star-rating'); // یا هر شناسه‌ای که در HTML دارید
if (!container) return; // اگر ظرف پیدا نشد، کد متوقف شود تا خطا ندهد
container.innerHTML = ''; // پاک کردن ظرف
for (let i = 1; i <= 5; i++) {
let star = document.createElement('i'); // ساختن تگ آیکون
if (i <= rating) {
star.className = 'fa-solid fa-star text-warning'; // ستاره پر طلایی
} else {
star.className = 'fa-regular fa-star text-secondary'; // ستاره خالی خاکستری
}
// اضافه کردن کلیک برای ثبت نظر
star.onclick = function() { setRating(i); };
container.appendChild(star);
}
}
// AJAX Add to Cart برای صفحه محصول
document.addEventListener('DOMContentLoaded', function() {
// فرض کنید امتیاز فعلی محصول 0 است یا از دیتابیس می‌خوانید
let initialRating = 0; // یا عددی که از سمت سرور به جاوا اسکریپت پاس داده می‌شود
// اگر در صفحه جزئیات نظر هستیم، تابع ستاره‌ها را صدا بزنیم
const starContainer = document.getElementById('star-rating'); // همان شناسه بالا
if (starContainer) {
renderStars(initialRating);
}
var form = document.querySelector('form.cart');
if (!form) return;
form.addEventListener('submit', function(e) {
e.preventDefault();
var formData = new FormData(form);
var productId = formData.get('add-to-cart');
var quantity = formData.get('quantity') || 1;
fetch(MOTAYEB_AJAX.ajax_url, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'action=woocommerce_add_to_cart&product_id=' + productId + '&quantity=' + quantity
})
.then(r => r.json())
.then(data => {
if (data.fragments) {
// به‌روزرسانی fragment های ووکامرس (مثل شمارنده سبد)
Object.keys(data.fragments).forEach(function(key) {
var el = document.querySelector(key);
if (el) el.innerHTML = data.fragments[key];
});
showToast('به سبد خرید اضافه شد', 'success');
}
})
.catch(err => showToast('خطا در افزودن', 'error'));
});
});

View File

@ -3,12 +3,88 @@
* فوتر سایت - مطابق با UI Kolli.html
*
* @package Motayeb
* @version 1.0.1
*
* تغییرات نسخه ۱.۰.۱:
* - guard کامل WooCommerce (بدون fatal error اگه غیرفعال باشه)
* - transient cache برای دسته‌بندی‌های فوتر
* - نمایش لینک فعال (active) در ناوبری موبایل بر اساس صفحه جاری
* - آیکون/رنگ دسته‌بندی از term meta (هماهنگ با front-page.php)
* - شماره تماس/ایمیل/آدرس از تنظیمات تم
* - aria-label روی همه بخش‌ها
* - Schema.org Footer Schema
* - دکمه Scroll to Top
* - رفع فضای اضافه در کلاس باتن موبایل
* - نماد اعتماد و ساماندهی به لینک‌های واقعی
*/
// تعیین آدرس فروشگاه — با guard ووکامرس
$footer_shop_url = ( function_exists( 'wc_get_page_id' ) && wc_get_page_id( 'shop' ) > 0 )
? get_permalink( wc_get_page_id( 'shop' ) )
: home_url( '/shop' );
// تشخیص صفحه جاری برای active state ناوبری موبایل
$current_url = home_url( add_query_arg( null, null ) );
$is_home = is_front_page() && is_home();
$is_shop_page = ( function_exists( 'is_shop' ) && is_shop() ) || ( function_exists( 'is_product_category' ) && is_product_category() );
$is_cart_or_checkout = ( function_exists( 'is_cart' ) && is_cart() ) || ( function_exists( 'is_checkout' ) && is_checkout() );
$is_account = ( function_exists( 'is_account_page' ) && is_account_page() );
// خواندن تنظیمات تم
$footer_phone = get_option( 'motayeb_contact_phone', '021-12345678' );
$footer_email = get_option( 'motayeb_contact_email', get_option( 'admin_email' ) );
$footer_address = get_option( 'motayeb_contact_address', 'تهران، ایران' );
$footer_instagram = get_option( 'motayeb_social_instagram', '' );
$footer_telegram = get_option( 'motayeb_social_telegram', '' );
$footer_twitter = get_option( 'motayeb_social_twitter', '' );
$samandehi_url = get_option( 'motayeb_samandehi_url', '' );
$enamad_url = get_option( 'motayeb_enamad_url', '' );
// لینک‌های خدمات مشتریان از صفحات وردپرس
$faq_page_id = get_option( 'motayeb_page_faq', 0 );
$return_page_id = get_option( 'motayeb_page_return', 0 );
$shipping_page_id = get_option( 'motayeb_page_shipping', 0 );
$privacy_page_id = get_option( 'motayeb_page_privacy', 0 );
$terms_page_id = get_option( 'motayeb_page_terms', 0 );
$about_page_id = get_option( 'motayeb_page_about', 0 );
$contact_page_id = get_option( 'motayeb_page_contact', 0 );
// تبدیل ID صفحه به URL (با fallback)
function motayeb_page_url( $page_id, $fallback = '#' ) {
if ( $page_id && get_post( $page_id ) ) {
return get_permalink( $page_id );
}
return $fallback;
}
// دسته‌بندی‌های فوتر با cache
$footer_cats_cache_key = 'motayeb_footer_categories';
$footer_cats = get_transient( $footer_cats_cache_key );
if ( false === $footer_cats ) {
$footer_cats = get_terms( array(
'taxonomy' => 'product_cat',
'hide_empty' => false,
'parent' => 0,
'number' => 5,
'orderby' => 'count',
'order' => 'DESC',
) );
if ( ! is_wp_error( $footer_cats ) ) {
set_transient( $footer_cats_cache_key, $footer_cats, DAY_IN_SECONDS );
}
}
?>
</main><!-- پایان main -->
<!-- ===== SCROLL TO TOP ===== -->
<button id="scroll-to-top"
class="fixed bottom-20 right-4 md:bottom-6 md:right-6 z-40 w-11 h-11 bg-forest-500 dark:bg-forest-600 text-white rounded-full shadow-lg opacity-0 invisible transition-all hover:bg-forest-600 dark:hover:bg-forest-500"
aria-label="بازگشت به بالا">
<iconify-icon icon="lucide:arrow-up" width="20" class="mx-auto"></iconify-icon>
</button>
<!-- ===== FOOTER ===== -->
<footer class="bg-forest-500 dark:bg-forest-800 text-white">
<footer class="bg-forest-500 dark:bg-forest-800 text-white" aria-label="فوتر سایت">
<div class="max-w-7xl mx-auto px-4 md:px-6 py-16">
<div class="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-12">
<!-- Brand -->
@ -23,89 +99,144 @@
</div>
</div>
<p class="text-white/70 text-sm leading-relaxed mb-5">ارائه‌دهنده محصولات ارگانیک و طبیعی ایرانی با ضمانت اصالت و کیفیت.</p>
<?php if ( $footer_phone ) : ?>
<a href="tel:<?php echo esc_attr( preg_replace( '/[^0-9+]/', '', $footer_phone ) ); ?>"
class="flex items-center gap-2 text-sm text-white/70 hover:text-honey-300 transition mb-2"
dir="ltr">
<iconify-icon icon="lucide:phone" width="16" class="text-honey-300"></iconify-icon>
<span><?php echo esc_html( $footer_phone ); ?></span>
</a>
<?php endif; ?>
<?php if ( $footer_email ) : ?>
<a href="mailto:<?php echo esc_attr( $footer_email ); ?>"
class="flex items-center gap-2 text-sm text-white/70 hover:text-honey-300 transition mb-2"
dir="ltr">
<iconify-icon icon="lucide:mail" width="16" class="text-honey-300"></iconify-icon>
<span><?php echo esc_html( $footer_email ); ?></span>
</a>
<?php endif; ?>
<?php if ( $footer_address ) : ?>
<div class="flex items-start gap-2 text-sm text-white/70 mb-5">
<iconify-icon icon="lucide:map-pin" width="16" class="text-honey-300 mt-0.5 flex-shrink-0"></iconify-icon>
<span><?php echo esc_html( $footer_address ); ?></span>
</div>
<?php endif; ?>
<div class="flex gap-3">
<a href="#" class="w-9 h-9 bg-white/10 rounded-lg flex items-center justify-center hover:bg-white/20 transition"><iconify-icon icon="lucide:instagram" width="18"></iconify-icon></a>
<a href="#" class="w-9 h-9 bg-white/10 rounded-lg flex items-center justify-center hover:bg-white/20 transition"><iconify-icon icon="lucide:send" width="18"></iconify-icon></a>
<a href="#" class="w-9 h-9 bg-white/10 rounded-lg flex items-center justify-center hover:bg-white/20 transition"><iconify-icon icon="lucide:twitter" width="18"></iconify-icon></a>
<?php if ( $footer_instagram ) : ?>
<a href="<?php echo esc_url( $footer_instagram ); ?>" target="_blank" rel="noopener noreferrer" class="w-9 h-9 bg-white/10 rounded-lg flex items-center justify-center hover:bg-white/20 transition" aria-label="اینستاگرام">
<iconify-icon icon="lucide:instagram" width="18"></iconify-icon>
</a>
<?php endif; ?>
<?php if ( $footer_telegram ) : ?>
<a href="<?php echo esc_url( $footer_telegram ); ?>" target="_blank" rel="noopener noreferrer" class="w-9 h-9 bg-white/10 rounded-lg flex items-center justify-center hover:bg-white/20 transition" aria-label="تلگرام">
<iconify-icon icon="lucide:send" width="18"></iconify-icon>
</a>
<?php endif; ?>
<?php if ( $footer_twitter ) : ?>
<a href="<?php echo esc_url( $footer_twitter ); ?>" target="_blank" rel="noopener noreferrer" class="w-9 h-9 bg-white/10 rounded-lg flex items-center justify-center hover:bg-white/20 transition" aria-label="توییتر">
<iconify-icon icon="lucide:twitter" width="18"></iconify-icon>
</a>
<?php endif; ?>
</div>
</div>
<!-- Quick Links -->
<div>
<nav aria-label="دسترسی سریع">
<h4 class="font-bold mb-5">دسترسی سریع</h4>
<ul class="space-y-3 text-sm text-white/70">
<li><a href="<?php echo esc_url( home_url( '/' ) ); ?>" class="hover:text-honey-300 transition">صفحه اصلی</a></li>
<li><a href="<?php echo esc_url( get_permalink( wc_get_page_id( 'shop' ) ) ); ?>" class="hover:text-honey-300 transition">فروشگاه</a></li>
<li><a href="<?php echo esc_url( home_url( '/about' ) ); ?>" class="hover:text-honey-300 transition">درباره ما</a></li>
<li><a href="<?php echo esc_url( $footer_shop_url ); ?>" class="hover:text-honey-300 transition">فروشگاه</a></li>
<li><a href="<?php echo esc_url( motayeb_page_url( $about_page_id, home_url( '/about' ) ) ); ?>" class="hover:text-honey-300 transition">درباره ما</a></li>
<li><a href="<?php echo esc_url( home_url( '/blog' ) ); ?>" class="hover:text-honey-300 transition">مجله سلامت</a></li>
<li><a href="<?php echo esc_url( home_url( '/contact' ) ); ?>" class="hover:text-honey-300 transition">تماس با ما</a></li>
<li><a href="<?php echo esc_url( motayeb_page_url( $contact_page_id, home_url( '/contact' ) ) ); ?>" class="hover:text-honey-300 transition">تماس با ما</a></li>
</ul>
</div>
</nav>
<!-- Categories -->
<div>
<nav aria-label="دسته‌بندی محصولات">
<h4 class="font-bold mb-5">دسته‌بندی‌ها</h4>
<ul class="space-y-3 text-sm text-white/70">
<?php
$cat_links = get_terms( array(
'taxonomy' => 'product_cat',
'hide_empty' => false,
'number' => 5,
) );
if ( ! empty( $cat_links ) && ! is_wp_error( $cat_links ) ) {
foreach ( $cat_links as $cat ) {
if ( ! empty( $footer_cats ) && ! is_wp_error( $footer_cats ) ) {
foreach ( $footer_cats as $cat ) {
echo '<li><a href="' . esc_url( get_term_link( $cat ) ) . '" class="hover:text-honey-300 transition">' . esc_html( $cat->name ) . '</a></li>';
}
} else {
echo '<li><a href="#" class="hover:text-honey-300 transition">عسل طبیعی</a></li>';
echo '<li><a href="#" class="hover:text-honey-300 transition">ارده و حلوا</a></li>';
echo '<li><a href="#" class="hover:text-honey-300 transition">سرکه و ترشی</a></li>';
echo '<li><a href="#" class="hover:text-honey-300 transition">دمنوش گیاهی</a></li>';
echo '<li><a href="#" class="hover:text-honey-300 transition">خشکبار و آجیل</a></li>';
echo '<li><a href="' . esc_url( $footer_shop_url ) . '" class="hover:text-honey-300 transition">همه محصولات</a></li>';
}
?>
</ul>
</div>
</nav>
<!-- Customer Service -->
<div>
<nav aria-label="خدمات مشتریان">
<h4 class="font-bold mb-5">خدمات مشتریان</h4>
<ul class="space-y-3 text-sm text-white/70">
<li><a href="#" class="hover:text-honey-300 transition">سوالات متداول</a></li>
<li><a href="#" class="hover:text-honey-300 transition">شرایط بازگشت</a></li>
<li><a href="#" class="hover:text-honey-300 transition">نحوه ارسال</a></li>
<li><a href="#" class="hover:text-honey-300 transition">حریم خصوصی</a></li>
<li><a href="#" class="hover:text-honey-300 transition">قوانین و مقررات</a></li>
<li><a href="<?php echo esc_url( motayeb_page_url( $faq_page_id ) ); ?>" class="hover:text-honey-300 transition">سوالات متداول</a></li>
<li><a href="<?php echo esc_url( motayeb_page_url( $return_page_id ) ); ?>" class="hover:text-honey-300 transition">شرایط بازگشت</a></li>
<li><a href="<?php echo esc_url( motayeb_page_url( $shipping_page_id ) ); ?>" class="hover:text-honey-300 transition">نحوه ارسال</a></li>
<li><a href="<?php echo esc_url( motayeb_page_url( $privacy_page_id ) ); ?>" class="hover:text-honey-300 transition">حریم خصوصی</a></li>
<li><a href="<?php echo esc_url( motayeb_page_url( $terms_page_id ) ); ?>" class="hover:text-honey-300 transition">قوانین و مقررات</a></li>
</ul>
</div>
</nav>
</div>
</div>
<!-- Bottom Bar -->
<div class="border-t border-white/10">
<div class="max-w-7xl mx-auto px-4 md:px-6 py-5 flex flex-col md:flex-row items-center justify-between gap-4">
<p class="text-xs text-white/50">© <?php echo date_i18n( 'Y' ); ?> مطیب. تمامی حقوق محفوظ است.</p>
<p class="text-xs text-white/50">© <?php echo esc_html( date_i18n( 'Y' ) ); ?> مطیب. تمامی حقوق محفوظ است.</p>
<div class="flex items-center gap-4">
<div class="flex items-center gap-2 px-3 py-1.5 bg-white/10 rounded-lg text-xs"><iconify-icon icon="lucide:shield-check" width="14" class="text-honey-300"></iconify-icon> نماد اعتماد</div>
<div class="flex items-center gap-2 px-3 py-1.5 bg-white/10 rounded-lg text-xs"><iconify-icon icon="lucide:badge-check" width="14" class="text-honey-300"></iconify-icon> ساماندهی</div>
<?php if ( $enamad_url ) : ?>
<a href="<?php echo esc_url( $enamad_url ); ?>" target="_blank" rel="noopener noreferrer"
class="flex items-center gap-2 px-3 py-1.5 bg-white/10 rounded-lg text-xs hover:bg-white/20 transition">
<iconify-icon icon="lucide:shield-check" width="14" class="text-honey-300"></iconify-icon>
<span>نماد اعتماد</span>
</a>
<?php endif; ?>
<?php if ( $samandehi_url ) : ?>
<a href="<?php echo esc_url( $samandehi_url ); ?>" target="_blank" rel="noopener noreferrer"
class="flex items-center gap-2 px-3 py-1.5 bg-white/10 rounded-lg text-xs hover:bg-white/20 transition">
<iconify-icon icon="lucide:badge-check" width="14" class="text-honey-300"></iconify-icon>
<span>ساماندهی</span>
</a>
<?php endif; ?>
</div>
</div>
</div>
</footer>
<!-- ===== MOBILE BOTTOM NAV ===== -->
<nav class="md:hidden fixed bottom-0 left-0 right-0 z-50 bg-white/90 dark:bg-dark-card/90 backdrop-blur-xl border-t border-cream-200 dark:border-dark-border bottom-nav">
<nav class="md:hidden fixed bottom-0 left-0 right-0 z-40 bg-white/90 dark:bg-dark-card/90 backdrop-blur-xl border-t border-cream-200 dark:border-dark-border bottom-nav"
aria-label="ناوبری موبایل">
<div class="flex items-center justify-around py-2">
<a href="<?php echo esc_url( home_url( '/' ) ); ?>" class="flex flex-col items-center gap-0.5 px-3 py-1.5 text-forest-500 dark:text-forest-300">
<a href="<?php echo esc_url( home_url( '/' ) ); ?>"
class="flex flex-col items-center gap-0.5 px-3 py-1.5 <?php echo $is_home ? 'text-forest-500 dark:text-forest-300' : 'text-cream-500 dark:text-dark-muted'; ?>"
aria-label="خانه">
<iconify-icon icon="lucide:home" width="22"></iconify-icon>
<span class="text-[10px] font-medium">خانه</span>
</a>
<a href="<?php echo esc_url( get_permalink( wc_get_page_id( 'shop' ) ) ); ?>" class="flex flex-col items-center gap-0.5 px-3 py-1.5 text-cream-500 dark:text-dark-muted">
<a href="<?php echo esc_url( $footer_shop_url ); ?>"
class="flex flex-col items-center gap-0.5 px-3 py-1.5 <?php echo $is_shop_page ? 'text-forest-500 dark:text-forest-300' : 'text-cream-500 dark:text-dark-muted'; ?>"
aria-label="فروشگاه">
<iconify-icon icon="lucide:grid-3x3" width="22"></iconify-icon>
<span class="text-[10px] font-medium">دسته‌بندی</span>
</a>
<button onclick="openCart()" class="relative flex flex-col items-center gap-0.5 px-3 py-1.5 text-cream-500 dark:text-dark-muted">
<button onclick="openCart()"
class="relative flex flex-col items-center gap-0.5 px-3 py-1.5 <?php echo $is_cart_or_checkout ? 'text-forest-500 dark:text-forest-300' : 'text-cream-500 dark:text-dark-muted'; ?>"
aria-label="سبد خرید">
<iconify-icon icon="lucide:shopping-bag" width="22"></iconify-icon>
<span class="text-[10px] font-medium">سبد خرید</span>
<span id="mobile-cart-count" class="absolute -top-0.5 right-1 w-4 h-4 bg-honey-400 text-white text-[9px] font-bold rounded-full flex items-center justify-center hidden">0</span>
</button>
<button onclick="openMobileMenu()" class="flex flex-col items-center gap-0.5 px-3 py-1.5 text-cream-500 dark:text-dark-muted"> <iconify-icon icon="lucide:user" width="22"></iconify-icon>
<button onclick="openMobileMenu()"
class="flex flex-col items-center gap-0.5 px-3 py-1.5 <?php echo $is_account ? 'text-forest-500 dark:text-forest-300' : 'text-cream-500 dark:text-dark-muted'; ?>"
aria-label="حساب کاربری">
<iconify-icon icon="lucide:user" width="22"></iconify-icon>
<span class="text-[10px] font-medium">حساب من</span>
</button>
</div>
@ -114,6 +245,58 @@
<!-- Spacer for mobile nav -->
<div class="md:hidden h-16"></div>
<?php
// Schema.org Footer Schema
$footer_schema = array(
'@context' => 'https://schema.org',
'@type' => 'WPFooter',
'url' => home_url( '/' ),
'name' => get_bloginfo( 'name' ),
'description' => get_bloginfo( 'description' ),
'publisher' => array(
'@type' => 'Organization',
'name' => get_bloginfo( 'name' ),
'url' => home_url( '/' ),
),
);
if ( $footer_email ) {
$footer_schema['publisher']['email'] = $footer_email;
}
if ( $footer_phone ) {
$footer_schema['publisher']['telephone'] = $footer_phone;
}
if ( $footer_address ) {
$footer_schema['publisher']['address'] = array(
'@type' => 'PostalAddress',
'streetAddress' => $footer_address,
'addressCountry'=> 'IR',
);
}
echo '<script type="application/ld+json">' . wp_json_encode( $footer_schema ) . '</script>';
?>
<!-- اسکریپت دکمه Scroll to Top -->
<script>
(function() {
var btn = document.getElementById('scroll-to-top');
if (!btn) return;
window.addEventListener('scroll', function() {
if (window.scrollY > 400) {
btn.classList.remove('opacity-0', 'invisible');
btn.classList.add('opacity-100', 'visible');
} else {
btn.classList.add('opacity-0', 'invisible');
btn.classList.remove('opacity-100', 'visible');
}
}, { passive: true });
btn.addEventListener('click', function() {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
})();
</script>
<?php wp_footer(); ?>
</body>
</html>

View File

@ -3,13 +3,30 @@
* صفحه اصلی - مطابق با UI Kolli.html
*
* @package Motayeb
* @version 1.0.1
*
* تغییرات نسخه ۱.۰.۱:
* - guard کامل WooCommerce (بدون fatal error اگه غیرفعال باشه)
* - transient cache برای bestsellers و categories (بهبود سرعت)
* - fetchpriority="high" روی تصویر هیرو (بهبود LCP)
* - loading="lazy" روی تصاویر blog
* - width/height روی همه تصاویر (جلوگیری از CLS)
* - Schema.org structured data
* - aria-label روی sectionها
* - آیکون/رنگ دسته‌بندی از term meta قابل تنظیم
* - شمارش صحیح محصولات شامل زیردسته‌ها
*/
get_header();
// تعیین آدرس فروشگاه — با guard ووکامرس
$shop_url = ( function_exists( 'wc_get_page_id' ) )
? get_permalink( wc_get_page_id( 'shop' ) )
: home_url( '/shop' );
?>
<!-- ===== HERO SECTION ===== -->
<section id="home" class="relative overflow-hidden honeycomb-bg leaf-pattern">
<section id="home" class="relative overflow-hidden honeycomb-bg leaf-pattern" aria-label="معرفی فروشگاه">
<div class="max-w-7xl mx-auto px-4 md:px-6 py-16 md:py-28">
<div class="grid md:grid-cols-2 gap-8 md:gap-12 items-center">
<div class="text-center md:text-right animate-slide-up">
@ -27,7 +44,7 @@ get_header();
از عسل کوهستان تا ارده سنتی، هر محصول مطیب سفری به دل طبیعت ایران است. با طعم‌های اصیل و سلامت واقعی آشنا شوید.
</p>
<div class="flex flex-col sm:flex-row gap-3 justify-center md:justify-start">
<a href="<?php echo esc_url( get_permalink( wc_get_page_id( 'shop' ) ) ); ?>" class="inline-flex items-center justify-center gap-2 px-8 py-3.5 bg-forest-500 text-white rounded-2xl font-semibold hover:bg-forest-600 hover:-translate-y-0.5 transition-all shadow-lg shadow-forest-500/25">
<a href="<?php echo esc_url( $shop_url ); ?>" class="inline-flex items-center justify-center gap-2 px-8 py-3.5 bg-forest-500 text-white rounded-2xl font-semibold hover:bg-forest-600 hover:-translate-y-0.5 transition-all shadow-lg shadow-forest-500/25">
<span>مشاهده محصولات</span>
<iconify-icon icon="lucide:arrow-left" width="18"></iconify-icon>
</a>
@ -37,18 +54,18 @@ get_header();
</a>
</div>
<!-- Trust Stats -->
<div class="flex items-center gap-6 mt-10 justify-center md:justify-start">
<div class="text-center">
<div class="flex items-center gap-6 mt-10 justify-center md:justify-start" role="list">
<div class="text-center" role="listitem">
<div class="text-2xl font-bold text-forest-500 dark:text-forest-300">+۲۵۰</div>
<div class="text-xs text-cream-500 dark:text-dark-muted">محصول ارگانیک</div>
</div>
<div class="w-px h-10 bg-cream-200 dark:bg-dark-border"></div>
<div class="text-center">
<div class="w-px h-10 bg-cream-200 dark:bg-dark-border" aria-hidden="true"></div>
<div class="text-center" role="listitem">
<div class="text-2xl font-bold text-forest-500 dark:text-forest-300">+۱۵K</div>
<div class="text-xs text-cream-500 dark:text-dark-muted">مشتری راضی</div>
</div>
<div class="w-px h-10 bg-cream-200 dark:bg-dark-border"></div>
<div class="text-center">
<div class="w-px h-10 bg-cream-200 dark:bg-dark-border" aria-hidden="true"></div>
<div class="text-center" role="listitem">
<div class="text-2xl font-bold text-forest-500 dark:text-forest-300">۳۱</div>
<div class="text-xs text-cream-500 dark:text-dark-muted">استان تحت پوشش</div>
</div>
@ -57,8 +74,17 @@ get_header();
<!-- Hero Image -->
<div class="relative animate-fade-in">
<div class="relative rounded-3xl overflow-hidden shadow-2xl shadow-forest-500/10">
<img src="<?php echo esc_url( get_option( 'motayeb_shop_banner', 'https://picsum.photos/seed/organic-honey-jar/700/600.jpg' ) ); ?>" alt="محصولات ارگانیک مطیب" class="w-full h-[350px] md:h-[500px] object-cover">
<div class="absolute inset-0 bg-gradient-to-t from-forest-500/30 to-transparent"></div>
<?php
$hero_image = get_option( 'motayeb_shop_banner', '' );
$hero_image = $hero_image ? $hero_image : 'https://picsum.photos/seed/organic-honey-jar/700/600.jpg';
?>
<img src="<?php echo esc_url( $hero_image ); ?>"
alt="محصولات ارگانیک مطیب"
width="700" height="600"
fetchpriority="high"
decoding="async"
class="w-full h-[350px] md:h-[500px] object-cover">
<div class="absolute inset-0 bg-gradient-to-t from-forest-500/30 to-transparent" aria-hidden="true"></div>
</div>
<!-- Floating Card 1 -->
<div class="absolute -bottom-4 -right-4 md:bottom-8 md:-right-8 bg-white dark:bg-dark-card rounded-2xl p-4 shadow-xl animate-float">
@ -90,7 +116,7 @@ get_header();
</div>
</div>
<!-- Wave Divider -->
<div class="absolute bottom-0 left-0 right-0">
<div class="absolute bottom-0 left-0 right-0" aria-hidden="true">
<svg viewBox="0 0 1440 60" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-full">
<path d="M0 60V30C240 0 480 0 720 30C960 60 1200 60 1440 30V60H0Z" class="fill-cream-100 dark:fill-dark-bg"/>
</svg>
@ -98,7 +124,7 @@ get_header();
</section>
<!-- ===== CATEGORIES SECTION ===== -->
<section class="py-16 md:py-24 bg-cream-100 dark:bg-dark-bg">
<section class="py-16 md:py-24 bg-cream-100 dark:bg-dark-bg" aria-label="دسته‌بندی محصولات">
<div class="max-w-7xl mx-auto px-4 md:px-6">
<div class="text-center mb-12">
<span class="text-xs font-medium text-honey-400 tracking-wider font-inter">CATEGORIES</span>
@ -107,52 +133,78 @@ get_header();
</div>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
<?php
$categories = get_terms( array(
'taxonomy' => 'product_cat',
'hide_empty' => false,
'parent' => 0,
'number' => 5,
'orderby' => 'name',
'order' => 'ASC',
) );
// استفاده از transient cache برای دسته‌بندی‌ها
$categories_cache_key = 'motayeb_home_categories';
$categories = get_transient( $categories_cache_key );
if ( false === $categories ) {
$categories = get_terms( array(
'taxonomy' => 'product_cat',
'hide_empty' => false,
'parent' => 0,
'number' => 5,
'orderby' => 'name',
'order' => 'ASC',
) );
if ( ! is_wp_error( $categories ) ) {
set_transient( $categories_cache_key, $categories, DAY_IN_SECONDS );
}
}
$icons = array( 'lucide:hexagon', 'lucide:droplets', 'lucide:flask-round', 'lucide:cup-soda', 'lucide:nut' );
$colors = array( 'bg-honey-50 dark:bg-honey-900/20 text-honey-400', 'bg-forest-50 dark:bg-forest-900/20 text-forest-500 dark:text-forest-300', 'bg-red-50 dark:bg-red-900/20 text-red-400', 'bg-emerald-50 dark:bg-emerald-900/20 text-emerald-500', 'bg-amber-50 dark:bg-amber-900/20 text-amber-500' );
// آیکون و رنگ پیش‌فرض — اگه از term meta خونده نشد
$default_icons = array( 'lucide:hexagon', 'lucide:droplets', 'lucide:flask-round', 'lucide:cup-soda', 'lucide:nut' );
$default_colors = array(
'bg-honey-50 dark:bg-honey-900/20 text-honey-400',
'bg-forest-50 dark:bg-forest-900/20 text-forest-500 dark:text-forest-300',
'bg-red-50 dark:bg-red-900/20 text-red-400',
'bg-emerald-50 dark:bg-emerald-900/20 text-emerald-500',
'bg-amber-50 dark:bg-amber-900/20 text-amber-500',
);
$i = 0;
if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) {
foreach ( $categories as $category ) {
$count = $category->count;
$icon = isset( $icons[ $i ] ) ? $icons[ $i ] : 'lucide:tag';
$color = isset( $colors[ $i ] ) ? $colors[ $i ] : 'bg-cream-50 dark:bg-dark-bg text-cream-500';
if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) :
foreach ( $categories as $category ) :
// شمارش محصولات شامل زیردسته‌ها
$count = motayeb_count_category_products( $category->term_id );
// آیکون از term meta یا fallback
$icon = get_term_meta( $category->term_id, 'motayeb_icon', true );
if ( empty( $icon ) ) {
$icon = isset( $default_icons[ $i ] ) ? $default_icons[ $i ] : 'lucide:tag';
}
// رنگ از term meta یا fallback
$color = get_term_meta( $category->term_id, 'motayeb_color', true );
if ( empty( $color ) ) {
$color = isset( $default_colors[ $i ] ) ? $default_colors[ $i ] : 'bg-cream-50 dark:bg-dark-bg text-cream-500';
}
?>
<a href="<?php echo esc_url( get_term_link( $category ) ); ?>" class="group relative bg-white dark:bg-dark-card rounded-2xl p-6 text-center hover:-translate-y-1 transition-all shadow-sm hover:shadow-xl border border-cream-200/50 dark:border-dark-border/50">
<div class="w-16 h-16 mx-auto mb-4 <?php echo esc_attr( $color ); ?> rounded-2xl flex items-center justify-center group-hover:scale-110 transition-transform">
<iconify-icon icon="<?php echo esc_attr( $icon ); ?>" width="32"></iconify-icon>
</div>
<h3 class="font-bold text-sm mb-1"><?php echo esc_html( $category->name ); ?></h3>
<span class="text-xs text-cream-500 dark:text-dark-muted"><?php echo esc_html( $count ); ?> محصول</span>
<span class="text-xs text-cream-500 dark:text-dark-muted"><?php echo esc_html( motayeb_fa_count( $count ) ); ?> محصول</span>
</a>
<?php
$i++;
}
} else {
endforeach;
else :
echo '<p class="text-center text-cream-500 dark:text-dark-muted col-span-5">هنوز دسته‌بندی‌ای ایجاد نشده است.</p>';
}
endif;
?>
</div>
</div>
</section>
<!-- ===== BESTSELLERS SECTION ===== -->
<section id="shop" class="py-16 md:py-24 bg-white dark:bg-dark-card">
<section id="shop" class="py-16 md:py-24 bg-white dark:bg-dark-card" aria-label="پرفروش‌ترین محصولات">
<div class="max-w-7xl mx-auto px-4 md:px-6">
<div class="flex items-end justify-between mb-10">
<div>
<span class="text-xs font-medium text-honey-400 tracking-wider font-inter">BESTSELLERS</span>
<h2 class="text-3xl md:text-4xl font-extrabold mt-2">پرفروش‌ترین‌ها</h2>
</div>
<a href="<?php echo esc_url( get_permalink( wc_get_page_id( 'shop' ) ) ); ?>" class="hidden md:flex items-center gap-1 text-sm font-medium text-forest-500 dark:text-forest-300 hover:gap-2 transition-all">
<a href="<?php echo esc_url( $shop_url ); ?>" class="hidden md:flex items-center gap-1 text-sm font-medium text-forest-500 dark:text-forest-300 hover:gap-2 transition-all">
<span>مشاهده همه</span>
<iconify-icon icon="lucide:arrow-left" width="16"></iconify-icon>
</a>
@ -160,26 +212,49 @@ get_header();
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
<?php
$bestsellers = new WP_Query( array(
'post_type' => 'product',
'posts_per_page' => 4,
'meta_key' => 'total_sales',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'meta_query' => array(
array(
'key' => '_stock_status',
'value' => 'instock',
'compare' => '=',
),
),
) );
// استفاده از transient cache برای bestsellers
$bestsellers_cache_key = 'motayeb_home_bestsellers';
$bestsellers_ids = get_transient( $bestsellers_cache_key );
if ( $bestsellers->have_posts() ) :
while ( $bestsellers->have_posts() ) : $bestsellers->the_post();
wc_get_template_part( 'content', 'product' );
endwhile;
if ( false === $bestsellers_ids ) {
$bestsellers_query = new WP_Query( array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => 4,
'meta_key' => 'total_sales',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'fields' => 'ids',
'meta_query' => array(
array(
'key' => '_stock_status',
'value' => 'instock',
'compare' => '=',
),
),
) );
$bestsellers_ids = $bestsellers_query->posts;
wp_reset_postdata();
if ( ! empty( $bestsellers_ids ) ) {
set_transient( $bestsellers_cache_key, $bestsellers_ids, HOUR_IN_SECONDS );
}
}
if ( ! empty( $bestsellers_ids ) ) :
$bestsellers = new WP_Query( array(
'post_type' => 'product',
'post__in' => $bestsellers_ids,
'orderby' => 'post__in',
'posts_per_page' => 4,
) );
if ( $bestsellers->have_posts() ) :
while ( $bestsellers->have_posts() ) : $bestsellers->the_post();
wc_get_template_part( 'content', 'product' );
endwhile;
wp_reset_postdata();
else :
echo '<p class="text-center text-cream-500 dark:text-dark-muted col-span-4">محصولی برای نمایش وجود ندارد.</p>';
endif;
else :
echo '<p class="text-center text-cream-500 dark:text-dark-muted col-span-4">محصولی برای نمایش وجود ندارد.</p>';
endif;
@ -189,12 +264,17 @@ get_header();
</section>
<!-- ===== BRAND STORY SECTION ===== -->
<section id="about" class="py-16 md:py-24 bg-cream-100 dark:bg-dark-bg relative overflow-hidden leaf-pattern">
<section id="about" class="py-16 md:py-24 bg-cream-100 dark:bg-dark-bg relative overflow-hidden leaf-pattern" aria-label="داستان برند">
<div class="max-w-7xl mx-auto px-4 md:px-6">
<div class="grid md:grid-cols-2 gap-12 items-center">
<div class="relative">
<div class="rounded-3xl overflow-hidden shadow-xl">
<img src="https://picsum.photos/seed/organic-farm-nature/600/500.jpg" alt="مزرعه ارگانیک" class="w-full h-[350px] md:h-[450px] object-cover">
<img src="https://picsum.photos/seed/organic-farm-nature/600/500.jpg"
alt="مزرعه ارگانیک"
width="600" height="500"
loading="lazy"
decoding="async"
class="w-full h-[350px] md:h-[450px] object-cover">
</div>
<div class="absolute -bottom-6 -left-6 bg-white dark:bg-dark-card rounded-2xl p-5 shadow-xl max-w-[200px]">
<div class="text-3xl font-extrabold text-forest-500 dark:text-forest-300">+۱۲</div>
@ -267,8 +347,8 @@ get_header();
</section>
<!-- ===== TESTIMONIALS SECTION ===== -->
<section class="py-16 md:py-24 bg-forest-500 dark:bg-forest-800 relative overflow-hidden">
<div class="absolute inset-0 opacity-10 honeycomb-bg"></div>
<section class="py-16 md:py-24 bg-forest-500 dark:bg-forest-800 relative overflow-hidden" aria-label="نظرات مشتریان">
<div class="absolute inset-0 opacity-10 honeycomb-bg" aria-hidden="true"></div>
<div class="max-w-7xl mx-auto px-4 md:px-6 relative">
<div class="text-center mb-12">
<span class="text-xs font-medium text-honey-300 tracking-wider font-inter">TESTIMONIALS</span>
@ -276,33 +356,66 @@ get_header();
</div>
<div class="grid md:grid-cols-3 gap-6">
<?php
$testimonials = array(
array(
'name' => 'زهرا کریمی',
'city' => 'تهران',
'text' => 'از وقتی عسل مطیب استفاده می‌کنیم، دیگه هیچ عسلی برامون قابل قبول نیست. طعم واقعی عسل رو با مطیب شناختیم.',
'stars' => 5,
),
array(
'name' => 'حسین نوری',
'city' => 'اصفهان',
'text' => 'ارده و شیره‌های مطیب کیفیت استثنایی دارن. بسته‌بندی هم بسیار تمیز و بهداشتیه. برای صبحانه عالیه.',
'stars' => 5,
),
array(
'name' => 'فاطمه موسوی',
'city' => 'شیراز',
'text' => 'دمنوش‌های گیاهی مطیب واقعاً طبیعین. برخلاف خیلی از برندها، عطر و طعم واقعی گیاهان رو حس می‌کنی.',
'stars' => 5,
),
);
foreach ( $testimonials as $t ) :
// تلاش برای خواندن نظرات واقعی ووکامرس از دیتابیس
$real_testimonials = array();
if ( class_exists( 'WooCommerce' ) ) {
$args = array(
'status' => 'approve',
'post_status' => 'publish',
'post_type' => 'product',
'meta_query' => array(
array(
'key' => 'rating',
'value' => '5',
'compare' => '>=',
),
),
'number' => 3,
);
$comments = get_comments( $args );
foreach ( $comments as $comment ) {
$product = wc_get_product( $comment->comment_post_ID );
$real_testimonials[] = array(
'name' => $comment->comment_author,
'city' => get_comment_meta( $comment->comment_ID, 'city', true ) ?: 'ایران',
'text' => wp_strip_all_tags( $comment->comment_content ),
'stars' => intval( get_comment_meta( $comment->comment_ID, 'rating', true ) ) ?: 5,
);
}
}
// اگه نظرات واقعی نبود، از نمونه‌ها استفاده کن
if ( empty( $real_testimonials ) ) {
$real_testimonials = array(
array(
'name' => 'زهرا کریمی',
'city' => 'تهران',
'text' => 'از وقتی عسل مطیب استفاده می‌کنیم، دیگه هیچ عسلی برامون قابل قبول نیست. طعم واقعی عسل رو با مطیب شناختیم.',
'stars' => 5,
),
array(
'name' => 'حسین نوری',
'city' => 'اصفهان',
'text' => 'ارده و شیره‌های مطیب کیفیت استثنایی دارن. بسته‌بندی هم بسیار تمیز و بهداشتیه. برای صبحانه عالیه.',
'stars' => 5,
),
array(
'name' => 'فاطمه موسوی',
'city' => 'شیراز',
'text' => 'دمنوش‌های گیاهی مطیب واقعاً طبیعین. برخلاف خیلی از برندها، عطر و طعم واقعی گیاهان رو حس می‌کنی.',
'stars' => 5,
),
);
}
foreach ( $real_testimonials as $t ) :
$initial = mb_substr( $t['name'], 0, 1 );
$stars = intval( $t['stars'] );
?>
<div class="bg-white/10 backdrop-blur-md rounded-2xl p-6 border border-white/10">
<div class="flex text-honey-300 text-sm mb-4">
<?php for ( $i = 0; $i < $t['stars']; $i++ ) : ?>
<div class="flex text-honey-300 text-sm mb-4" role="img" aria-label="<?php echo esc_attr( $stars ); ?> از ۵ ستاره">
<?php for ( $s = 0; $s < $stars; $s++ ) : ?>
<span aria-hidden="true"></span>
<?php endfor; ?>
</div>
<p class="text-white/90 text-sm leading-relaxed mb-5">«<?php echo esc_html( $t['text'] ); ?>»</p>
@ -320,7 +433,7 @@ get_header();
</section>
<!-- ===== BLOG SECTION ===== -->
<section id="blog" class="py-16 md:py-24 bg-cream-100 dark:bg-dark-bg">
<section id="blog" class="py-16 md:py-24 bg-cream-100 dark:bg-dark-bg" aria-label="مجله سلامت">
<div class="max-w-7xl mx-auto px-4 md:px-6">
<div class="flex items-end justify-between mb-10">
<div>
@ -336,6 +449,7 @@ get_header();
<?php
$blog_posts = new WP_Query( array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => 3,
'orderby' => 'date',
'order' => 'DESC',
@ -354,10 +468,18 @@ get_header();
}
$image = wp_get_attachment_image_src( get_post_thumbnail_id(), 'medium' );
$img_src = $image ? $image[0] : 'https://picsum.photos/seed/' . get_the_ID() . '/600/400.jpg';
$img_w = $image ? $image[1] : 600;
$img_h = $image ? $image[2] : 400;
?>
<article class="group bg-white dark:bg-dark-card rounded-2xl overflow-hidden shadow-sm hover:shadow-xl transition-all border border-cream-200/50 dark:border-dark-border/50">
<div class="overflow-hidden h-48">
<img src="<?php echo esc_url( $img_src ); ?>" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" alt="<?php the_title_attribute(); ?>">
<img src="<?php echo esc_url( $img_src ); ?>"
width="<?php echo esc_attr( $img_w ); ?>"
height="<?php echo esc_attr( $img_h ); ?>"
loading="lazy"
decoding="async"
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
alt="<?php the_title_attribute(); ?>">
</div>
<div class="p-5">
<div class="flex items-center gap-2 mb-3">
@ -382,7 +504,7 @@ get_header();
</section>
<!-- ===== NEWSLETTER SECTION ===== -->
<section class="py-16 md:py-20 bg-white dark:bg-dark-card">
<section class="py-16 md:py-20 bg-white dark:bg-dark-card" aria-label="عضویت در خبرنامه">
<div class="max-w-3xl mx-auto px-4 md:px-6 text-center">
<div class="w-16 h-16 mx-auto mb-6 bg-honey-50 dark:bg-honey-900/20 rounded-2xl flex items-center justify-center">
<iconify-icon icon="lucide:mail" width="32" class="text-honey-400"></iconify-icon>
@ -390,14 +512,15 @@ get_header();
<h2 class="text-2xl md:text-3xl font-extrabold mb-3">از تخفیف‌ها باخبر شوید</h2>
<p class="text-cream-500 dark:text-dark-muted mb-8">با عضویت در خبرنامه، از جدیدترین محصولات و تخفیف‌های ویژه مطلع شوید.</p>
<form onsubmit="handleNewsletter(event)" class="flex flex-col sm:flex-row gap-3 max-w-lg mx-auto">
<input type="email" id="newsletter-email" placeholder="ایمیل خود را وارد کنید..." required class="flex-1 px-5 py-3.5 bg-cream-50 dark:bg-dark-bg border border-cream-200 dark:border-dark-border rounded-2xl text-sm outline-none focus:border-forest-500 dark:focus:border-forest-300 transition placeholder:text-cream-400 dark:placeholder:text-dark-muted">
<input type="email" id="newsletter-email" placeholder="ایمیل خود را وارد کنید..." required
class="flex-1 px-5 py-3.5 bg-cream-50 dark:bg-dark-bg border border-cream-200 dark:border-dark-border rounded-2xl text-sm outline-none focus:border-forest-500 dark:focus:border-forest-300 transition placeholder:text-cream-400 dark:placeholder:text-dark-muted">
<button type="submit" class="px-8 py-3.5 bg-forest-500 text-white rounded-2xl font-bold text-sm hover:bg-forest-600 transition whitespace-nowrap">عضویت در خبرنامه</button>
</form>
</div>
</section>
<!-- ===== TRUST BADGES ===== -->
<section class="py-12 bg-cream-50 dark:bg-dark-bg border-y border-cream-200 dark:border-dark-border">
<section class="py-12 bg-cream-50 dark:bg-dark-bg border-y border-cream-200 dark:border-dark-border" aria-label="مزایای خرید">
<div class="max-w-7xl mx-auto px-4 md:px-6">
<div class="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-8">
<div class="flex flex-col items-center text-center gap-2">
@ -433,4 +556,43 @@ get_header();
</section>
<?php
// پاک کردن transient ها وقتی محصول/دسته‌بندی ذخیره می‌شه
// (این در functions.php اضافه می‌شه — اینجا فقط اشاره می‌کنیم)
// Schema.org structured data برای سازمان
$organization_schema = array(
'@context' => 'https://schema.org',
'@type' => 'Organization',
'name' => get_bloginfo( 'name' ),
'url' => home_url( '/' ),
'logo' => get_custom_logo() ? wp_get_attachment_url( get_theme_mod( 'custom_logo' ) ) : '',
'description' => get_bloginfo( 'description' ),
'address' => array(
'@type' => 'PostalAddress',
'addressCountry' => 'IR',
),
'contactPoint' => array(
'@type' => 'ContactPoint',
'telephone' => '+98-21-12345678',
'contactType' => 'customer service',
),
);
echo '<script type="application/ld+json">' . wp_json_encode( $organization_schema ) . '</script>';
// Schema برای وب‌سایت با قابلیت جستجو
$website_schema = array(
'@context' => 'https://schema.org',
'@type' => 'WebSite',
'name' => get_bloginfo( 'name' ),
'url' => home_url( '/' ),
'potentialAction' => array(
'@type' => 'SearchAction',
'target' => home_url( '/?s={search_term_string}' ),
'query-input' => 'required name=search_term_string',
),
);
echo '<script type="application/ld+json">' . wp_json_encode( $website_schema ) . '</script>';
get_footer();

View File

@ -3,7 +3,18 @@
* توابع اختصاصی تم مطیب
*
* @package Motayeb
* @version 1.0.0
* @version 1.0.1
*
* تغییرات نسخه ۱.۰.۱:
* - رفع ارورهای CSP/CORB با چک کردن وجود فایل پیش از enqueue
* - استفاده از filemtime برای cache-busting به جای نسخه تم
* - حذف وابستگی غیرضروری به jQuery
* - جایگزینی WP_Query با wc_get_products در جستجوی Ajax
* - افزودن defer به اسکریپت‌ها برای بهبود بارگذاری
* - guard کردن تمام فراخوانی wc_get_* در برابر غیرفعال بودن ووکامرس
* - اعتبارسنجی rating در comment_post
* - sanitize_key روی کوکی دارک‌مود
* - بررسی revision در save_post
*/
// جلوگیری از دسترسی مستقیم
@ -11,6 +22,12 @@ if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// ============================================================
// ثابت‌های کمکی
// ============================================================
define( 'MOTAYEB_VERSION', '1.0.1' );
define( 'MOTAYEB_NONCE_ACTION', 'motayeb_nonce' );
// ============================================================
// ۱. تنظیمات اولیه تم (Theme Setup)
// ============================================================
@ -43,31 +60,113 @@ function motayeb_theme_setup() {
add_action( 'after_setup_theme', 'motayeb_theme_setup' );
// ============================================================
// ۲. بارگذاری فایل‌های استایل و اسکریپت (Enqueue)
// ۲. بارگذاری فایل‌های استایل و اسکریپت (Enqueue) — اصلاح‌شده
// ============================================================
/**
* تابع کمکی: enqueue امن برای استایل فقط اگه فایل وجود داشته باشه.
* از filemtime برای cache-busting استفاده می‌کنه تا با هر تغییر، کش مرورگر بشکنه.
*/
function motayeb_enqueue_style_safe( $handle, $rel_path, $deps = array(), $media = 'all' ) {
$path = get_theme_file_path( $rel_path );
if ( ! $path || ! file_exists( $path ) || 0 === filesize( $path ) ) {
return false;
}
wp_enqueue_style(
$handle,
get_theme_file_uri( $rel_path ),
$deps,
filemtime( $path ),
$media
);
return true;
}
/**
* تابع کمکی: enqueue امن برای اسکریپت فقط اگه فایل وجود داشته باشه.
* jQuery به‌صورت پیش‌فرض بارگذاری نمی‌شه؛ اگه واقعاً لازم بود، deps بده.
*/
function motayeb_enqueue_script_safe( $handle, $rel_path, $deps = array(), $in_footer = true ) {
$path = get_theme_file_path( $rel_path );
if ( ! $path || ! file_exists( $path ) || 0 === filesize( $path ) ) {
return false;
}
wp_enqueue_script(
$handle,
get_theme_file_uri( $rel_path ),
$deps,
filemtime( $path ),
$in_footer
);
return true;
}
function motayeb_scripts() {
wp_enqueue_style( 'motayeb-style', get_stylesheet_uri(), array(), wp_get_theme()->get( 'Version' ) );
$ver = MOTAYEB_VERSION;
// ۱. استایل اصلی تم (style.css در ریشه)
wp_enqueue_style( 'motayeb-style', get_stylesheet_uri(), array(), $ver );
// ۲. استایل Tailwind ساخته‌شده (پس از build استاتیک جایگزین Play CDN می‌شه)
motayeb_enqueue_style_safe( 'motayeb-tailwind', '/assets/css/tailwind.css', array( 'motayeb-style' ) );
// ۳. استایل ووکامرس — فقط اگه فایل واقعاً وجود داشت و خالی نبود
if ( class_exists( 'WooCommerce' ) ) {
wp_enqueue_style( 'motayeb-woocommerce', get_theme_file_uri( '/assets/css/woocommerce.css' ), array( 'motayeb-style' ), wp_get_theme()->get( 'Version' ) );
motayeb_enqueue_style_safe( 'motayeb-woocommerce', '/assets/css/woocommerce.css', array( 'motayeb-style' ) );
}
wp_enqueue_style( 'motayeb-responsive', get_theme_file_uri( '/assets/css/responsive.css' ), array( 'motayeb-style' ), wp_get_theme()->get( 'Version' ) );
wp_enqueue_script( 'motayeb-main', get_theme_file_uri( '/assets/js/main.js' ), array( 'jquery' ), wp_get_theme()->get( 'Version' ), true );
// ۴. استایل ریسپانسیو
motayeb_enqueue_style_safe( 'motayeb-responsive', '/assets/css/responsive.css', array( 'motayeb-style' ) );
// ۵. اسکریپت اصلی (بدون jQuery — فرض بر اینه که با vanilla JS نوشته شده)
$main_loaded = motayeb_enqueue_script_safe( 'motayeb-main', '/assets/js/main.js' );
// ۶. پاس دادن متغیرها به JS — با guard ووکامرس
if ( $main_loaded ) {
$localize_data = array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( MOTAYEB_NONCE_ACTION ),
'home_url' => home_url( '/' ),
);
if ( class_exists( 'WooCommerce' ) ) {
$localize_data['checkout_url'] = wc_get_checkout_url();
$localize_data['cart_url'] = wc_get_cart_url();
}
wp_localize_script( 'motayeb-main', 'motayeb_ajax', $localize_data );
}
// ۷. اسکریپت ووکامرس — فقط روی صفحات فروشگاهی و اگه فایل پر بود
if ( class_exists( 'WooCommerce' ) ) {
wp_enqueue_script( 'motayeb-woocommerce', get_theme_file_uri( '/assets/js/woocommerce.js' ), array( 'jquery', 'motayeb-main' ), wp_get_theme()->get( 'Version' ), true );
if ( is_woocommerce() || is_cart() || is_checkout() || is_account_page() ) {
motayeb_enqueue_script_safe( 'motayeb-woocommerce', '/assets/js/woocommerce.js', array( 'motayeb-main' ) );
}
}
// ۸. اسکریپت دارک‌مود
motayeb_enqueue_script_safe( 'motayeb-darkmode', '/assets/js/darkmode.js' );
// ۹. اسکریپت‌های پیش‌فرض ووکامرس روی صفحات مربوطه
if ( class_exists( 'WooCommerce' ) ) {
if ( is_product() ) { wp_enqueue_script( 'wc-single-product' ); }
if ( is_cart() ) { wp_enqueue_script( 'wc-cart' ); }
if ( is_checkout() ) { wp_enqueue_script( 'wc-checkout' ); }
}
wp_enqueue_script( 'motayeb-darkmode', get_theme_file_uri( '/assets/js/darkmode.js' ), array( 'jquery' ), wp_get_theme()->get( 'Version' ), true );
wp_localize_script( 'motayeb-main', 'motayeb_ajax', array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'motayeb_nonce' ),
'checkout_url' => wc_get_checkout_url(),
'cart_url' => wc_get_cart_url(),
) );
if ( is_product() ) { wp_enqueue_script( 'wc-single-product' ); }
if ( is_cart() ) { wp_enqueue_script( 'wc-cart' ); }
if ( is_checkout() ) { wp_enqueue_script( 'wc-checkout' ); }
}
add_action( 'wp_enqueue_scripts', 'motayeb_scripts' );
/**
* افزودن `defer` به اسکریپت‌های خودمان برای بهبود بارگذاری.
* این کار parsing HTML رو مسدود نمی‌کنه.
*/
function motayeb_script_loader_tag( $tag, $handle ) {
$defer_handles = array( 'motayeb-main', 'motayeb-darkmode', 'motayeb-woocommerce' );
if ( in_array( $handle, $defer_handles, true ) ) {
return str_replace( ' src', ' defer src', $tag );
}
return $tag;
}
add_filter( 'script_loader_tag', 'motayeb_script_loader_tag', 10, 2 );
// ============================================================
// ۳. ثبت ناحیه‌های ویجت (Widget Areas)
// ============================================================
@ -108,12 +207,14 @@ add_action( 'widgets_init', 'motayeb_widgets_init' );
// ============================================================
add_filter( 'loop_shop_columns', function() { return 4; } );
add_filter( 'loop_shop_per_page', function() { return 12; } );
function motayeb_body_classes( $classes ) {
if ( class_exists( 'WooCommerce' ) ) {
if ( is_product() ) { $classes[] = 'motayeb-product-page'; }
if ( is_shop() ) { $classes[] = 'motayeb-shop-page'; }
}
if ( isset( $_COOKIE['motayeb_darkmode'] ) && 'on' === $_COOKIE['motayeb_darkmode'] ) {
// کوکی با sanitize_key بررسی می‌شه تا فقط 'on' یا '' قبول بشه
if ( isset( $_COOKIE['motayeb_darkmode'] ) && 'on' === sanitize_key( wp_unslash( $_COOKIE['motayeb_darkmode'] ) ) ) {
$classes[] = 'dark-mode';
}
return $classes;
@ -150,46 +251,73 @@ class Motayeb_Mobile_Walker extends Walker_Nav_Menu {
}
// ============================================================
// ۶. توابع Ajax برای جستجو و دریافت محصولات
// ۶. توابع Ajax برای جستجو و دریافت محصولات — اصلاح‌شده
// ============================================================
/**
* دریافت اطلاعات یک محصول با ID.
* فقط روی محصولات منتشرشده کار می‌کنه.
*/
function motayeb_ajax_get_product_data() {
check_ajax_referer( 'motayeb_nonce', 'nonce' );
check_ajax_referer( MOTAYEB_NONCE_ACTION, 'nonce' );
if ( ! class_exists( 'WooCommerce' ) ) {
wp_send_json_error( 'ووکامرس فعال نیست' );
}
$product_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0;
if ( ! $product_id ) { wp_send_json_error( 'شناسه محصول نامعتبر است' ); }
if ( ! $product_id ) {
wp_send_json_error( 'شناسه محصول نامعتبر است' );
}
$product = wc_get_product( $product_id );
if ( ! $product ) { wp_send_json_error( 'محصول یافت نشد' ); }
if ( ! $product || 'publish' !== $product->get_status() ) {
wp_send_json_error( 'محصول یافت نشد' );
}
$image = wp_get_attachment_image_src( $product->get_image_id(), 'thumbnail' );
wp_send_json_success( array(
'id' => $product->get_id(),
'name' => $product->get_name(),
'price' => $product->get_price() ? floatval( $product->get_price() ) : 0,
'img' => $image ? $image[0] : 'default-product',
'stock' => $product->get_stock_quantity(),
'id' => $product->get_id(),
'name' => $product->get_name(),
'price' => $product->get_price() !== '' ? floatval( $product->get_price() ) : 0,
'img' => $image ? $image[0] : '',
'stock' => $product->get_stock_quantity(),
'permalink' => $product->get_permalink(),
) );
}
add_action( 'wp_ajax_get_product_data', 'motayeb_ajax_get_product_data' );
add_action( 'wp_ajax_nopriv_get_product_data', 'motayeb_ajax_get_product_data' );
/**
* جستجوی زنده محصولات از wc_get_products استفاده می‌کنه تا
* visibility ووکامرسی (محصولات hidden از کاتالوگ) رعایت بشه.
*/
function motayeb_ajax_search_products() {
check_ajax_referer( 'motayeb_nonce', 'nonce' );
$term = isset( $_GET['term'] ) ? sanitize_text_field( $_GET['term'] ) : '';
if ( strlen( $term ) < 2 ) { wp_send_json_success( array() ); }
$args = array( 'post_type' => 'product', 'posts_per_page' => 6, 's' => $term );
$query = new WP_Query( $args );
check_ajax_referer( MOTAYEB_NONCE_ACTION, 'nonce' );
if ( ! class_exists( 'WooCommerce' ) ) {
wp_send_json_error( 'ووکامرس فعال نیست' );
}
$term = isset( $_GET['term'] ) ? sanitize_text_field( wp_unslash( $_GET['term'] ) ) : '';
if ( strlen( $term ) < 2 ) {
wp_send_json_success( array() );
}
$products = wc_get_products( array(
'status' => 'publish',
'limit' => 6,
'paginate' => false,
's' => $term,
) );
$results = array();
while ( $query->have_posts() ) {
$query->the_post();
$product = wc_get_product( get_the_ID() );
$image = wp_get_attachment_image_src( get_post_thumbnail_id(), 'thumbnail' );
foreach ( $products as $product ) {
$image = wp_get_attachment_image_src( $product->get_image_id(), 'thumbnail' );
$terms = wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) );
$results[] = array(
'id' => get_the_ID(),
'name' => get_the_title(),
'price' => $product->get_price() ? floatval( $product->get_price() ) : 0,
'image' => $image ? $image[0] : '',
'category' => 'محصول',
'id' => $product->get_id(),
'name' => $product->get_name(),
'price' => $product->get_price() !== '' ? floatval( $product->get_price() ) : 0,
'image' => $image ? $image[0] : '',
'category' => ! empty( $terms ) ? $terms[0] : '',
'permalink' => $product->get_permalink(),
);
}
wp_reset_postdata();
wp_send_json_success( $results );
}
add_action( 'wp_ajax_search_products', 'motayeb_ajax_search_products' );
@ -205,7 +333,7 @@ function motayeb_add_product_metaboxes() {
add_action( 'add_meta_boxes', 'motayeb_add_product_metaboxes' );
function motayeb_ingredients_metabox_callback( $post ) {
wp_nonce_field( 'motayeb_ingredients_nonce', 'motayeb_ingredients_nonce' );
wp_nonce_field( 'motayeb_save_ingredients', 'motayeb_ingredients_nonce' );
$value = get_post_meta( $post->ID, '_ingredients', true );
echo '<p><label for="ingredients_field">متن ترکیبات را وارد کنید (می‌توانید از تگ‌های HTML ساده استفاده کنید):</label></p>';
wp_editor( $value, 'ingredients_field', array(
@ -214,7 +342,7 @@ function motayeb_ingredients_metabox_callback( $post ) {
}
function motayeb_health_benefits_metabox_callback( $post ) {
wp_nonce_field( 'motayeb_health_benefits_nonce', 'motayeb_health_benefits_nonce' );
wp_nonce_field( 'motayeb_save_health_benefits', 'motayeb_health_benefits_nonce' );
$benefits = get_post_meta( $post->ID, '_health_benefits', true );
if ( ! is_array( $benefits ) ) { $benefits = array(); }
$benefits_title = get_post_meta( $post->ID, '_health_benefits_title', true );
@ -228,20 +356,20 @@ function motayeb_health_benefits_metabox_callback( $post ) {
<p>هر آیتم شامل عنوان، توضیحات، آیکون و رنگ است. برای افزودن آیتم جدید، دکمه زیر را بزنید.</p>
<div id="motayeb-benefits-container">
<?php if ( empty( $benefits ) ) : ?>
<div class="motayeb-benefit-item" style="background:#f9f9f9;padding:15px;margin-bottom:10px;border:1px solid #ddd;border-radius:4px;">
<p><label>عنوان: <input type="text" name="benefit_title[]" value="" style="width:100%;" /></label></p>
<p><label>توضیحات: <textarea name="benefit_desc[]" rows="2" style="width:100%;"></textarea></label></p>
<p><label>آیکون: <select name="benefit_icon[]" style="width:100%;"><?php foreach ( $icon_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>'; } ?></select></label></p>
<p><label>رنگ: <select name="benefit_color[]" style="width:100%;"><?php foreach ( $color_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>'; } ?></select></label></p>
<div class="motayeb-benefit-item">
<p><label>عنوان: <input type="text" name="benefit_title[]" value="" /></label></p>
<p><label>توضیحات: <textarea name="benefit_desc[]" rows="2"></textarea></label></p>
<p><label>آیکون: <select name="benefit_icon[]"><?php foreach ( $icon_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>'; } ?></select></label></p>
<p><label>رنگ: <select name="benefit_color[]"><?php foreach ( $color_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>'; } ?></select></label></p>
<button type="button" class="button remove-benefit">حذف این آیتم</button>
</div>
<?php else : ?>
<?php foreach ( $benefits as $item ) : ?>
<div class="motayeb-benefit-item" style="background:#f9f9f9;padding:15px;margin-bottom:10px;border:1px solid #ddd;border-radius:4px;">
<p><label>عنوان: <input type="text" name="benefit_title[]" value="<?php echo esc_attr( $item['title'] ); ?>" style="width:100%;" /></label></p>
<p><label>توضیحات: <textarea name="benefit_desc[]" rows="2" style="width:100%;"><?php echo esc_textarea( $item['description'] ); ?></textarea></label></p>
<p><label>آیکون: <select name="benefit_icon[]" style="width:100%;"><?php foreach ( $icon_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '" ' . selected( $item['icon'], $value, false ) . '>' . esc_html( $label ) . '</option>'; } ?></select></label></p>
<p><label>رنگ: <select name="benefit_color[]" style="width:100%;"><?php foreach ( $color_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '" ' . selected( $item['color'], $value, false ) . '>' . esc_html( $label ) . '</option>'; } ?></select></label></p>
<div class="motayeb-benefit-item">
<p><label>عنوان: <input type="text" name="benefit_title[]" value="<?php echo esc_attr( $item['title'] ); ?>" /></label></p>
<p><label>توضیحات: <textarea name="benefit_desc[]" rows="2"><?php echo esc_textarea( $item['description'] ); ?></textarea></label></p>
<p><label>آیکون: <select name="benefit_icon[]"><?php foreach ( $icon_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '" ' . selected( $item['icon'], $value, false ) . '>' . esc_html( $label ) . '</option>'; } ?></select></label></p>
<p><label>رنگ: <select name="benefit_color[]"><?php foreach ( $color_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '" ' . selected( $item['color'], $value, false ) . '>' . esc_html( $label ) . '</option>'; } ?></select></label></p>
<button type="button" class="button remove-benefit">حذف این آیتم</button>
</div>
<?php endforeach; ?>
@ -249,17 +377,21 @@ function motayeb_health_benefits_metabox_callback( $post ) {
</div>
<button type="button" id="add-benefit" class="button button-primary">افزودن آیتم جدید</button>
</div>
<style>.motayeb-benefit-item { background: #f9f9f9; padding: 15px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 4px; } .motayeb-benefit-item input, .motayeb-benefit-item textarea, .motayeb-benefit-item select { width: 100%; } .motayeb-benefit-item select { max-width: 300px; }</style>
<style>
.motayeb-benefit-item { background: #f9f9f9; padding: 15px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 4px; }
.motayeb-benefit-item input[type="text"], .motayeb-benefit-item textarea { width: 100%; }
.motayeb-benefit-item select { max-width: 300px; }
</style>
<script>
jQuery(document).ready(function($) {
$('#add-benefit').on('click', function() {
var html = `<div class="motayeb-benefit-item" style="background:#f9f9f9;padding:15px;margin-bottom:10px;border:1px solid #ddd;border-radius:4px;">
<p><label>عنوان: <input type="text" name="benefit_title[]" value="" style="width:100%;" /></label></p>
<p><label>توضیحات: <textarea name="benefit_desc[]" rows="2" style="width:100%;"></textarea></label></p>
<p><label>آیکون: <select name="benefit_icon[]" style="width:100%;"><?php foreach ( $icon_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>'; } ?></select></label></p>
<p><label>رنگ: <select name="benefit_color[]" style="width:100%;"><?php foreach ( $color_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>'; } ?></select></label></p>
<button type="button" class="button remove-benefit">حذف این آیتم</button>
</div>`;
var html = '<div class="motayeb-benefit-item">' +
'<p><label>عنوان: <input type="text" name="benefit_title[]" value="" /></label></p>' +
'<p><label>توضیحات: <textarea name="benefit_desc[]" rows="2"></textarea></label></p>' +
'<p><label>آیکون: <select name="benefit_icon[]"><?php foreach ( $icon_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>'; } ?></select></label></p>' +
'<p><label>رنگ: <select name="benefit_color[]"><?php foreach ( $color_list as $value => $label ) { echo '<option value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>'; } ?></select></label></p>' +
'<button type="button" class="button remove-benefit">حذف این آیتم</button>' +
'</div>';
$('#motayeb-benefits-container').append(html);
});
$(document).on('click', '.remove-benefit', function() {
@ -270,18 +402,36 @@ function motayeb_health_benefits_metabox_callback( $post ) {
<?php
}
/**
* ذخیره متاباکس‌ها با بررسی revision، nonce مجزا برای هر اکشن، و unslash.
*/
function motayeb_save_product_metaboxes( $post_id ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; }
if ( wp_is_post_revision( $post_id ) ) { return; }
if ( ! current_user_can( 'edit_post', $post_id ) ) { return; }
if ( isset( $_POST['motayeb_ingredients_nonce'] ) && wp_verify_nonce( $_POST['motayeb_ingredients_nonce'], 'motayeb_ingredients_nonce' ) && isset( $_POST['ingredients_field'] ) ) {
update_post_meta( $post_id, '_ingredients', wp_kses_post( $_POST['ingredients_field'] ) );
// ذخیره ترکیبات
if (
isset( $_POST['motayeb_ingredients_nonce'] )
&& wp_verify_nonce( $_POST['motayeb_ingredients_nonce'], 'motayeb_save_ingredients' )
&& isset( $_POST['ingredients_field'] )
) {
update_post_meta( $post_id, '_ingredients', wp_kses_post( wp_unslash( $_POST['ingredients_field'] ) ) );
}
if ( isset( $_POST['motayeb_health_benefits_nonce'] ) && wp_verify_nonce( $_POST['motayeb_health_benefits_nonce'], 'motayeb_health_benefits_nonce' ) ) {
if ( isset( $_POST['benefits_title_field'] ) ) { update_post_meta( $post_id, '_health_benefits_title', sanitize_text_field( $_POST['benefits_title_field'] ) ); }
$titles = isset( $_POST['benefit_title'] ) ? array_map( 'sanitize_text_field', $_POST['benefit_title'] ) : array();
$descs = isset( $_POST['benefit_desc'] ) ? array_map( 'sanitize_textarea_field', $_POST['benefit_desc'] ) : array();
$icons = isset( $_POST['benefit_icon'] ) ? array_map( 'sanitize_text_field', $_POST['benefit_icon'] ) : array();
$colors = isset( $_POST['benefit_color'] ) ? array_map( 'sanitize_text_field', $_POST['benefit_color'] ) : array();
// ذخیره خواص سلامت
if (
isset( $_POST['motayeb_health_benefits_nonce'] )
&& wp_verify_nonce( $_POST['motayeb_health_benefits_nonce'], 'motayeb_save_health_benefits' )
) {
if ( isset( $_POST['benefits_title_field'] ) ) {
update_post_meta( $post_id, '_health_benefits_title', sanitize_text_field( wp_unslash( $_POST['benefits_title_field'] ) ) );
}
$titles = isset( $_POST['benefit_title'] ) ? array_map( 'sanitize_text_field', wp_unslash( (array) $_POST['benefit_title'] ) ) : array();
$descs = isset( $_POST['benefit_desc'] ) ? array_map( 'sanitize_textarea_field', wp_unslash( (array) $_POST['benefit_desc'] ) ) : array();
$icons = isset( $_POST['benefit_icon'] ) ? array_map( 'sanitize_key', wp_unslash( (array) $_POST['benefit_icon'] ) ) : array();
$colors = isset( $_POST['benefit_color'] ) ? array_map( 'sanitize_key', wp_unslash( (array) $_POST['benefit_color'] ) ) : array();
$benefits = array();
foreach ( $titles as $index => $title ) {
if ( ! empty( $title ) && isset( $descs[ $index ] ) ) {
@ -297,20 +447,23 @@ function motayeb_save_product_metaboxes( $post_id ) {
}
}
add_action( 'save_post_product', 'motayeb_save_product_metaboxes' );
add_action( 'comment_post', 'motayeb_save_rating_field' );
// ============================================================
// ۸. ذخیره امتیاز دیدگاه‌ها — با اعتبارسنجی محدوده ۱ تا ۵
// ============================================================
function motayeb_save_rating_field( $comment_id ) {
if ( isset( $_POST['rating'] ) && '' !== $_POST['rating'] ) {
add_comment_meta( $comment_id, 'rating', intval( $_POST['rating'] ) );
if ( isset( $_POST['rating'] ) ) {
$rating = intval( $_POST['rating'] );
if ( $rating >= 1 && $rating <= 5 ) {
update_comment_meta( $comment_id, 'rating', $rating );
}
}
}
add_action( 'comment_post', 'motayeb_save_rating_field' );
// ============================================================
// ۸. تنظیمات تم مطیب (با استفاده از Settings API) - بخش جدید
// ۹. تنظیمات تم مطیب (با استفاده از Settings API)
// ============================================================
/**
* ۱. ایجاد صفحه تنظیمات در پنل ادمین
*/
function motayeb_add_admin_menu() {
add_options_page(
'تنظیمات تم مطیب',
@ -322,9 +475,6 @@ function motayeb_add_admin_menu() {
}
add_action( 'admin_menu', 'motayeb_add_admin_menu' );
/**
* ۲. بارگذاری اسکریپت‌های مدیا آپلودر در صفحه تنظیمات
*/
function motayeb_admin_scripts( $hook ) {
if ( 'settings_page_motayeb_settings' !== $hook ) {
return;
@ -334,11 +484,7 @@ function motayeb_admin_scripts( $hook ) {
}
add_action( 'admin_enqueue_scripts', 'motayeb_admin_scripts' );
/**
* ۳. ثبت تنظیمات، بخش‌ها و فیلدها
*/
function motayeb_register_settings() {
// ثبت یک بخش
add_settings_section(
'motayeb_shop_section',
'تنظیمات صفحه فروشگاه',
@ -346,7 +492,6 @@ function motayeb_register_settings() {
'motayeb_settings'
);
// ۱. فیلد بنر فروشگاه
add_settings_field(
'motayeb_shop_banner',
'عکس بنر فروشگاه',
@ -356,9 +501,7 @@ function motayeb_register_settings() {
);
register_setting( 'motayeb_settings', 'motayeb_shop_banner', 'esc_url_raw' );
// ۲. فیلدهای ویژگی‌ها (آیکون و متن)
for ( $i = 1; $i <= 4; $i++ ) {
// آیکون
add_settings_field(
'motayeb_feature_' . $i . '_icon',
'آیکون ویژگی ' . $i . ' (مثلاً: lucide:leaf)',
@ -369,7 +512,6 @@ function motayeb_register_settings() {
);
register_setting( 'motayeb_settings', 'motayeb_feature_' . $i . '_icon', 'sanitize_text_field' );
// متن
add_settings_field(
'motayeb_feature_' . $i . '_text',
'متن ویژگی ' . $i,
@ -383,9 +525,6 @@ function motayeb_register_settings() {
}
add_action( 'admin_init', 'motayeb_register_settings' );
/**
* ۴. توابع بازگشتی (Callback) برای نمایش فیلدها در ادمین
*/
function motayeb_shop_section_callback() {
echo '<p>تنظیمات بنر و ویژگی‌های نمایش داده شده در صفحه فروشگاه را در اینجا مدیریت کنید.</p>';
}
@ -397,7 +536,7 @@ function motayeb_shop_banner_callback() {
<input type="text" id="motayeb_shop_banner" name="motayeb_shop_banner" value="<?php echo esc_attr( $value ); ?>" style="width: 70%;" class="regular-text" placeholder="آدرس تصویر را وارد کنید یا انتخاب کنید">
<button type="button" class="button motayeb-upload-btn" data-target="motayeb_shop_banner">انتخاب تصویر</button>
<br><br>
<img src="<?php echo esc_attr( $value ); ?>" style="max-width: 300px; height: auto; display: <?php echo empty($value) ? 'none' : 'block'; ?>;" id="preview-motayeb_shop_banner" />
<img src="<?php echo esc_attr( $value ); ?>" style="max-width: 300px; height: auto; display: <?php echo empty( $value ) ? 'none' : 'block'; ?>;" id="preview-motayeb_shop_banner" alt="" />
</div>
<script>
jQuery(document).ready(function($){
@ -421,15 +560,12 @@ function motayeb_shop_banner_callback() {
}
function motayeb_text_field_callback( $args ) {
$id = $args['id'];
$id = $args['id'];
$default = $args['default'];
$value = get_option( $id, $default );
$value = get_option( $id, $default );
echo '<input type="text" id="' . esc_attr( $id ) . '" name="' . esc_attr( $id ) . '" value="' . esc_attr( $value ) . '" style="width: 100%; max-width: 400px;" />';
}
/**
* ۵. نمایش صفحه تنظیمات در ادمین
*/
function motayeb_settings_page_callback() {
?>
<div class="wrap">
@ -451,36 +587,278 @@ function motayeb_settings_page_callback() {
add_filter( 'woocommerce_enqueue_styles', '__return_empty_array' );
// ============================================================
// ۹. نمایش ستاره‌های امتیاز (تابع کمکی برای صفحه محصول)
// ۱۱. نمایش ستاره‌های امتیاز (تابع کمکی برای صفحه محصول)
// ============================================================
function motayeb_get_rating_html( $rating, $count = 0 ) {
if ( $rating <= 0 ) {
return '<span class="text-xs text-cream-400 dark:text-dark-muted">بدون امتیاز</span>';
}
$full_stars = floor( $rating );
$half_star = ( $rating - $full_stars ) >= 0.5 ? 1 : 0;
$rating = floatval( $rating );
$full_stars = floor( $rating );
$half_star = ( $rating - $full_stars ) >= 0.5 ? 1 : 0;
$empty_stars = 5 - $full_stars - $half_star;
$html = '<span class="flex items-center gap-0.5 text-honey-400 text-sm">';
$html = '<span class="flex items-center gap-0.5 text-honey-400 text-sm" aria-label="' . esc_attr( sprintf( __( 'امتیاز %s از ۵', 'motayeb' ), number_format_i18n( $rating, 1 ) ) ) . '">';
for ( $i = 0; $i < $full_stars; $i++ ) {
$html .= '★';
}
if ( $half_star ) {
$html .= '<span class="relative">★<span class="absolute inset-0 overflow-hidden" style="width:50%;color:#D4A574;">★</span></span>';
}
for ( $i = 0; $i < $empty_stars; $i++ ) {
$html .= '<span class="text-cream-300 dark:text-dark-muted">★</span>';
}
if ( $count > 0 ) {
$html .= ' <span class="text-xs text-cream-400 dark:text-dark-muted mr-1">(' . esc_html( $count ) . ')</span>';
$html .= ' <span class="text-xs text-cream-400 dark:text-dark-muted mr-1">(' . esc_html( number_format_i18n( $count ) ) . ')</span>';
}
$html .= '</span>';
return $html;
}
// ============================================================
// ۱۲. پاک‌سازی transient cache هنگام ذخیره محصول/دسته‌بندی
// ============================================================
function motayeb_clear_home_transients( $post_id ) {
if ( 'product' === get_post_type( $post_id ) ) {
delete_transient( 'motayeb_home_bestsellers' );
// پاک کردن کش شمارش دسته‌بندی‌ها
$terms = wp_get_post_terms( $post_id, 'product_cat', array( 'fields' => 'ids' ) );
if ( ! is_wp_error( $terms ) ) {
foreach ( $terms as $term_id ) {
delete_transient( 'motayeb_cat_count_' . $term_id );
}
}
}
}
add_action( 'save_post_product', 'motayeb_clear_home_transients' );
add_action( 'woocommerce_product_set_stock_status', 'motayeb_clear_home_transients' );
// پاک‌سازی هنگام ویرایش دسته‌بندی
function motayeb_clear_category_transients( $term_id, $taxonomy = '' ) {
if ( 'product_cat' === $taxonomy || empty( $taxonomy ) ) {
delete_transient( 'motayeb_home_categories' );
delete_transient( 'motayeb_cat_count_' . $term_id );
}
}
add_action( 'edited_product_cat', 'motayeb_clear_category_transients', 10, 2 );
add_action( 'created_product_cat', 'motayeb_clear_category_transients', 10, 2 );
add_action( 'delete_product_cat', 'motayeb_clear_category_transients', 10, 2 );
// اضافه کردن فیلد آیکون به صفحه افزودن/ویرایش دسته‌بندی محصول
function motayeb_add_category_icon_field( $term = null ) {
$icon = $term ? get_term_meta( $term->term_id, 'motayeb_icon', true ) : '';
$color = $term ? get_term_meta( $term->term_id, 'motayeb_color', true ) : '';
?>
<div class="form-field">
<label for="motayeb_icon">آیکون (Iconify)</label>
<input type="text" name="motayeb_icon" id="motayeb_icon" value="<?php echo esc_attr( $icon ); ?>" placeholder="lucide:hexagon">
<p>نام آیکون از سایت <a href="https://icon-sets.iconify.design" target="_blank">Iconify</a></p>
</div>
<div class="form-field">
<label for="motayeb_color">کلاس رنگ Tailwind</label>
<input type="text" name="motayeb_color" id="motayeb_color" value="<?php echo esc_attr( $color ); ?>" placeholder="bg-honey-50 dark:bg-honey-900/20 text-honey-400">
</div>
<?php
}
add_action( 'product_cat_add_form_fields', 'motayeb_add_category_icon_field' );
add_action( 'product_cat_edit_form_fields', 'motayeb_add_category_icon_field' );
function motayeb_save_category_icon( $term_id ) {
if ( isset( $_POST['motayeb_icon'] ) ) {
update_term_meta( $term_id, 'motayeb_icon', sanitize_text_field( $_POST['motayeb_icon'] ) );
}
if ( isset( $_POST['motayeb_color'] ) ) {
update_term_meta( $term_id, 'motayeb_color', sanitize_text_field( $_POST['motayeb_color'] ) );
}
}
add_action( 'created_product_cat', 'motayeb_save_category_icon' );
add_action( 'edited_product_cat', 'motayeb_save_category_icon' );
// ============================================================
// ۹.۱ افزودن فیلدهای تنظیمات تماس و شبکه‌های اجتماعی
// ============================================================
function motayeb_register_contact_settings() {
// بخش تنظیمات تماس
add_settings_section(
'motayeb_contact_section',
'اطلاعات تماس',
'motayeb_contact_section_callback',
'motayeb_settings'
);
// فیلدهای تماس
$contact_fields = array(
'motayeb_contact_phone' => 'شماره تماس',
'motayeb_contact_email' => 'ایمیل',
'motayeb_contact_address' => 'آدرس',
);
foreach ( $contact_fields as $id => $label ) {
add_settings_field(
$id,
$label,
'motayeb_text_field_callback',
'motayeb_settings',
'motayeb_contact_section',
array( 'id' => $id, 'default' => '' )
);
register_setting( 'motayeb_settings', $id, 'sanitize_text_field' );
}
// بخش شبکه‌های اجتماعی
add_settings_section(
'motayeb_social_section',
'شبکه‌های اجتماعی',
'motayeb_social_section_callback',
'motayeb_settings'
);
$social_fields = array(
'motayeb_social_instagram' => 'لینک اینستاگرام',
'motayeb_social_telegram' => 'لینک تلگرام',
'motayeb_social_twitter' => 'لینک توییتر',
);
foreach ( $social_fields as $id => $label ) {
add_settings_field(
$id,
$label,
'motayeb_text_field_callback',
'motayeb_settings',
'motayeb_social_section',
array( 'id' => $id, 'default' => '' )
);
register_setting( 'motayeb_settings', $id, 'esc_url_raw' );
}
// بخش نمادهای اعتماد
add_settings_section(
'motayeb_badges_section',
'نمادهای اعتماد',
'motayeb_badges_section_callback',
'motayeb_settings'
);
$badge_fields = array(
'motayeb_enamad_url' => 'لینک نماد اعتماد الکترونیکی',
'motayeb_samandehi_url'=> 'لینک ساماندهی',
);
foreach ( $badge_fields as $id => $label ) {
add_settings_field(
$id,
$label,
'motayeb_text_field_callback',
'motayeb_settings',
'motayeb_badges_section',
array( 'id' => $id, 'default' => '' )
);
register_setting( 'motayeb_settings', $id, 'esc_url_raw' );
}
// بخش انتخاب صفحات خدمات مشتریان
add_settings_section(
'motayeb_pages_section',
'صفحات خدمات مشتریان',
'motayeb_pages_section_callback',
'motayeb_settings'
);
$page_fields = array(
'motayeb_page_faq' => 'صفحه سوالات متداول',
'motayeb_page_return' => 'صفحه شرایط بازگشت',
'motayeb_page_shipping' => 'صفحه نحوه ارسال',
'motayeb_page_privacy' => 'صفحه حریم خصوصی',
'motayeb_page_terms' => 'صفحه قوانین و مقررات',
'motayeb_page_about' => 'صفحه درباره ما',
'motayeb_page_contact' => 'صفحه تماس با ما',
);
foreach ( $page_fields as $id => $label ) {
add_settings_field(
$id,
$label,
'motayeb_page_select_callback',
'motayeb_settings',
'motayeb_pages_section',
array( 'id' => $id )
);
register_setting( 'motayeb_settings', $id, 'intval' );
}
}
add_action( 'admin_init', 'motayeb_register_contact_settings' );
function motayeb_contact_section_callback() {
echo '<p>اطلاعاتی که در فوتر سایت نمایش داده می‌شود را وارد کنید.</p>';
}
function motayeb_social_section_callback() {
echo '<p>لینک شبکه‌های اجتماعی فروشگاه خود را وارد کنید. برای مخفی کردن یک شبکه، فیلد آن را خالی بگذارید.</p>';
}
function motayeb_badges_section_callback() {
echo '<p>لینک صفحه نماد اعتماد و ساماندهی خود را وارد کنید. اگه خالی باشند، نمایش داده نمی‌شوند.</p>';
}
function motayeb_pages_section_callback() {
echo '<p>صفحات وردپرس که برای خدمات مشتریان استفاده می‌شوند را انتخاب کنید.</p>';
}
// Callback برای انتخاب صفحه
function motayeb_page_select_callback( $args ) {
$id = $args['id'];
$current = get_option( $id, 0 );
wp_dropdown_pages( array(
'name' => $id,
'id' => $id,
'selected' => $current,
'show_option_none' => '— انتخاب کنید —',
'option_none_value'=> 0,
) );
}
// ============================================================
// ۱۳. شمارش محصولات در دسته شامل زیردسته‌ها (با cache)
// ============================================================
function motayeb_count_category_products( $term_id ) {
$transient_key = 'motayeb_cat_count_' . $term_id;
$count = get_transient( $transient_key );
if ( false === $count ) {
$terms = get_terms( array(
'taxonomy' => 'product_cat',
'child_of' => $term_id,
'hide_empty' => false,
'fields' => 'ids',
) );
$all_ids = array_merge( array( $term_id ), ( is_array( $terms ) ? $terms : array() ) );
$query = new WP_Query( array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids',
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'field' => 'term_id',
'terms' => $all_ids,
),
),
) );
$count = $query->found_posts;
wp_reset_postdata();
set_transient( $transient_key, $count, DAY_IN_SECONDS );
}
return $count;
}
// عدد فارسی برای شمارش
function motayeb_fa_count( $n ) {
return number_format_i18n( intval( $n ) );
}

View File

@ -3,66 +3,78 @@
* هدر سایت - مطابق با UI Kolli.html
*
* @package Motayeb
* @version 1.0.1
*
* تغییرات:
* - حذف Tailwind Play CDN (رفع ارور CSP eval + افزایش سرعت)
* - حذف بلوک <style> تکراری
* - یکسان‌سازی منبع فونت Vazirmatn (محلی، بدون CDN گوگل)
* - انتقال پیکربندی Tailwind به tailwind.config.js
* - بارگذاری non-blocking برای FontAwesome و Iconify
* - پیش‌بارگذاری فونت‌ها با preconnect و preload
*/
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?> class="scroll-smooth">
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<?php wp_head(); ?>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Iconify -->
<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
forest: { 50:'#e8f5ec', 100:'#c6e6cf', 200:'#9ed0ae', 300:'#6fb88a', 400:'#4a9a6b', 500:'#2D5F3F', 600:'#254d34', 700:'#1c3b28', 800:'#13291c', 900:'#0a1710' },
cream: { 50:'#FDFBF7', 100:'#F5F0E8', 200:'#EBE3D5', 300:'#DDD2BF', 400:'#C4B49A', 500:'#A99478' },
honey: { 50:'#FDF5ED', 100:'#F9E6D0', 200:'#F2CC9E', 300:'#E8AC6A', 400:'#D4A574', 500:'#C08B52', 600:'#A07040', 700:'#7D5630', 800:'#5A3D20', 900:'#3A2612' },
earth: { 50:'#F5EDE7', 100:'#E8D5C7', 200:'#D4B5A0', 300:'#BF9276', 400:'#8B5A3C', 500:'#734A30', 600:'#5C3B25', 700:'#452C1B', 800:'#2E1D12', 900:'#170E09' },
dark: { bg:'#1A1D23', card:'#22262E', border:'#2E333D', text:'#E8E6E3', muted:'#9CA3AF' }
},
fontFamily: { vazir: ['Vazirmatn', 'sans-serif'], inter: ['Inter', 'sans-serif'] },
animation: {
'fade-in': 'fadeIn 0.6s ease-out forwards',
'slide-up': 'slideUp 0.6s ease-out forwards',
'slide-right': 'slideRight 0.5s ease-out forwards',
'float': 'float 3s ease-in-out infinite',
'pulse-soft': 'pulseSoft 2s ease-in-out infinite',
},
keyframes: {
fadeIn: { '0%': { opacity:'0' }, '100%': { opacity:'1' } },
slideUp: { '0%': { opacity:'0', transform:'translateY(30px)' }, '100%': { opacity:'1', transform:'translateY(0)' } },
slideRight: { '0%': { opacity:'0', transform:'translateX(-20px)' }, '100%': { opacity:'1', transform:'translateX(0)' } },
float: { '0%,100%': { transform:'translateY(0)' }, '50%': { transform:'translateY(-10px)' } },
pulseSoft: { '0%,100%': { opacity:'1' }, '50%': { opacity:'0.7' } },
}
}
<meta name="theme-color" content="#2D5F3F">
<!-- جلوگیری از FOUC دارک مود قبل از لود CSS اجرا می‌شه -->
<script>
(function() {
var match = document.cookie.match(/(?:^|;\s*)motayeb_darkmode=([^;]+)/);
if (match && decodeURIComponent(match[1]) === 'on') {
document.documentElement.classList.add('dark');
}
}
</script>
})();
</script>
<?php wp_head(); ?>
<!-- ============================================================
پیش‌بارگذاری فونت‌ها (Vazirmatn محلی)
============================================================ -->
<?php
$font_regular = get_theme_file_uri( '/assets/fonts/Vazirmatn-Regular.woff2' );
$font_bold = get_theme_file_uri( '/assets/fonts/Vazirmatn-Bold.woff2' );
?>
<link rel="preload" href="<?php echo esc_url( $font_regular ); ?>" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="<?php echo esc_url( $font_bold ); ?>" as="font" type="font/woff2" crossorigin>
<!-- ============================================================
Iconify non-blocking (فقط برای آیکون‌های UI)
توصیه: در آینده به SVG sprite محلی تبدیل شود
============================================================ -->
<link rel="preconnect" href="https://code.iconify.design">
<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js" defer></script>
<!-- ============================================================
FontAwesome non-blocking (فقط برای ستاره‌های امتیاز ووکامرس)
نکته: در آینده با SVG جایگزین شود تا حذف بشه
============================================================ -->
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
media="print"
onload="this.media='all'">
<noscript>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</noscript>
<!-- ============================================================
استایل‌های کمکی که Tailwind پوشش نمی‌ده
(الگوهای پس‌زمینه، اسکرول‌بار، انیمیشن‌های اختصاصی)
این بلاک سبک است و اجازه می‌ده sync لود بشه تا FOUC نباشه
============================================================ -->
<style>
* { font-family: 'Vazirmatn', sans-serif; }
.font-inter { font-family: 'Inter', sans-serif; }
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: #F5F0E8; }
::-webkit-scrollbar-thumb { background: #D4A574; border-radius: 3px; }
.dark ::-webkit-scrollbar-track { background: #1A1D23; }
.dark ::-webkit-scrollbar-thumb { background: #2D5F3F; }
/* الگوی لانه زنبوری */
.honeycomb-bg {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='56' height='100'%3E%3Cpath d='M28 66L0 50L0 16L28 0L56 16L56 50L28 66L28 100' fill='none' stroke='%23D4A574' stroke-width='0.5' opacity='0.1'/%3E%3Cpath d='M28 0L28 34L0 50L0 84L28 100L56 84L56 50L28 34' fill='none' stroke='%23D4A574' stroke-width='0.5' opacity='0.1'/%3E%3C/svg%3E");
}
/* الگوی برگ */
.leaf-pattern::before {
content: '';
position: absolute;
@ -70,171 +82,46 @@
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60' viewBox='0 0 60 60'%3E%3Cpath d='M30 5C30 5 45 20 45 35C45 43.28 38.28 50 30 50C21.72 50 15 43.28 15 35C15 20 30 5 30 5Z' fill='none' stroke='%232D5F3F' stroke-width='0.5' opacity='0.05'/%3E%3C/svg%3E");
pointer-events: none;
}
.line-clamp-2 { display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.product-card:hover .product-img { transform: scale(1.08); }
.product-card:hover .quick-actions { opacity:1; transform:translateY(0); }
.quick-actions { opacity:0; transform:translateY(10px); transition: all 0.3s ease; }
.product-img { transition: transform 0.5s ease; }
.tab-active { color: #2D5F3F; border-color: #2D5F3F; }
.dark .tab-active { color: #6fb88a; border-color: #6fb88a; }
.nav-link::after { content:''; display:block; width:0; height:2px; background:#2D5F3F; transition:width 0.3s ease; }
.dark .nav-link::after { background:#6fb88a; }
.nav-link:hover::after { width:100%; }
.toast { animation: slideUp 0.4s ease-out forwards; }
.bottom-nav { padding-bottom: env(safe-area-inset-bottom, 0); }
.checkout-step.active { background: #2D5F3F; color: white; }
.dark .checkout-step.active { background: #6fb88a; color: #1A1D23; }
.gallery-thumb.active { border-color: #2D5F3F; }
.dark .gallery-thumb.active { border-color: #6fb88a; }
<style>
* { font-family: 'Vazirmatn', sans-serif; }
.font-inter { font-family: 'Inter', sans-serif; }
/* اسکرول‌بار سفارشی */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: #F5F0E8; }
::-webkit-scrollbar-thumb { background: #D4A574; border-radius: 3px; }
.dark ::-webkit-scrollbar-track { background: #1A1D23; }
.dark ::-webkit-scrollbar-thumb { background: #2D5F3F; }
.honeycomb-bg {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='56' height='100'%3E%3Cpath d='M28 66L0 50L0 16L28 0L56 16L56 50L28 66L28 100' fill='none' stroke='%23D4A574' stroke-width='0.5' opacity='0.1'/%3E%3Cpath d='M28 0L28 34L0 50L0 84L28 100L56 84L56 50L28 34' fill='none' stroke='%23D4A574' stroke-width='0.5' opacity='0.1'/%3E%3C/svg%3E");
}
.leaf-pattern::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60' viewBox='0 0 60 60'%3E%3Cpath d='M30 5C30 5 45 20 45 35C45 43.28 38.28 50 30 50C21.72 50 15 43.28 15 35C15 20 30 5 30 5Z' fill='none' stroke='%232D5F3F' stroke-width='0.5' opacity='0.05'/%3E%3C/svg%3E");
pointer-events: none;
}
.line-clamp-2 { display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
/* کارت محصول — انیمیشن hover */
.product-card:hover .product-img { transform: scale(1.08); }
.product-card:hover .quick-actions { opacity:1; transform:translateY(0); }
.quick-actions { opacity:0; transform:translateY(10px); transition: all 0.3s ease; }
.product-card:hover .quick-actions { opacity: 1; transform: translateY(0); }
.quick-actions { opacity: 0; transform: translateY(10px); transition: all 0.3s ease; }
.product-img { transition: transform 0.5s ease; }
/* تب فعال */
.tab-active { color: #2D5F3F; border-color: #2D5F3F; }
.dark .tab-active { color: #6fb88a; border-color: #6fb88a; }
.nav-link::after { content:''; display:block; width:0; height:2px; background:#2D5F3F; transition:width 0.3s ease; }
.dark .nav-link::after { background:#6fb88a; }
.nav-link:hover::after { width:100%; }
.toast { animation: slideUp 0.4s ease-out forwards; }
/* خط زیر منو */
.nav-link::after { content: ''; display: block; width: 0; height: 2px; background: #2D5F3F; transition: width 0.3s ease; }
.dark .nav-link::after { background: #6fb88a; }
.nav-link:hover::after { width: 100%; }
/* Toast و ناوبری پایین */
.toast { animation: motayebSlideUp 0.4s ease-out forwards; }
.bottom-nav { padding-bottom: env(safe-area-inset-bottom, 0); }
/* گام‌های checkout و گالری */
.checkout-step.active { background: #2D5F3F; color: white; }
.dark .checkout-step.active { background: #6fb88a; color: #1A1D23; }
.gallery-thumb.active { border-color: #2D5F3F; }
.dark .gallery-thumb.active { border-color: #6fb88a; }
/* ===== استایل‌دهی ستاره‌های ووکامرس با FontAwesome ===== */
.comment-form-rating p.stars {
direction: rtl;
margin: 10px 0;
display: inline-block;
}
.comment-form-rating p.stars a {
font-size: 0 !important;
display: inline-block;
width: 1.8rem;
height: 1.8rem;
position: relative;
text-decoration: none;
margin-left: 5px;
}
.comment-form-rating p.stars a::before {
font-family: 'Font Awesome 6 Free';
font-weight: 400;
content: "\f005";
font-size: 1.8rem;
position: absolute;
top: 0;
right: 0;
color: #ccc;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.comment-form-rating p.stars a:hover::before,
.comment-form-rating p.stars a.active::before,
.comment-form-rating p.stars a.selected::before {
font-weight: 900;
color: #2D5F3F;
}
/* رفع قطعی باریک شدن کارت‌ها */
.product-card { width: 100% !important; min-width: 100% !important; }
/* ============================================================ */
/* نادیده گرفتن کامل استایل‌های ووکامرس با اولویت فوق‌العاده بالا */
/* ============================================================ */
.woocommerce form .form-row .input-text,
.woocommerce form .form-row textarea,
.woocommerce form .form-row select,
.select2-container .select2-selection--single,
#add_payment_method #payment ul.payment_methods li,
.woocommerce-checkout #payment ul.payment_methods li {
background-color: #FDFBF7 !important; /* کرم ۵۰ */
border: 1px solid #EBE3D5 !important; /* کرم ۲۰۰ */
border-radius: 12px !important;
color: #734A30 !important;
box-shadow: none !important;
outline: none !important;
transition: 0.3s ease !important;
}
.woocommerce form .form-row .input-text:focus,
.woocommerce form .form-row textarea:focus,
.woocommerce form .form-row select:focus,
.select2-container .select2-selection--single:focus {
border-color: #2D5F3F !important;
background-color: #ffffff !important;
}
/* دارک مود */
.dark .woocommerce form .form-row .input-text,
.dark .woocommerce form .form-row textarea,
.dark .woocommerce form .form-row select,
.dark .select2-container .select2-selection--single,
.dark #add_payment_method #payment ul.payment_methods li,
.dark .woocommerce-checkout #payment ul.payment_methods li {
background-color: #1A1D23 !important;
border-color: #2E333D !important;
color: #E8E6E3 !important;
}
.dark .woocommerce form .form-row .input-text:focus,
.dark .woocommerce form .form-row textarea:focus,
.dark .woocommerce form .form-row select:focus,
.dark .select2-container .select2-selection--single:focus {
border-color: #6fb88a !important;
}
/* برچسب‌ها */
.woocommerce form .form-row label {
display: block !important;
font-weight: 700 !important;
font-size: 14px !important;
margin-bottom: 8px !important;
color: #734A30 !important;
}
/* رادیو باتن‌های درگاه پرداخت */
#add_payment_method #payment ul.payment_methods li input[type="radio"],
.woocommerce-checkout #payment ul.payment_methods li input[type="radio"] {
accent-color: #2D5F3F !important;
}
/* جدول خلاصه سفارش */
.woocommerce-checkout-review-order table.shop_table th,
.woocommerce-checkout-review-order table.shop_table td {
border-color: #EBE3D5 !important;
}
/* ============================================================ */
/* رفع قطعی باریک شدن کارت‌ها در پرفروش‌ترین‌ها و لیست محصولات */
/* ============================================================ */
.product-card {
width: 100% !important;
min-width: 100% !important;
@keyframes motayebSlideUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body <?php body_class( 'bg-cream-100 text-earth-400 dark:bg-dark-bg dark:text-dark-text transition-colors duration-300' ); ?>>
<?php wp_body_open(); ?>
@ -248,10 +135,10 @@
<div class="bg-white dark:bg-dark-card rounded-2xl shadow-2xl p-6">
<div class="flex items-center gap-3 border-b border-cream-200 dark:border-dark-border pb-4">
<iconify-icon icon="lucide:search" width="22" class="text-forest-500 dark:text-forest-300"></iconify-icon>
<input id="search-input" type="text" placeholder="جستجو در محصولات..."
<input id="search-input" type="text" placeholder="جستجو در محصولات..."
class="flex-1 bg-transparent text-lg outline-none placeholder:text-cream-400 dark:placeholder:text-dark-muted"
oninput="handleSearch(this.value)">
<button onclick="closeSearch()" class="text-cream-400 hover:text-earth-400 dark:text-dark-muted">
<button onclick="closeSearch()" class="text-cream-400 hover:text-earth-400 dark:text-dark-muted" aria-label="بستن">
<iconify-icon icon="lucide:x" width="22"></iconify-icon>
</button>
</div>
@ -277,7 +164,7 @@
<div class="flex flex-col h-full">
<div class="flex items-center justify-between p-5 border-b border-cream-200 dark:border-dark-border">
<h3 class="text-lg font-bold">سبد خرید (<span id="cart-drawer-count">0</span>)</h3>
<button onclick="closeCart()" class="w-8 h-8 flex items-center justify-center rounded-full hover:bg-cream-100 dark:hover:bg-dark-bg transition">
<button onclick="closeCart()" class="w-8 h-8 flex items-center justify-center rounded-full hover:bg-cream-100 dark:hover:bg-dark-bg transition" aria-label="بستن">
<iconify-icon icon="lucide:x" width="20"></iconify-icon>
</button>
</div>
@ -315,7 +202,7 @@
</div>
<span class="font-bold text-lg">مطیب</span>
</div>
<button onclick="closeMobileMenu()" class="w-8 h-8 flex items-center justify-center rounded-full hover:bg-cream-100 dark:hover:bg-dark-bg transition">
<button onclick="closeMobileMenu()" class="w-8 h-8 flex items-center justify-center rounded-full hover:bg-cream-100 dark:hover:bg-dark-bg transition" aria-label="بستن">
<iconify-icon icon="lucide:x" width="20"></iconify-icon>
</button>
</div>
@ -334,7 +221,7 @@
<div class="p-4 border-t border-cream-200 dark:border-dark-border">
<div class="flex items-center justify-between px-4 py-2 mb-3">
<span class="text-sm text-cream-500 dark:text-dark-muted">حالت تاریک</span>
<button onclick="toggleDark()" class="w-12 h-6 bg-cream-200 dark:bg-forest-500 rounded-full relative transition">
<button onclick="toggleDark()" class="w-12 h-6 bg-cream-200 dark:bg-forest-500 rounded-full relative transition" aria-label="تغییر حالت تاریک">
<div class="w-5 h-5 bg-white dark:bg-dark-bg rounded-full absolute top-0.5 right-0.5 dark:right-auto dark:left-0.5 transition-all shadow"></div>
</button>
</div>
@ -367,15 +254,15 @@
</div>
</div>
</div>
<!-- Main Nav -->
<div class="max-w-7xl mx-auto px-4 md:px-6">
<div class="flex items-center justify-between h-16 md:h-20">
<!-- Mobile Menu Toggle -->
<button onclick="openMobileMenu()" class="md:hidden w-10 h-10 flex items-center justify-center rounded-xl hover:bg-cream-200 dark:hover:bg-dark-card transition">
<button onclick="openMobileMenu()" class="md:hidden w-10 h-10 flex items-center justify-center rounded-xl hover:bg-cream-200 dark:hover:bg-dark-card transition" aria-label="باز کردن منو">
<iconify-icon icon="lucide:menu" width="22"></iconify-icon>
</button>
<!-- Logo -->
<a href="<?php echo esc_url( home_url( '/' ) ); ?>" class="flex items-center gap-2.5">
<div class="w-10 h-10 bg-forest-500 rounded-2xl flex items-center justify-center shadow-lg shadow-forest-500/20">
@ -386,7 +273,7 @@
<span class="block text-[9px] text-cream-500 dark:text-dark-muted font-inter tracking-widest">MOTAYEB</span>
</div>
</a>
<!-- Desktop Nav -->
<nav class="hidden md:flex items-center gap-8">
<?php
@ -400,7 +287,7 @@
) );
?>
</nav>
<!-- Actions -->
<div class="flex items-center gap-2">
<button onclick="openSearch()" class="w-10 h-10 flex items-center justify-center rounded-xl hover:bg-cream-200 dark:hover:bg-dark-card transition" aria-label="جستجو">

16
node_modules/.bin/cssesc generated vendored Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../cssesc/bin/cssesc" "$@"
else
exec node "$basedir/../cssesc/bin/cssesc" "$@"
fi

17
node_modules/.bin/cssesc.cmd generated vendored Normal file
View File

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\cssesc\bin\cssesc" %*

28
node_modules/.bin/cssesc.ps1 generated vendored Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../cssesc/bin/cssesc" $args
} else {
& "$basedir/node$exe" "$basedir/../cssesc/bin/cssesc" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../cssesc/bin/cssesc" $args
} else {
& "node$exe" "$basedir/../cssesc/bin/cssesc" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/jiti generated vendored Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../jiti/bin/jiti.js" "$@"
else
exec node "$basedir/../jiti/bin/jiti.js" "$@"
fi

17
node_modules/.bin/jiti.cmd generated vendored Normal file
View File

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\jiti\bin\jiti.js" %*

28
node_modules/.bin/jiti.ps1 generated vendored Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../jiti/bin/jiti.js" $args
} else {
& "$basedir/node$exe" "$basedir/../jiti/bin/jiti.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../jiti/bin/jiti.js" $args
} else {
& "node$exe" "$basedir/../jiti/bin/jiti.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/mini-svg-data-uri generated vendored Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../mini-svg-data-uri/cli.js" "$@"
else
exec node "$basedir/../mini-svg-data-uri/cli.js" "$@"
fi

17
node_modules/.bin/mini-svg-data-uri.cmd generated vendored Normal file
View File

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\mini-svg-data-uri\cli.js" %*

28
node_modules/.bin/mini-svg-data-uri.ps1 generated vendored Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../mini-svg-data-uri/cli.js" $args
} else {
& "$basedir/node$exe" "$basedir/../mini-svg-data-uri/cli.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../mini-svg-data-uri/cli.js" $args
} else {
& "node$exe" "$basedir/../mini-svg-data-uri/cli.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/nanoid generated vendored Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../nanoid/bin/nanoid.cjs" "$@"
else
exec node "$basedir/../nanoid/bin/nanoid.cjs" "$@"
fi

17
node_modules/.bin/nanoid.cmd generated vendored Normal file
View File

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nanoid\bin\nanoid.cjs" %*

28
node_modules/.bin/nanoid.ps1 generated vendored Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
} else {
& "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
} else {
& "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/resolve generated vendored Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../resolve/bin/resolve" "$@"
else
exec node "$basedir/../resolve/bin/resolve" "$@"
fi

17
node_modules/.bin/resolve.cmd generated vendored Normal file
View File

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\resolve\bin\resolve" %*

28
node_modules/.bin/resolve.ps1 generated vendored Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../resolve/bin/resolve" $args
} else {
& "$basedir/node$exe" "$basedir/../resolve/bin/resolve" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../resolve/bin/resolve" $args
} else {
& "node$exe" "$basedir/../resolve/bin/resolve" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/sucrase generated vendored Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../sucrase/bin/sucrase" "$@"
else
exec node "$basedir/../sucrase/bin/sucrase" "$@"
fi

16
node_modules/.bin/sucrase-node generated vendored Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../sucrase/bin/sucrase-node" "$@"
else
exec node "$basedir/../sucrase/bin/sucrase-node" "$@"
fi

17
node_modules/.bin/sucrase-node.cmd generated vendored Normal file
View File

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\sucrase\bin\sucrase-node" %*

28
node_modules/.bin/sucrase-node.ps1 generated vendored Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../sucrase/bin/sucrase-node" $args
} else {
& "$basedir/node$exe" "$basedir/../sucrase/bin/sucrase-node" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../sucrase/bin/sucrase-node" $args
} else {
& "node$exe" "$basedir/../sucrase/bin/sucrase-node" $args
}
$ret=$LASTEXITCODE
}
exit $ret

17
node_modules/.bin/sucrase.cmd generated vendored Normal file
View File

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\sucrase\bin\sucrase" %*

28
node_modules/.bin/sucrase.ps1 generated vendored Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../sucrase/bin/sucrase" $args
} else {
& "$basedir/node$exe" "$basedir/../sucrase/bin/sucrase" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../sucrase/bin/sucrase" $args
} else {
& "node$exe" "$basedir/../sucrase/bin/sucrase" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/tailwind generated vendored Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../tailwindcss/lib/cli.js" "$@"
else
exec node "$basedir/../tailwindcss/lib/cli.js" "$@"
fi

17
node_modules/.bin/tailwind.cmd generated vendored Normal file
View File

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\tailwindcss\lib\cli.js" %*

28
node_modules/.bin/tailwind.ps1 generated vendored Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../tailwindcss/lib/cli.js" $args
} else {
& "$basedir/node$exe" "$basedir/../tailwindcss/lib/cli.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../tailwindcss/lib/cli.js" $args
} else {
& "node$exe" "$basedir/../tailwindcss/lib/cli.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/tailwindcss generated vendored Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../tailwindcss/lib/cli.js" "$@"
else
exec node "$basedir/../tailwindcss/lib/cli.js" "$@"
fi

17
node_modules/.bin/tailwindcss.cmd generated vendored Normal file
View File

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\tailwindcss\lib\cli.js" %*

28
node_modules/.bin/tailwindcss.ps1 generated vendored Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../tailwindcss/lib/cli.js" $args
} else {
& "$basedir/node$exe" "$basedir/../tailwindcss/lib/cli.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../tailwindcss/lib/cli.js" $args
} else {
& "node$exe" "$basedir/../tailwindcss/lib/cli.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

1016
node_modules/.package-lock.json generated vendored Normal file

File diff suppressed because it is too large Load Diff

144
node_modules/@alloc/quick-lru/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,144 @@
declare namespace QuickLRU {
interface Options<KeyType, ValueType> {
/**
The maximum number of milliseconds an item should remain in the cache.
@default Infinity
By default, `maxAge` will be `Infinity`, which means that items will never expire.
Lazy expiration upon the next write or read call.
Individual expiration of an item can be specified by the `set(key, value, maxAge)` method.
*/
readonly maxAge?: number;
/**
The target maximum number of items before evicting the least recently used items.
The dual-cache algorithm may physically retain up to twice `maxSize` entries for performance reasons, even though the reported cache size does not exceed `maxSize`.
*/
readonly maxSize: number;
/**
Called right before an item is evicted from the cache due to capacity pressure or TTL expiration.
Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`).
This callback is not called for manual removals via `delete()` or `clear()`.
*/
onEviction?: (key: KeyType, value: ValueType) => void;
}
}
declare class QuickLRU<KeyType, ValueType>
implements Iterable<[KeyType, ValueType]> {
/**
The stored item count.
*/
readonly size: number;
/**
Simple ["Least Recently Used" (LRU) cache](https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29).
The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop.
@example
```
import QuickLRU = require('@alloc/quick-lru');
const lru = new QuickLRU({maxSize: 1000});
lru.set('🦄', '🌈');
lru.has('🦄');
//=> true
lru.get('🦄');
//=> '🌈'
```
*/
constructor(options: QuickLRU.Options<KeyType, ValueType>);
[Symbol.iterator](): IterableIterator<[KeyType, ValueType]>;
/**
Set an item. Returns the instance.
Individual expiration of an item can be specified with the `maxAge` option. If not specified, the global `maxAge` value will be used in case it is specified in the constructor, otherwise the item will never expire.
@returns The list instance.
*/
set(key: KeyType, value: ValueType, options?: {maxAge?: number}): this;
/**
Get an item.
@returns The stored item or `undefined`.
*/
get(key: KeyType): ValueType | undefined;
/**
Check if an item exists.
*/
has(key: KeyType): boolean;
/**
Get an item without marking it as recently used.
@returns The stored item or `undefined`.
*/
peek(key: KeyType): ValueType | undefined;
/**
Get the remaining time to live (in milliseconds) for the given item, or `undefined` when the item is not in the cache.
- Does not mark the item as recently used.
- Does not trigger lazy expiration or remove the entry when it is expired.
- Returns `Infinity` if the item has no expiration.
- May return a negative number if the item is already expired but not yet lazily removed.
@returns Remaining time to live in milliseconds when set, `Infinity` when there is no expiration, or `undefined` when the item does not exist.
*/
expiresIn(key: KeyType): number | undefined;
/**
Delete an item.
@returns `true` if the item is removed or `false` if the item doesn't exist.
*/
delete(key: KeyType): boolean;
/**
Delete all items.
*/
clear(): void;
/**
Update the `maxSize` in-place, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee.
Useful for on-the-fly tuning of cache sizes in live systems.
*/
resize(maxSize: number): void;
/**
Iterable for all the keys.
*/
keys(): IterableIterator<KeyType>;
/**
Iterable for all the values.
*/
values(): IterableIterator<ValueType>;
/**
Iterable for all entries, starting with the oldest (ascending in recency).
*/
entriesAscending(): IterableIterator<[KeyType, ValueType]>;
/**
Iterable for all entries, starting with the newest (descending in recency).
*/
entriesDescending(): IterableIterator<[KeyType, ValueType]>;
}
export = QuickLRU;

276
node_modules/@alloc/quick-lru/index.js generated vendored Normal file
View File

@ -0,0 +1,276 @@
'use strict';
class QuickLRU {
constructor(options = {}) {
if (!(options.maxSize && options.maxSize > 0)) {
throw new TypeError('`maxSize` must be a number greater than 0');
}
if (typeof options.maxAge === 'number' && options.maxAge === 0) {
throw new TypeError('`maxAge` must be a number greater than 0');
}
this.maxSize = options.maxSize;
this.maxAge = options.maxAge || Infinity;
this.onEviction = options.onEviction;
this.cache = new Map();
this.oldCache = new Map();
this._size = 0;
}
_emitEvictions(cache) {
if (typeof this.onEviction !== 'function') {
return;
}
for (const [key, item] of cache) {
this.onEviction(key, item.value);
}
}
_deleteIfExpired(key, item) {
if (typeof item.expiry === 'number' && item.expiry <= Date.now()) {
if (typeof this.onEviction === 'function') {
this.onEviction(key, item.value);
}
return this.delete(key);
}
return false;
}
_getOrDeleteIfExpired(key, item) {
const deleted = this._deleteIfExpired(key, item);
if (deleted === false) {
return item.value;
}
}
_getItemValue(key, item) {
return item.expiry ? this._getOrDeleteIfExpired(key, item) : item.value;
}
_peek(key, cache) {
const item = cache.get(key);
return this._getItemValue(key, item);
}
_set(key, value) {
this.cache.set(key, value);
this._size++;
if (this._size >= this.maxSize) {
this._size = 0;
this._emitEvictions(this.oldCache);
this.oldCache = this.cache;
this.cache = new Map();
}
}
_moveToRecent(key, item) {
this.oldCache.delete(key);
this._set(key, item);
}
* _entriesAscending() {
for (const item of this.oldCache) {
const [key, value] = item;
if (!this.cache.has(key)) {
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield item;
}
}
}
for (const item of this.cache) {
const [key, value] = item;
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield item;
}
}
}
get(key) {
if (this.cache.has(key)) {
const item = this.cache.get(key);
return this._getItemValue(key, item);
}
if (this.oldCache.has(key)) {
const item = this.oldCache.get(key);
if (this._deleteIfExpired(key, item) === false) {
this._moveToRecent(key, item);
return item.value;
}
}
}
set(key, value, {maxAge = this.maxAge} = {}) {
const expiry =
typeof maxAge === 'number' && maxAge !== Infinity ?
Date.now() + maxAge :
undefined;
if (this.cache.has(key)) {
this.cache.set(key, {
value,
expiry
});
} else {
this._set(key, {value, expiry});
}
return this;
}
has(key) {
if (this.cache.has(key)) {
return !this._deleteIfExpired(key, this.cache.get(key));
}
if (this.oldCache.has(key)) {
return !this._deleteIfExpired(key, this.oldCache.get(key));
}
return false;
}
peek(key) {
if (this.cache.has(key)) {
return this._peek(key, this.cache);
}
if (this.oldCache.has(key)) {
return this._peek(key, this.oldCache);
}
}
expiresIn(key) {
const item = this.cache.get(key) || this.oldCache.get(key);
if (item) {
return item.expiry ? item.expiry - Date.now() : Infinity;
}
}
delete(key) {
const deleted = this.cache.delete(key);
if (deleted) {
this._size--;
}
return this.oldCache.delete(key) || deleted;
}
clear() {
this.cache.clear();
this.oldCache.clear();
this._size = 0;
}
resize(newSize) {
if (!(newSize && newSize > 0)) {
throw new TypeError('`maxSize` must be a number greater than 0');
}
const items = [...this._entriesAscending()];
const removeCount = items.length - newSize;
if (removeCount < 0) {
this.cache = new Map(items);
this.oldCache = new Map();
this._size = items.length;
} else {
if (removeCount > 0) {
this._emitEvictions(items.slice(0, removeCount));
}
this.oldCache = new Map(items.slice(removeCount));
this.cache = new Map();
this._size = 0;
}
this.maxSize = newSize;
}
* keys() {
for (const [key] of this) {
yield key;
}
}
* values() {
for (const [, value] of this) {
yield value;
}
}
* [Symbol.iterator]() {
for (const item of this.cache) {
const [key, value] = item;
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield [key, value.value];
}
}
for (const item of this.oldCache) {
const [key, value] = item;
if (!this.cache.has(key)) {
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield [key, value.value];
}
}
}
}
* entriesDescending() {
let items = [...this.cache];
for (let i = items.length - 1; i >= 0; --i) {
const item = items[i];
const [key, value] = item;
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield [key, value.value];
}
}
items = [...this.oldCache];
for (let i = items.length - 1; i >= 0; --i) {
const item = items[i];
const [key, value] = item;
if (!this.cache.has(key)) {
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield [key, value.value];
}
}
}
}
* entriesAscending() {
for (const [key, value] of this._entriesAscending()) {
yield [key, value.value];
}
}
get size() {
if (!this._size) {
return this.oldCache.size;
}
let oldCacheSize = 0;
for (const key of this.oldCache.keys()) {
if (!this.cache.has(key)) {
oldCacheSize++;
}
}
return Math.min(this._size + oldCacheSize, this.maxSize);
}
}
module.exports = QuickLRU;

9
node_modules/@alloc/quick-lru/license generated vendored Normal file
View File

@ -0,0 +1,9 @@
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

43
node_modules/@alloc/quick-lru/package.json generated vendored Normal file
View File

@ -0,0 +1,43 @@
{
"name": "@alloc/quick-lru",
"version": "5.3.0",
"description": "Simple “Least Recently Used” (LRU) cache",
"license": "MIT",
"repository": "https://github.com/aleclarson/quick-lru",
"funding": "https://github.com/sponsors/sindresorhus",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "https://sindresorhus.com"
},
"engines": {
"node": ">=10"
},
"scripts": {
"test": "xo && nyc ava && tsd"
},
"files": [
"index.js",
"index.d.ts"
],
"keywords": [
"lru",
"quick",
"cache",
"caching",
"least",
"recently",
"used",
"fast",
"map",
"hash",
"buffer"
],
"devDependencies": {
"ava": "^2.0.0",
"coveralls": "^3.0.3",
"nyc": "^15.0.0",
"tsd": "^0.11.0",
"xo": "^0.26.0"
}
}

160
node_modules/@alloc/quick-lru/readme.md generated vendored Normal file
View File

@ -0,0 +1,160 @@
# @alloc/quick-lru [![Build Status](https://travis-ci.org/aleclarson/quick-lru.svg?branch=master)](https://travis-ci.org/aleclarson/quick-lru) [![Coverage Status](https://coveralls.io/repos/github/aleclarson/quick-lru/badge.svg?branch=master)](https://coveralls.io/github/aleclarson/quick-lru?branch=master)
> Simple [“Least Recently Used” (LRU) cache](https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29)
This is a CommonJS-only fork of [`quick-lru`](https://github.com/sindresorhus/quick-lru).
Useful when you need to cache something and limit memory usage.
See the [algorithm section](#algorithm) for implementation details.
## Install
```
$ npm install @alloc/quick-lru
```
## Usage
```js
const QuickLRU = require('@alloc/quick-lru');
const lru = new QuickLRU({maxSize: 1000});
lru.set('🦄', '🌈');
lru.has('🦄');
//=> true
lru.get('🦄');
//=> '🌈'
```
## API
### new QuickLRU(options?)
Returns a new instance.
### options
Type: `object`
#### maxSize
*Required*\
Type: `number`
The target maximum number of items before evicting the least recently used items.
> **Note:** The dual-cache algorithm may physically retain up to twice `maxSize` entries for performance reasons, even though the reported cache size does not exceed `maxSize`.
#### maxAge
Type: `number`\
Default: `Infinity`
The maximum number of milliseconds an item should remain in cache.
By default maxAge will be Infinity, which means that items will never expire.
Lazy expiration happens upon the next `write` or `read` call.
Individual expiration of an item can be specified by the `set(key, value, options)` method.
#### onEviction
*Optional*\
Type: `(key, value) => void`
Called right before an item is evicted from the cache due to capacity pressure or TTL expiration.
Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`).
This callback is not called for manual removals via `delete()` or `clear()`.
### Instance
The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop.
Both `key` and `value` can be of any type.
#### .set(key, value, options?)
Set an item. Returns the instance.
Individual expiration of an item can be specified with the `maxAge` option. If not specified, the global `maxAge` value will be used in case it is specified on the constructor, otherwise the item will never expire.
#### .get(key)
Get an item.
#### .has(key)
Check if an item exists.
#### .peek(key)
Get an item without marking it as recently used.
#### .expiresIn(key)
Get the remaining time to live (in milliseconds) for the given item, or `undefined` if the item is not in the cache.
- Does not mark the item as recently used.
- Does not trigger lazy expiration or remove the entry when it is expired.
- Returns `Infinity` if the item has no expiration.
- May return a negative number if the item has already expired but has not yet been lazily removed.
#### .delete(key)
Delete an item.
Returns `true` if the item is removed or `false` if the item doesn't exist.
#### .clear()
Delete all items.
#### .resize(maxSize)
Update the `maxSize`, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee.
Useful for on-the-fly tuning of cache sizes in live systems.
#### .keys()
Iterable for all the keys.
#### .values()
Iterable for all the values.
#### .entriesAscending()
Iterable for all entries, starting with the oldest (ascending in recency).
#### .entriesDescending()
Iterable for all entries, starting with the newest (descending in recency).
#### .size
The stored item count.
## Algorithm
This library implements a variant of the [`hashlru` algorithm](https://github.com/dominictarr/hashlru#algorithm) using two [`Map`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map) objects. One map holds recently added or accessed entries, while the other holds older entries. When the recent map reaches `maxSize`, it replaces the older map and a new recent map is created.
This avoids the frequent delete operations required by a traditional linked-list LRU and supports keys of any type and `undefined` values. The tradeoff is that the two maps may physically retain up to twice the target `maxSize` between rotations. Use a strict-size cache when that temporary memory overhead is unacceptable.
---
<div align="center">
<b>
<a href="https://tidelift.com/subscription/pkg/npm-quick-lru?utm_source=npm-quick-lru&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
</b>
<br>
<sub>
Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
</sub>
</div>

19
node_modules/@jridgewell/gen-mapping/LICENSE generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright 2024 Justin Ridgewell <justin@ridgewell.name>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

227
node_modules/@jridgewell/gen-mapping/README.md generated vendored Normal file
View File

@ -0,0 +1,227 @@
# @jridgewell/gen-mapping
> Generate source maps
`gen-mapping` allows you to generate a source map during transpilation or minification.
With a source map, you're able to trace the original location in the source file, either in Chrome's
DevTools or using a library like [`@jridgewell/trace-mapping`][trace-mapping].
You may already be familiar with the [`source-map`][source-map] package's `SourceMapGenerator`. This
provides the same `addMapping` and `setSourceContent` API.
## Installation
```sh
npm install @jridgewell/gen-mapping
```
## Usage
```typescript
import { GenMapping, addMapping, setSourceContent, toEncodedMap, toDecodedMap } from '@jridgewell/gen-mapping';
const map = new GenMapping({
file: 'output.js',
sourceRoot: 'https://example.com/',
});
setSourceContent(map, 'input.js', `function foo() {}`);
addMapping(map, {
// Lines start at line 1, columns at column 0.
generated: { line: 1, column: 0 },
source: 'input.js',
original: { line: 1, column: 0 },
});
addMapping(map, {
generated: { line: 1, column: 9 },
source: 'input.js',
original: { line: 1, column: 9 },
name: 'foo',
});
assert.deepEqual(toDecodedMap(map), {
version: 3,
file: 'output.js',
names: ['foo'],
sourceRoot: 'https://example.com/',
sources: ['input.js'],
sourcesContent: ['function foo() {}'],
mappings: [
[ [0, 0, 0, 0], [9, 0, 0, 9, 0] ]
],
});
assert.deepEqual(toEncodedMap(map), {
version: 3,
file: 'output.js',
names: ['foo'],
sourceRoot: 'https://example.com/',
sources: ['input.js'],
sourcesContent: ['function foo() {}'],
mappings: 'AAAA,SAASA',
});
```
### Smaller Sourcemaps
Not everything needs to be added to a sourcemap, and needless markings can cause signficantly
larger file sizes. `gen-mapping` exposes `maybeAddSegment`/`maybeAddMapping` APIs that will
intelligently determine if this marking adds useful information. If not, the marking will be
skipped.
```typescript
import { maybeAddMapping } from '@jridgewell/gen-mapping';
const map = new GenMapping();
// Adding a sourceless marking at the beginning of a line isn't useful.
maybeAddMapping(map, {
generated: { line: 1, column: 0 },
});
// Adding a new source marking is useful.
maybeAddMapping(map, {
generated: { line: 1, column: 0 },
source: 'input.js',
original: { line: 1, column: 0 },
});
// But adding another marking pointing to the exact same original location isn't, even if the
// generated column changed.
maybeAddMapping(map, {
generated: { line: 1, column: 9 },
source: 'input.js',
original: { line: 1, column: 0 },
});
assert.deepEqual(toEncodedMap(map), {
version: 3,
names: [],
sources: ['input.js'],
sourcesContent: [null],
mappings: 'AAAA',
});
```
## Benchmarks
```
node v18.0.0
amp.js.map
Memory Usage:
gen-mapping: addSegment 5852872 bytes
gen-mapping: addMapping 7716042 bytes
source-map-js 6143250 bytes
source-map-0.6.1 6124102 bytes
source-map-0.8.0 6121173 bytes
Smallest memory usage is gen-mapping: addSegment
Adding speed:
gen-mapping: addSegment x 441 ops/sec ±2.07% (90 runs sampled)
gen-mapping: addMapping x 350 ops/sec ±2.40% (86 runs sampled)
source-map-js: addMapping x 169 ops/sec ±2.42% (80 runs sampled)
source-map-0.6.1: addMapping x 167 ops/sec ±2.56% (80 runs sampled)
source-map-0.8.0: addMapping x 168 ops/sec ±2.52% (80 runs sampled)
Fastest is gen-mapping: addSegment
Generate speed:
gen-mapping: decoded output x 150,824,370 ops/sec ±0.07% (102 runs sampled)
gen-mapping: encoded output x 663 ops/sec ±0.22% (98 runs sampled)
source-map-js: encoded output x 197 ops/sec ±0.45% (84 runs sampled)
source-map-0.6.1: encoded output x 198 ops/sec ±0.33% (85 runs sampled)
source-map-0.8.0: encoded output x 197 ops/sec ±0.06% (93 runs sampled)
Fastest is gen-mapping: decoded output
***
babel.min.js.map
Memory Usage:
gen-mapping: addSegment 37578063 bytes
gen-mapping: addMapping 37212897 bytes
source-map-js 47638527 bytes
source-map-0.6.1 47690503 bytes
source-map-0.8.0 47470188 bytes
Smallest memory usage is gen-mapping: addMapping
Adding speed:
gen-mapping: addSegment x 31.05 ops/sec ±8.31% (43 runs sampled)
gen-mapping: addMapping x 29.83 ops/sec ±7.36% (51 runs sampled)
source-map-js: addMapping x 20.73 ops/sec ±6.22% (38 runs sampled)
source-map-0.6.1: addMapping x 20.03 ops/sec ±10.51% (38 runs sampled)
source-map-0.8.0: addMapping x 19.30 ops/sec ±8.27% (37 runs sampled)
Fastest is gen-mapping: addSegment
Generate speed:
gen-mapping: decoded output x 381,379,234 ops/sec ±0.29% (96 runs sampled)
gen-mapping: encoded output x 95.15 ops/sec ±2.98% (72 runs sampled)
source-map-js: encoded output x 15.20 ops/sec ±7.41% (33 runs sampled)
source-map-0.6.1: encoded output x 16.36 ops/sec ±10.46% (31 runs sampled)
source-map-0.8.0: encoded output x 16.06 ops/sec ±6.45% (31 runs sampled)
Fastest is gen-mapping: decoded output
***
preact.js.map
Memory Usage:
gen-mapping: addSegment 416247 bytes
gen-mapping: addMapping 419824 bytes
source-map-js 1024619 bytes
source-map-0.6.1 1146004 bytes
source-map-0.8.0 1113250 bytes
Smallest memory usage is gen-mapping: addSegment
Adding speed:
gen-mapping: addSegment x 13,755 ops/sec ±0.15% (98 runs sampled)
gen-mapping: addMapping x 13,013 ops/sec ±0.11% (101 runs sampled)
source-map-js: addMapping x 4,564 ops/sec ±0.21% (98 runs sampled)
source-map-0.6.1: addMapping x 4,562 ops/sec ±0.11% (99 runs sampled)
source-map-0.8.0: addMapping x 4,593 ops/sec ±0.11% (100 runs sampled)
Fastest is gen-mapping: addSegment
Generate speed:
gen-mapping: decoded output x 379,864,020 ops/sec ±0.23% (93 runs sampled)
gen-mapping: encoded output x 14,368 ops/sec ±4.07% (82 runs sampled)
source-map-js: encoded output x 5,261 ops/sec ±0.21% (99 runs sampled)
source-map-0.6.1: encoded output x 5,124 ops/sec ±0.58% (99 runs sampled)
source-map-0.8.0: encoded output x 5,434 ops/sec ±0.33% (96 runs sampled)
Fastest is gen-mapping: decoded output
***
react.js.map
Memory Usage:
gen-mapping: addSegment 975096 bytes
gen-mapping: addMapping 1102981 bytes
source-map-js 2918836 bytes
source-map-0.6.1 2885435 bytes
source-map-0.8.0 2874336 bytes
Smallest memory usage is gen-mapping: addSegment
Adding speed:
gen-mapping: addSegment x 4,772 ops/sec ±0.15% (100 runs sampled)
gen-mapping: addMapping x 4,456 ops/sec ±0.13% (97 runs sampled)
source-map-js: addMapping x 1,618 ops/sec ±0.24% (97 runs sampled)
source-map-0.6.1: addMapping x 1,622 ops/sec ±0.12% (99 runs sampled)
source-map-0.8.0: addMapping x 1,631 ops/sec ±0.12% (100 runs sampled)
Fastest is gen-mapping: addSegment
Generate speed:
gen-mapping: decoded output x 379,107,695 ops/sec ±0.07% (99 runs sampled)
gen-mapping: encoded output x 5,421 ops/sec ±1.60% (89 runs sampled)
source-map-js: encoded output x 2,113 ops/sec ±1.81% (98 runs sampled)
source-map-0.6.1: encoded output x 2,126 ops/sec ±0.10% (100 runs sampled)
source-map-0.8.0: encoded output x 2,176 ops/sec ±0.39% (98 runs sampled)
Fastest is gen-mapping: decoded output
```
[source-map]: https://www.npmjs.com/package/source-map
[trace-mapping]: https://github.com/jridgewell/sourcemaps/tree/main/packages/trace-mapping

View File

@ -0,0 +1,292 @@
// src/set-array.ts
var SetArray = class {
constructor() {
this._indexes = { __proto__: null };
this.array = [];
}
};
function cast(set) {
return set;
}
function get(setarr, key) {
return cast(setarr)._indexes[key];
}
function put(setarr, key) {
const index = get(setarr, key);
if (index !== void 0) return index;
const { array, _indexes: indexes } = cast(setarr);
const length = array.push(key);
return indexes[key] = length - 1;
}
function remove(setarr, key) {
const index = get(setarr, key);
if (index === void 0) return;
const { array, _indexes: indexes } = cast(setarr);
for (let i = index + 1; i < array.length; i++) {
const k = array[i];
array[i - 1] = k;
indexes[k]--;
}
indexes[key] = void 0;
array.pop();
}
// src/gen-mapping.ts
import {
encode
} from "@jridgewell/sourcemap-codec";
import { TraceMap, decodedMappings } from "@jridgewell/trace-mapping";
// src/sourcemap-segment.ts
var COLUMN = 0;
var SOURCES_INDEX = 1;
var SOURCE_LINE = 2;
var SOURCE_COLUMN = 3;
var NAMES_INDEX = 4;
// src/gen-mapping.ts
var NO_NAME = -1;
var GenMapping = class {
constructor({ file, sourceRoot } = {}) {
this._names = new SetArray();
this._sources = new SetArray();
this._sourcesContent = [];
this._mappings = [];
this.file = file;
this.sourceRoot = sourceRoot;
this._ignoreList = new SetArray();
}
};
function cast2(map) {
return map;
}
function addSegment(map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) {
return addSegmentInternal(
false,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content
);
}
function addMapping(map, mapping) {
return addMappingInternal(false, map, mapping);
}
var maybeAddSegment = (map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => {
return addSegmentInternal(
true,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content
);
};
var maybeAddMapping = (map, mapping) => {
return addMappingInternal(true, map, mapping);
};
function setSourceContent(map, source, content) {
const {
_sources: sources,
_sourcesContent: sourcesContent
// _originalScopes: originalScopes,
} = cast2(map);
const index = put(sources, source);
sourcesContent[index] = content;
}
function setIgnore(map, source, ignore = true) {
const {
_sources: sources,
_sourcesContent: sourcesContent,
_ignoreList: ignoreList
// _originalScopes: originalScopes,
} = cast2(map);
const index = put(sources, source);
if (index === sourcesContent.length) sourcesContent[index] = null;
if (ignore) put(ignoreList, index);
else remove(ignoreList, index);
}
function toDecodedMap(map) {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names,
_ignoreList: ignoreList
// _originalScopes: originalScopes,
// _generatedRanges: generatedRanges,
} = cast2(map);
removeEmptyFinalLines(mappings);
return {
version: 3,
file: map.file || void 0,
names: names.array,
sourceRoot: map.sourceRoot || void 0,
sources: sources.array,
sourcesContent,
mappings,
// originalScopes,
// generatedRanges,
ignoreList: ignoreList.array
};
}
function toEncodedMap(map) {
const decoded = toDecodedMap(map);
return Object.assign({}, decoded, {
// originalScopes: decoded.originalScopes.map((os) => encodeOriginalScopes(os)),
// generatedRanges: encodeGeneratedRanges(decoded.generatedRanges as GeneratedRange[]),
mappings: encode(decoded.mappings)
});
}
function fromMap(input) {
const map = new TraceMap(input);
const gen = new GenMapping({ file: map.file, sourceRoot: map.sourceRoot });
putAll(cast2(gen)._names, map.names);
putAll(cast2(gen)._sources, map.sources);
cast2(gen)._sourcesContent = map.sourcesContent || map.sources.map(() => null);
cast2(gen)._mappings = decodedMappings(map);
if (map.ignoreList) putAll(cast2(gen)._ignoreList, map.ignoreList);
return gen;
}
function allMappings(map) {
const out = [];
const { _mappings: mappings, _sources: sources, _names: names } = cast2(map);
for (let i = 0; i < mappings.length; i++) {
const line = mappings[i];
for (let j = 0; j < line.length; j++) {
const seg = line[j];
const generated = { line: i + 1, column: seg[COLUMN] };
let source = void 0;
let original = void 0;
let name = void 0;
if (seg.length !== 1) {
source = sources.array[seg[SOURCES_INDEX]];
original = { line: seg[SOURCE_LINE] + 1, column: seg[SOURCE_COLUMN] };
if (seg.length === 5) name = names.array[seg[NAMES_INDEX]];
}
out.push({ generated, source, original, name });
}
}
return out;
}
function addSegmentInternal(skipable, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names
// _originalScopes: originalScopes,
} = cast2(map);
const line = getIndex(mappings, genLine);
const index = getColumnIndex(line, genColumn);
if (!source) {
if (skipable && skipSourceless(line, index)) return;
return insert(line, index, [genColumn]);
}
assert(sourceLine);
assert(sourceColumn);
const sourcesIndex = put(sources, source);
const namesIndex = name ? put(names, name) : NO_NAME;
if (sourcesIndex === sourcesContent.length) sourcesContent[sourcesIndex] = content != null ? content : null;
if (skipable && skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex)) {
return;
}
return insert(
line,
index,
name ? [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex] : [genColumn, sourcesIndex, sourceLine, sourceColumn]
);
}
function assert(_val) {
}
function getIndex(arr, index) {
for (let i = arr.length; i <= index; i++) {
arr[i] = [];
}
return arr[index];
}
function getColumnIndex(line, genColumn) {
let index = line.length;
for (let i = index - 1; i >= 0; index = i--) {
const current = line[i];
if (genColumn >= current[COLUMN]) break;
}
return index;
}
function insert(array, index, value) {
for (let i = array.length; i > index; i--) {
array[i] = array[i - 1];
}
array[index] = value;
}
function removeEmptyFinalLines(mappings) {
const { length } = mappings;
let len = length;
for (let i = len - 1; i >= 0; len = i, i--) {
if (mappings[i].length > 0) break;
}
if (len < length) mappings.length = len;
}
function putAll(setarr, array) {
for (let i = 0; i < array.length; i++) put(setarr, array[i]);
}
function skipSourceless(line, index) {
if (index === 0) return true;
const prev = line[index - 1];
return prev.length === 1;
}
function skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex) {
if (index === 0) return false;
const prev = line[index - 1];
if (prev.length === 1) return false;
return sourcesIndex === prev[SOURCES_INDEX] && sourceLine === prev[SOURCE_LINE] && sourceColumn === prev[SOURCE_COLUMN] && namesIndex === (prev.length === 5 ? prev[NAMES_INDEX] : NO_NAME);
}
function addMappingInternal(skipable, map, mapping) {
const { generated, source, original, name, content } = mapping;
if (!source) {
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
null,
null,
null,
null,
null
);
}
assert(original);
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
source,
original.line - 1,
original.column,
name,
content
);
}
export {
GenMapping,
addMapping,
addSegment,
allMappings,
fromMap,
maybeAddMapping,
maybeAddSegment,
setIgnore,
setSourceContent,
toDecodedMap,
toEncodedMap
};
//# sourceMappingURL=gen-mapping.mjs.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,358 @@
(function (global, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
factory(module, require('@jridgewell/sourcemap-codec'), require('@jridgewell/trace-mapping'));
module.exports = def(module);
} else if (typeof define === 'function' && define.amd) {
define(['module', '@jridgewell/sourcemap-codec', '@jridgewell/trace-mapping'], function(mod) {
factory.apply(this, arguments);
mod.exports = def(mod);
});
} else {
const mod = { exports: {} };
factory(mod, global.sourcemapCodec, global.traceMapping);
global = typeof globalThis !== 'undefined' ? globalThis : global || self;
global.genMapping = def(mod);
}
function def(m) { return 'default' in m.exports ? m.exports.default : m.exports; }
})(this, (function (module, require_sourcemapCodec, require_traceMapping) {
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// umd:@jridgewell/sourcemap-codec
var require_sourcemap_codec = __commonJS({
"umd:@jridgewell/sourcemap-codec"(exports, module2) {
module2.exports = require_sourcemapCodec;
}
});
// umd:@jridgewell/trace-mapping
var require_trace_mapping = __commonJS({
"umd:@jridgewell/trace-mapping"(exports, module2) {
module2.exports = require_traceMapping;
}
});
// src/gen-mapping.ts
var gen_mapping_exports = {};
__export(gen_mapping_exports, {
GenMapping: () => GenMapping,
addMapping: () => addMapping,
addSegment: () => addSegment,
allMappings: () => allMappings,
fromMap: () => fromMap,
maybeAddMapping: () => maybeAddMapping,
maybeAddSegment: () => maybeAddSegment,
setIgnore: () => setIgnore,
setSourceContent: () => setSourceContent,
toDecodedMap: () => toDecodedMap,
toEncodedMap: () => toEncodedMap
});
module.exports = __toCommonJS(gen_mapping_exports);
// src/set-array.ts
var SetArray = class {
constructor() {
this._indexes = { __proto__: null };
this.array = [];
}
};
function cast(set) {
return set;
}
function get(setarr, key) {
return cast(setarr)._indexes[key];
}
function put(setarr, key) {
const index = get(setarr, key);
if (index !== void 0) return index;
const { array, _indexes: indexes } = cast(setarr);
const length = array.push(key);
return indexes[key] = length - 1;
}
function remove(setarr, key) {
const index = get(setarr, key);
if (index === void 0) return;
const { array, _indexes: indexes } = cast(setarr);
for (let i = index + 1; i < array.length; i++) {
const k = array[i];
array[i - 1] = k;
indexes[k]--;
}
indexes[key] = void 0;
array.pop();
}
// src/gen-mapping.ts
var import_sourcemap_codec = __toESM(require_sourcemap_codec());
var import_trace_mapping = __toESM(require_trace_mapping());
// src/sourcemap-segment.ts
var COLUMN = 0;
var SOURCES_INDEX = 1;
var SOURCE_LINE = 2;
var SOURCE_COLUMN = 3;
var NAMES_INDEX = 4;
// src/gen-mapping.ts
var NO_NAME = -1;
var GenMapping = class {
constructor({ file, sourceRoot } = {}) {
this._names = new SetArray();
this._sources = new SetArray();
this._sourcesContent = [];
this._mappings = [];
this.file = file;
this.sourceRoot = sourceRoot;
this._ignoreList = new SetArray();
}
};
function cast2(map) {
return map;
}
function addSegment(map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) {
return addSegmentInternal(
false,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content
);
}
function addMapping(map, mapping) {
return addMappingInternal(false, map, mapping);
}
var maybeAddSegment = (map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => {
return addSegmentInternal(
true,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content
);
};
var maybeAddMapping = (map, mapping) => {
return addMappingInternal(true, map, mapping);
};
function setSourceContent(map, source, content) {
const {
_sources: sources,
_sourcesContent: sourcesContent
// _originalScopes: originalScopes,
} = cast2(map);
const index = put(sources, source);
sourcesContent[index] = content;
}
function setIgnore(map, source, ignore = true) {
const {
_sources: sources,
_sourcesContent: sourcesContent,
_ignoreList: ignoreList
// _originalScopes: originalScopes,
} = cast2(map);
const index = put(sources, source);
if (index === sourcesContent.length) sourcesContent[index] = null;
if (ignore) put(ignoreList, index);
else remove(ignoreList, index);
}
function toDecodedMap(map) {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names,
_ignoreList: ignoreList
// _originalScopes: originalScopes,
// _generatedRanges: generatedRanges,
} = cast2(map);
removeEmptyFinalLines(mappings);
return {
version: 3,
file: map.file || void 0,
names: names.array,
sourceRoot: map.sourceRoot || void 0,
sources: sources.array,
sourcesContent,
mappings,
// originalScopes,
// generatedRanges,
ignoreList: ignoreList.array
};
}
function toEncodedMap(map) {
const decoded = toDecodedMap(map);
return Object.assign({}, decoded, {
// originalScopes: decoded.originalScopes.map((os) => encodeOriginalScopes(os)),
// generatedRanges: encodeGeneratedRanges(decoded.generatedRanges as GeneratedRange[]),
mappings: (0, import_sourcemap_codec.encode)(decoded.mappings)
});
}
function fromMap(input) {
const map = new import_trace_mapping.TraceMap(input);
const gen = new GenMapping({ file: map.file, sourceRoot: map.sourceRoot });
putAll(cast2(gen)._names, map.names);
putAll(cast2(gen)._sources, map.sources);
cast2(gen)._sourcesContent = map.sourcesContent || map.sources.map(() => null);
cast2(gen)._mappings = (0, import_trace_mapping.decodedMappings)(map);
if (map.ignoreList) putAll(cast2(gen)._ignoreList, map.ignoreList);
return gen;
}
function allMappings(map) {
const out = [];
const { _mappings: mappings, _sources: sources, _names: names } = cast2(map);
for (let i = 0; i < mappings.length; i++) {
const line = mappings[i];
for (let j = 0; j < line.length; j++) {
const seg = line[j];
const generated = { line: i + 1, column: seg[COLUMN] };
let source = void 0;
let original = void 0;
let name = void 0;
if (seg.length !== 1) {
source = sources.array[seg[SOURCES_INDEX]];
original = { line: seg[SOURCE_LINE] + 1, column: seg[SOURCE_COLUMN] };
if (seg.length === 5) name = names.array[seg[NAMES_INDEX]];
}
out.push({ generated, source, original, name });
}
}
return out;
}
function addSegmentInternal(skipable, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names
// _originalScopes: originalScopes,
} = cast2(map);
const line = getIndex(mappings, genLine);
const index = getColumnIndex(line, genColumn);
if (!source) {
if (skipable && skipSourceless(line, index)) return;
return insert(line, index, [genColumn]);
}
assert(sourceLine);
assert(sourceColumn);
const sourcesIndex = put(sources, source);
const namesIndex = name ? put(names, name) : NO_NAME;
if (sourcesIndex === sourcesContent.length) sourcesContent[sourcesIndex] = content != null ? content : null;
if (skipable && skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex)) {
return;
}
return insert(
line,
index,
name ? [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex] : [genColumn, sourcesIndex, sourceLine, sourceColumn]
);
}
function assert(_val) {
}
function getIndex(arr, index) {
for (let i = arr.length; i <= index; i++) {
arr[i] = [];
}
return arr[index];
}
function getColumnIndex(line, genColumn) {
let index = line.length;
for (let i = index - 1; i >= 0; index = i--) {
const current = line[i];
if (genColumn >= current[COLUMN]) break;
}
return index;
}
function insert(array, index, value) {
for (let i = array.length; i > index; i--) {
array[i] = array[i - 1];
}
array[index] = value;
}
function removeEmptyFinalLines(mappings) {
const { length } = mappings;
let len = length;
for (let i = len - 1; i >= 0; len = i, i--) {
if (mappings[i].length > 0) break;
}
if (len < length) mappings.length = len;
}
function putAll(setarr, array) {
for (let i = 0; i < array.length; i++) put(setarr, array[i]);
}
function skipSourceless(line, index) {
if (index === 0) return true;
const prev = line[index - 1];
return prev.length === 1;
}
function skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex) {
if (index === 0) return false;
const prev = line[index - 1];
if (prev.length === 1) return false;
return sourcesIndex === prev[SOURCES_INDEX] && sourceLine === prev[SOURCE_LINE] && sourceColumn === prev[SOURCE_COLUMN] && namesIndex === (prev.length === 5 ? prev[NAMES_INDEX] : NO_NAME);
}
function addMappingInternal(skipable, map, mapping) {
const { generated, source, original, name, content } = mapping;
if (!source) {
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
null,
null,
null,
null,
null
);
}
assert(original);
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
source,
original.line - 1,
original.column,
name,
content
);
}
}));
//# sourceMappingURL=gen-mapping.umd.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,88 @@
import type { SourceMapInput } from '@jridgewell/trace-mapping';
import type { DecodedSourceMap, EncodedSourceMap, Pos, Mapping } from './types';
export type { DecodedSourceMap, EncodedSourceMap, Mapping };
export type Options = {
file?: string | null;
sourceRoot?: string | null;
};
/**
* Provides the state to generate a sourcemap.
*/
export declare class GenMapping {
private _names;
private _sources;
private _sourcesContent;
private _mappings;
private _ignoreList;
file: string | null | undefined;
sourceRoot: string | null | undefined;
constructor({ file, sourceRoot }?: Options);
}
/**
* A low-level API to associate a generated position with an original source position. Line and
* column here are 0-based, unlike `addMapping`.
*/
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source?: null, sourceLine?: null, sourceColumn?: null, name?: null, content?: null): void;
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source: string, sourceLine: number, sourceColumn: number, name?: null, content?: string | null): void;
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source: string, sourceLine: number, sourceColumn: number, name: string, content?: string | null): void;
/**
* A high-level API to associate a generated position with an original source position. Line is
* 1-based, but column is 0-based, due to legacy behavior in `source-map` library.
*/
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source?: null;
original?: null;
name?: null;
content?: null;
}): void;
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source: string;
original: Pos;
name?: null;
content?: string | null;
}): void;
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source: string;
original: Pos;
name: string;
content?: string | null;
}): void;
/**
* Same as `addSegment`, but will only add the segment if it generates useful information in the
* resulting map. This only works correctly if segments are added **in order**, meaning you should
* not add a segment with a lower generated line/column than one that came before.
*/
export declare const maybeAddSegment: typeof addSegment;
/**
* Same as `addMapping`, but will only add the mapping if it generates useful information in the
* resulting map. This only works correctly if mappings are added **in order**, meaning you should
* not add a mapping with a lower generated line/column than one that came before.
*/
export declare const maybeAddMapping: typeof addMapping;
/**
* Adds/removes the content of the source file to the source map.
*/
export declare function setSourceContent(map: GenMapping, source: string, content: string | null): void;
export declare function setIgnore(map: GenMapping, source: string, ignore?: boolean): void;
/**
* Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export declare function toDecodedMap(map: GenMapping): DecodedSourceMap;
/**
* Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export declare function toEncodedMap(map: GenMapping): EncodedSourceMap;
/**
* Constructs a new GenMapping, using the already present mappings of the input.
*/
export declare function fromMap(input: SourceMapInput): GenMapping;
/**
* Returns an array of high-level mapping objects for every recorded segment, which could then be
* passed to the `source-map` library.
*/
export declare function allMappings(map: GenMapping): Mapping[];

View File

@ -0,0 +1,32 @@
type Key = string | number | symbol;
/**
* SetArray acts like a `Set` (allowing only one occurrence of a string `key`), but provides the
* index of the `key` in the backing array.
*
* This is designed to allow synchronizing a second array with the contents of the backing array,
* like how in a sourcemap `sourcesContent[i]` is the source content associated with `source[i]`,
* and there are never duplicates.
*/
export declare class SetArray<T extends Key = Key> {
private _indexes;
array: readonly T[];
constructor();
}
/**
* Gets the index associated with `key` in the backing array, if it is already present.
*/
export declare function get<T extends Key>(setarr: SetArray<T>, key: T): number | undefined;
/**
* Puts `key` into the backing array, if it is not already present. Returns
* the index of the `key` in the backing array.
*/
export declare function put<T extends Key>(setarr: SetArray<T>, key: T): number;
/**
* Pops the last added item out of the SetArray.
*/
export declare function pop<T extends Key>(setarr: SetArray<T>): void;
/**
* Removes the key, if it exists in the set.
*/
export declare function remove<T extends Key>(setarr: SetArray<T>, key: T): void;
export {};

View File

@ -0,0 +1,12 @@
type GeneratedColumn = number;
type SourcesIndex = number;
type SourceLine = number;
type SourceColumn = number;
type NamesIndex = number;
export type SourceMapSegment = [GeneratedColumn] | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn] | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn, NamesIndex];
export declare const COLUMN = 0;
export declare const SOURCES_INDEX = 1;
export declare const SOURCE_LINE = 2;
export declare const SOURCE_COLUMN = 3;
export declare const NAMES_INDEX = 4;
export {};

View File

@ -0,0 +1,43 @@
import type { SourceMapSegment } from './sourcemap-segment';
export interface SourceMapV3 {
file?: string | null;
names: readonly string[];
sourceRoot?: string;
sources: readonly (string | null)[];
sourcesContent?: readonly (string | null)[];
version: 3;
ignoreList?: readonly number[];
}
export interface EncodedSourceMap extends SourceMapV3 {
mappings: string;
}
export interface DecodedSourceMap extends SourceMapV3 {
mappings: readonly SourceMapSegment[][];
}
export interface Pos {
line: number;
column: number;
}
export interface OriginalPos extends Pos {
source: string;
}
export interface BindingExpressionRange {
start: Pos;
expression: string;
}
export type Mapping = {
generated: Pos;
source: undefined;
original: undefined;
name: undefined;
} | {
generated: Pos;
source: string;
original: Pos;
name: string;
} | {
generated: Pos;
source: string;
original: Pos;
name: undefined;
};

67
node_modules/@jridgewell/gen-mapping/package.json generated vendored Normal file
View File

@ -0,0 +1,67 @@
{
"name": "@jridgewell/gen-mapping",
"version": "0.3.13",
"description": "Generate source maps",
"keywords": [
"source",
"map"
],
"main": "dist/gen-mapping.umd.js",
"module": "dist/gen-mapping.mjs",
"types": "types/gen-mapping.d.cts",
"files": [
"dist",
"src",
"types"
],
"exports": {
".": [
{
"import": {
"types": "./types/gen-mapping.d.mts",
"default": "./dist/gen-mapping.mjs"
},
"default": {
"types": "./types/gen-mapping.d.cts",
"default": "./dist/gen-mapping.umd.js"
}
},
"./dist/gen-mapping.umd.js"
],
"./package.json": "./package.json"
},
"scripts": {
"benchmark": "run-s build:code benchmark:*",
"benchmark:install": "cd benchmark && npm install",
"benchmark:only": "node --expose-gc benchmark/index.js",
"build": "run-s -n build:code build:types",
"build:code": "node ../../esbuild.mjs gen-mapping.ts",
"build:types": "run-s build:types:force build:types:emit build:types:mts",
"build:types:force": "rimraf tsconfig.build.tsbuildinfo",
"build:types:emit": "tsc --project tsconfig.build.json",
"build:types:mts": "node ../../mts-types.mjs",
"clean": "run-s -n clean:code clean:types",
"clean:code": "tsc --build --clean tsconfig.build.json",
"clean:types": "rimraf dist types",
"test": "run-s -n test:types test:only test:format",
"test:format": "prettier --check '{src,test}/**/*.ts'",
"test:only": "mocha",
"test:types": "eslint '{src,test}/**/*.ts'",
"lint": "run-s -n lint:types lint:format",
"lint:format": "npm run test:format -- --write",
"lint:types": "npm run test:types -- --fix",
"prepublishOnly": "npm run-s -n build test"
},
"homepage": "https://github.com/jridgewell/sourcemaps/tree/main/packages/gen-mapping",
"repository": {
"type": "git",
"url": "git+https://github.com/jridgewell/sourcemaps.git",
"directory": "packages/gen-mapping"
},
"author": "Justin Ridgewell <justin@ridgewell.name>",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
}

614
node_modules/@jridgewell/gen-mapping/src/gen-mapping.ts generated vendored Normal file
View File

@ -0,0 +1,614 @@
import { SetArray, put, remove } from './set-array';
import {
encode,
// encodeGeneratedRanges,
// encodeOriginalScopes
} from '@jridgewell/sourcemap-codec';
import { TraceMap, decodedMappings } from '@jridgewell/trace-mapping';
import {
COLUMN,
SOURCES_INDEX,
SOURCE_LINE,
SOURCE_COLUMN,
NAMES_INDEX,
} from './sourcemap-segment';
import type { SourceMapInput } from '@jridgewell/trace-mapping';
// import type { OriginalScope, GeneratedRange } from '@jridgewell/sourcemap-codec';
import type { SourceMapSegment } from './sourcemap-segment';
import type {
DecodedSourceMap,
EncodedSourceMap,
Pos,
Mapping,
// BindingExpressionRange,
// OriginalPos,
// OriginalScopeInfo,
// GeneratedRangeInfo,
} from './types';
export type { DecodedSourceMap, EncodedSourceMap, Mapping };
export type Options = {
file?: string | null;
sourceRoot?: string | null;
};
const NO_NAME = -1;
/**
* Provides the state to generate a sourcemap.
*/
export class GenMapping {
declare private _names: SetArray<string>;
declare private _sources: SetArray<string>;
declare private _sourcesContent: (string | null)[];
declare private _mappings: SourceMapSegment[][];
// private declare _originalScopes: OriginalScope[][];
// private declare _generatedRanges: GeneratedRange[];
declare private _ignoreList: SetArray<number>;
declare file: string | null | undefined;
declare sourceRoot: string | null | undefined;
constructor({ file, sourceRoot }: Options = {}) {
this._names = new SetArray();
this._sources = new SetArray();
this._sourcesContent = [];
this._mappings = [];
// this._originalScopes = [];
// this._generatedRanges = [];
this.file = file;
this.sourceRoot = sourceRoot;
this._ignoreList = new SetArray();
}
}
interface PublicMap {
_names: GenMapping['_names'];
_sources: GenMapping['_sources'];
_sourcesContent: GenMapping['_sourcesContent'];
_mappings: GenMapping['_mappings'];
// _originalScopes: GenMapping['_originalScopes'];
// _generatedRanges: GenMapping['_generatedRanges'];
_ignoreList: GenMapping['_ignoreList'];
}
/**
* Typescript doesn't allow friend access to private fields, so this just casts the map into a type
* with public access modifiers.
*/
function cast(map: unknown): PublicMap {
return map as any;
}
/**
* A low-level API to associate a generated position with an original source position. Line and
* column here are 0-based, unlike `addMapping`.
*/
export function addSegment(
map: GenMapping,
genLine: number,
genColumn: number,
source?: null,
sourceLine?: null,
sourceColumn?: null,
name?: null,
content?: null,
): void;
export function addSegment(
map: GenMapping,
genLine: number,
genColumn: number,
source: string,
sourceLine: number,
sourceColumn: number,
name?: null,
content?: string | null,
): void;
export function addSegment(
map: GenMapping,
genLine: number,
genColumn: number,
source: string,
sourceLine: number,
sourceColumn: number,
name: string,
content?: string | null,
): void;
export function addSegment(
map: GenMapping,
genLine: number,
genColumn: number,
source?: string | null,
sourceLine?: number | null,
sourceColumn?: number | null,
name?: string | null,
content?: string | null,
): void {
return addSegmentInternal(
false,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content,
);
}
/**
* A high-level API to associate a generated position with an original source position. Line is
* 1-based, but column is 0-based, due to legacy behavior in `source-map` library.
*/
export function addMapping(
map: GenMapping,
mapping: {
generated: Pos;
source?: null;
original?: null;
name?: null;
content?: null;
},
): void;
export function addMapping(
map: GenMapping,
mapping: {
generated: Pos;
source: string;
original: Pos;
name?: null;
content?: string | null;
},
): void;
export function addMapping(
map: GenMapping,
mapping: {
generated: Pos;
source: string;
original: Pos;
name: string;
content?: string | null;
},
): void;
export function addMapping(
map: GenMapping,
mapping: {
generated: Pos;
source?: string | null;
original?: Pos | null;
name?: string | null;
content?: string | null;
},
): void {
return addMappingInternal(false, map, mapping as Parameters<typeof addMappingInternal>[2]);
}
/**
* Same as `addSegment`, but will only add the segment if it generates useful information in the
* resulting map. This only works correctly if segments are added **in order**, meaning you should
* not add a segment with a lower generated line/column than one that came before.
*/
export const maybeAddSegment: typeof addSegment = (
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content,
) => {
return addSegmentInternal(
true,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content,
);
};
/**
* Same as `addMapping`, but will only add the mapping if it generates useful information in the
* resulting map. This only works correctly if mappings are added **in order**, meaning you should
* not add a mapping with a lower generated line/column than one that came before.
*/
export const maybeAddMapping: typeof addMapping = (map, mapping) => {
return addMappingInternal(true, map, mapping as Parameters<typeof addMappingInternal>[2]);
};
/**
* Adds/removes the content of the source file to the source map.
*/
export function setSourceContent(map: GenMapping, source: string, content: string | null): void {
const {
_sources: sources,
_sourcesContent: sourcesContent,
// _originalScopes: originalScopes,
} = cast(map);
const index = put(sources, source);
sourcesContent[index] = content;
// if (index === originalScopes.length) originalScopes[index] = [];
}
export function setIgnore(map: GenMapping, source: string, ignore = true) {
const {
_sources: sources,
_sourcesContent: sourcesContent,
_ignoreList: ignoreList,
// _originalScopes: originalScopes,
} = cast(map);
const index = put(sources, source);
if (index === sourcesContent.length) sourcesContent[index] = null;
// if (index === originalScopes.length) originalScopes[index] = [];
if (ignore) put(ignoreList, index);
else remove(ignoreList, index);
}
/**
* Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export function toDecodedMap(map: GenMapping): DecodedSourceMap {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names,
_ignoreList: ignoreList,
// _originalScopes: originalScopes,
// _generatedRanges: generatedRanges,
} = cast(map);
removeEmptyFinalLines(mappings);
return {
version: 3,
file: map.file || undefined,
names: names.array,
sourceRoot: map.sourceRoot || undefined,
sources: sources.array,
sourcesContent,
mappings,
// originalScopes,
// generatedRanges,
ignoreList: ignoreList.array,
};
}
/**
* Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export function toEncodedMap(map: GenMapping): EncodedSourceMap {
const decoded = toDecodedMap(map);
return Object.assign({}, decoded, {
// originalScopes: decoded.originalScopes.map((os) => encodeOriginalScopes(os)),
// generatedRanges: encodeGeneratedRanges(decoded.generatedRanges as GeneratedRange[]),
mappings: encode(decoded.mappings as SourceMapSegment[][]),
});
}
/**
* Constructs a new GenMapping, using the already present mappings of the input.
*/
export function fromMap(input: SourceMapInput): GenMapping {
const map = new TraceMap(input);
const gen = new GenMapping({ file: map.file, sourceRoot: map.sourceRoot });
putAll(cast(gen)._names, map.names);
putAll(cast(gen)._sources, map.sources as string[]);
cast(gen)._sourcesContent = map.sourcesContent || map.sources.map(() => null);
cast(gen)._mappings = decodedMappings(map) as GenMapping['_mappings'];
// TODO: implement originalScopes/generatedRanges
if (map.ignoreList) putAll(cast(gen)._ignoreList, map.ignoreList);
return gen;
}
/**
* Returns an array of high-level mapping objects for every recorded segment, which could then be
* passed to the `source-map` library.
*/
export function allMappings(map: GenMapping): Mapping[] {
const out: Mapping[] = [];
const { _mappings: mappings, _sources: sources, _names: names } = cast(map);
for (let i = 0; i < mappings.length; i++) {
const line = mappings[i];
for (let j = 0; j < line.length; j++) {
const seg = line[j];
const generated = { line: i + 1, column: seg[COLUMN] };
let source: string | undefined = undefined;
let original: Pos | undefined = undefined;
let name: string | undefined = undefined;
if (seg.length !== 1) {
source = sources.array[seg[SOURCES_INDEX]];
original = { line: seg[SOURCE_LINE] + 1, column: seg[SOURCE_COLUMN] };
if (seg.length === 5) name = names.array[seg[NAMES_INDEX]];
}
out.push({ generated, source, original, name } as Mapping);
}
}
return out;
}
// This split declaration is only so that terser can elminiate the static initialization block.
function addSegmentInternal<S extends string | null | undefined>(
skipable: boolean,
map: GenMapping,
genLine: number,
genColumn: number,
source: S,
sourceLine: S extends string ? number : null | undefined,
sourceColumn: S extends string ? number : null | undefined,
name: S extends string ? string | null | undefined : null | undefined,
content: S extends string ? string | null | undefined : null | undefined,
): void {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names,
// _originalScopes: originalScopes,
} = cast(map);
const line = getIndex(mappings, genLine);
const index = getColumnIndex(line, genColumn);
if (!source) {
if (skipable && skipSourceless(line, index)) return;
return insert(line, index, [genColumn]);
}
// Sigh, TypeScript can't figure out sourceLine and sourceColumn aren't nullish if source
// isn't nullish.
assert<number>(sourceLine);
assert<number>(sourceColumn);
const sourcesIndex = put(sources, source);
const namesIndex = name ? put(names, name) : NO_NAME;
if (sourcesIndex === sourcesContent.length) sourcesContent[sourcesIndex] = content ?? null;
// if (sourcesIndex === originalScopes.length) originalScopes[sourcesIndex] = [];
if (skipable && skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex)) {
return;
}
return insert(
line,
index,
name
? [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex]
: [genColumn, sourcesIndex, sourceLine, sourceColumn],
);
}
function assert<T>(_val: unknown): asserts _val is T {
// noop.
}
function getIndex<T>(arr: T[][], index: number): T[] {
for (let i = arr.length; i <= index; i++) {
arr[i] = [];
}
return arr[index];
}
function getColumnIndex(line: SourceMapSegment[], genColumn: number): number {
let index = line.length;
for (let i = index - 1; i >= 0; index = i--) {
const current = line[i];
if (genColumn >= current[COLUMN]) break;
}
return index;
}
function insert<T>(array: T[], index: number, value: T) {
for (let i = array.length; i > index; i--) {
array[i] = array[i - 1];
}
array[index] = value;
}
function removeEmptyFinalLines(mappings: SourceMapSegment[][]) {
const { length } = mappings;
let len = length;
for (let i = len - 1; i >= 0; len = i, i--) {
if (mappings[i].length > 0) break;
}
if (len < length) mappings.length = len;
}
function putAll<T extends string | number>(setarr: SetArray<T>, array: T[]) {
for (let i = 0; i < array.length; i++) put(setarr, array[i]);
}
function skipSourceless(line: SourceMapSegment[], index: number): boolean {
// The start of a line is already sourceless, so adding a sourceless segment to the beginning
// doesn't generate any useful information.
if (index === 0) return true;
const prev = line[index - 1];
// If the previous segment is also sourceless, then adding another sourceless segment doesn't
// genrate any new information. Else, this segment will end the source/named segment and point to
// a sourceless position, which is useful.
return prev.length === 1;
}
function skipSource(
line: SourceMapSegment[],
index: number,
sourcesIndex: number,
sourceLine: number,
sourceColumn: number,
namesIndex: number,
): boolean {
// A source/named segment at the start of a line gives position at that genColumn
if (index === 0) return false;
const prev = line[index - 1];
// If the previous segment is sourceless, then we're transitioning to a source.
if (prev.length === 1) return false;
// If the previous segment maps to the exact same source position, then this segment doesn't
// provide any new position information.
return (
sourcesIndex === prev[SOURCES_INDEX] &&
sourceLine === prev[SOURCE_LINE] &&
sourceColumn === prev[SOURCE_COLUMN] &&
namesIndex === (prev.length === 5 ? prev[NAMES_INDEX] : NO_NAME)
);
}
function addMappingInternal<S extends string | null | undefined>(
skipable: boolean,
map: GenMapping,
mapping: {
generated: Pos;
source: S;
original: S extends string ? Pos : null | undefined;
name: S extends string ? string | null | undefined : null | undefined;
content: S extends string ? string | null | undefined : null | undefined;
},
) {
const { generated, source, original, name, content } = mapping;
if (!source) {
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
null,
null,
null,
null,
null,
);
}
assert<Pos>(original);
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
source as string,
original.line - 1,
original.column,
name,
content,
);
}
/*
export function addOriginalScope(
map: GenMapping,
data: {
start: Pos;
end: Pos;
source: string;
kind: string;
name?: string;
variables?: string[];
},
): OriginalScopeInfo {
const { start, end, source, kind, name, variables } = data;
const {
_sources: sources,
_sourcesContent: sourcesContent,
_originalScopes: originalScopes,
_names: names,
} = cast(map);
const index = put(sources, source);
if (index === sourcesContent.length) sourcesContent[index] = null;
if (index === originalScopes.length) originalScopes[index] = [];
const kindIndex = put(names, kind);
const scope: OriginalScope = name
? [start.line - 1, start.column, end.line - 1, end.column, kindIndex, put(names, name)]
: [start.line - 1, start.column, end.line - 1, end.column, kindIndex];
if (variables) {
scope.vars = variables.map((v) => put(names, v));
}
const len = originalScopes[index].push(scope);
return [index, len - 1, variables];
}
*/
// Generated Ranges
/*
export function addGeneratedRange(
map: GenMapping,
data: {
start: Pos;
isScope: boolean;
originalScope?: OriginalScopeInfo;
callsite?: OriginalPos;
},
): GeneratedRangeInfo {
const { start, isScope, originalScope, callsite } = data;
const {
_originalScopes: originalScopes,
_sources: sources,
_sourcesContent: sourcesContent,
_generatedRanges: generatedRanges,
} = cast(map);
const range: GeneratedRange = [
start.line - 1,
start.column,
0,
0,
originalScope ? originalScope[0] : -1,
originalScope ? originalScope[1] : -1,
];
if (originalScope?.[2]) {
range.bindings = originalScope[2].map(() => [[-1]]);
}
if (callsite) {
const index = put(sources, callsite.source);
if (index === sourcesContent.length) sourcesContent[index] = null;
if (index === originalScopes.length) originalScopes[index] = [];
range.callsite = [index, callsite.line - 1, callsite.column];
}
if (isScope) range.isScope = true;
generatedRanges.push(range);
return [range, originalScope?.[2]];
}
export function setEndPosition(range: GeneratedRangeInfo, pos: Pos) {
range[0][2] = pos.line - 1;
range[0][3] = pos.column;
}
export function addBinding(
map: GenMapping,
range: GeneratedRangeInfo,
variable: string,
expression: string | BindingExpressionRange,
) {
const { _names: names } = cast(map);
const bindings = (range[0].bindings ||= []);
const vars = range[1];
const index = vars!.indexOf(variable);
const binding = getIndex(bindings, index);
if (typeof expression === 'string') binding[0] = [put(names, expression)];
else {
const { start } = expression;
binding.push([put(names, expression.expression), start.line - 1, start.column]);
}
}
*/

82
node_modules/@jridgewell/gen-mapping/src/set-array.ts generated vendored Normal file
View File

@ -0,0 +1,82 @@
type Key = string | number | symbol;
/**
* SetArray acts like a `Set` (allowing only one occurrence of a string `key`), but provides the
* index of the `key` in the backing array.
*
* This is designed to allow synchronizing a second array with the contents of the backing array,
* like how in a sourcemap `sourcesContent[i]` is the source content associated with `source[i]`,
* and there are never duplicates.
*/
export class SetArray<T extends Key = Key> {
declare private _indexes: Record<T, number | undefined>;
declare array: readonly T[];
constructor() {
this._indexes = { __proto__: null } as any;
this.array = [];
}
}
interface PublicSet<T extends Key> {
array: T[];
_indexes: SetArray<T>['_indexes'];
}
/**
* Typescript doesn't allow friend access to private fields, so this just casts the set into a type
* with public access modifiers.
*/
function cast<T extends Key>(set: SetArray<T>): PublicSet<T> {
return set as any;
}
/**
* Gets the index associated with `key` in the backing array, if it is already present.
*/
export function get<T extends Key>(setarr: SetArray<T>, key: T): number | undefined {
return cast(setarr)._indexes[key];
}
/**
* Puts `key` into the backing array, if it is not already present. Returns
* the index of the `key` in the backing array.
*/
export function put<T extends Key>(setarr: SetArray<T>, key: T): number {
// The key may or may not be present. If it is present, it's a number.
const index = get(setarr, key);
if (index !== undefined) return index;
const { array, _indexes: indexes } = cast(setarr);
const length = array.push(key);
return (indexes[key] = length - 1);
}
/**
* Pops the last added item out of the SetArray.
*/
export function pop<T extends Key>(setarr: SetArray<T>): void {
const { array, _indexes: indexes } = cast(setarr);
if (array.length === 0) return;
const last = array.pop()!;
indexes[last] = undefined;
}
/**
* Removes the key, if it exists in the set.
*/
export function remove<T extends Key>(setarr: SetArray<T>, key: T): void {
const index = get(setarr, key);
if (index === undefined) return;
const { array, _indexes: indexes } = cast(setarr);
for (let i = index + 1; i < array.length; i++) {
const k = array[i];
array[i - 1] = k;
indexes[k]!--;
}
indexes[key] = undefined;
array.pop();
}

View File

@ -0,0 +1,16 @@
type GeneratedColumn = number;
type SourcesIndex = number;
type SourceLine = number;
type SourceColumn = number;
type NamesIndex = number;
export type SourceMapSegment =
| [GeneratedColumn]
| [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn]
| [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn, NamesIndex];
export const COLUMN = 0;
export const SOURCES_INDEX = 1;
export const SOURCE_LINE = 2;
export const SOURCE_COLUMN = 3;
export const NAMES_INDEX = 4;

61
node_modules/@jridgewell/gen-mapping/src/types.ts generated vendored Normal file
View File

@ -0,0 +1,61 @@
// import type { GeneratedRange, OriginalScope } from '@jridgewell/sourcemap-codec';
import type { SourceMapSegment } from './sourcemap-segment';
export interface SourceMapV3 {
file?: string | null;
names: readonly string[];
sourceRoot?: string;
sources: readonly (string | null)[];
sourcesContent?: readonly (string | null)[];
version: 3;
ignoreList?: readonly number[];
}
export interface EncodedSourceMap extends SourceMapV3 {
mappings: string;
// originalScopes: string[];
// generatedRanges: string;
}
export interface DecodedSourceMap extends SourceMapV3 {
mappings: readonly SourceMapSegment[][];
// originalScopes: readonly OriginalScope[][];
// generatedRanges: readonly GeneratedRange[];
}
export interface Pos {
line: number; // 1-based
column: number; // 0-based
}
export interface OriginalPos extends Pos {
source: string;
}
export interface BindingExpressionRange {
start: Pos;
expression: string;
}
// export type OriginalScopeInfo = [number, number, string[] | undefined];
// export type GeneratedRangeInfo = [GeneratedRange, string[] | undefined];
export type Mapping =
| {
generated: Pos;
source: undefined;
original: undefined;
name: undefined;
}
| {
generated: Pos;
source: string;
original: Pos;
name: string;
}
| {
generated: Pos;
source: string;
original: Pos;
name: undefined;
};

View File

@ -0,0 +1,89 @@
import type { SourceMapInput } from '@jridgewell/trace-mapping';
import type { DecodedSourceMap, EncodedSourceMap, Pos, Mapping } from './types.cts';
export type { DecodedSourceMap, EncodedSourceMap, Mapping };
export type Options = {
file?: string | null;
sourceRoot?: string | null;
};
/**
* Provides the state to generate a sourcemap.
*/
export declare class GenMapping {
private _names;
private _sources;
private _sourcesContent;
private _mappings;
private _ignoreList;
file: string | null | undefined;
sourceRoot: string | null | undefined;
constructor({ file, sourceRoot }?: Options);
}
/**
* A low-level API to associate a generated position with an original source position. Line and
* column here are 0-based, unlike `addMapping`.
*/
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source?: null, sourceLine?: null, sourceColumn?: null, name?: null, content?: null): void;
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source: string, sourceLine: number, sourceColumn: number, name?: null, content?: string | null): void;
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source: string, sourceLine: number, sourceColumn: number, name: string, content?: string | null): void;
/**
* A high-level API to associate a generated position with an original source position. Line is
* 1-based, but column is 0-based, due to legacy behavior in `source-map` library.
*/
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source?: null;
original?: null;
name?: null;
content?: null;
}): void;
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source: string;
original: Pos;
name?: null;
content?: string | null;
}): void;
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source: string;
original: Pos;
name: string;
content?: string | null;
}): void;
/**
* Same as `addSegment`, but will only add the segment if it generates useful information in the
* resulting map. This only works correctly if segments are added **in order**, meaning you should
* not add a segment with a lower generated line/column than one that came before.
*/
export declare const maybeAddSegment: typeof addSegment;
/**
* Same as `addMapping`, but will only add the mapping if it generates useful information in the
* resulting map. This only works correctly if mappings are added **in order**, meaning you should
* not add a mapping with a lower generated line/column than one that came before.
*/
export declare const maybeAddMapping: typeof addMapping;
/**
* Adds/removes the content of the source file to the source map.
*/
export declare function setSourceContent(map: GenMapping, source: string, content: string | null): void;
export declare function setIgnore(map: GenMapping, source: string, ignore?: boolean): void;
/**
* Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export declare function toDecodedMap(map: GenMapping): DecodedSourceMap;
/**
* Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export declare function toEncodedMap(map: GenMapping): EncodedSourceMap;
/**
* Constructs a new GenMapping, using the already present mappings of the input.
*/
export declare function fromMap(input: SourceMapInput): GenMapping;
/**
* Returns an array of high-level mapping objects for every recorded segment, which could then be
* passed to the `source-map` library.
*/
export declare function allMappings(map: GenMapping): Mapping[];
//# sourceMappingURL=gen-mapping.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"gen-mapping.d.ts","sourceRoot":"","sources":["../src/gen-mapping.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,GAAG,EACH,OAAO,EAKR,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC;AAE5D,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAIF;;GAEG;AACH,qBAAa,UAAU;IACrB,QAAgB,MAAM,CAAmB;IACzC,QAAgB,QAAQ,CAAmB;IAC3C,QAAgB,eAAe,CAAoB;IACnD,QAAgB,SAAS,CAAuB;IAGhD,QAAgB,WAAW,CAAmB;IACtC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;gBAElC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAE,OAAY;CAW/C;AAoBD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,IAAI,EACb,UAAU,CAAC,EAAE,IAAI,EACjB,YAAY,CAAC,EAAE,IAAI,EACnB,IAAI,CAAC,EAAE,IAAI,EACX,OAAO,CAAC,EAAE,IAAI,GACb,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,IAAI,EACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,IAAI,CAAC;AAwBR;;;GAGG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE;IACP,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB,GACA,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE;IACP,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE;IACP,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,IAAI,CAAC;AAcR;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,UAqBpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,UAEpC,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAS9F;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAO,QAYvE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,gBAAgB,CAwB9D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,gBAAgB,CAO9D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,UAAU,CAYzD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,EAAE,CA0BtD"}

View File

@ -0,0 +1,89 @@
import type { SourceMapInput } from '@jridgewell/trace-mapping';
import type { DecodedSourceMap, EncodedSourceMap, Pos, Mapping } from './types.mts';
export type { DecodedSourceMap, EncodedSourceMap, Mapping };
export type Options = {
file?: string | null;
sourceRoot?: string | null;
};
/**
* Provides the state to generate a sourcemap.
*/
export declare class GenMapping {
private _names;
private _sources;
private _sourcesContent;
private _mappings;
private _ignoreList;
file: string | null | undefined;
sourceRoot: string | null | undefined;
constructor({ file, sourceRoot }?: Options);
}
/**
* A low-level API to associate a generated position with an original source position. Line and
* column here are 0-based, unlike `addMapping`.
*/
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source?: null, sourceLine?: null, sourceColumn?: null, name?: null, content?: null): void;
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source: string, sourceLine: number, sourceColumn: number, name?: null, content?: string | null): void;
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source: string, sourceLine: number, sourceColumn: number, name: string, content?: string | null): void;
/**
* A high-level API to associate a generated position with an original source position. Line is
* 1-based, but column is 0-based, due to legacy behavior in `source-map` library.
*/
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source?: null;
original?: null;
name?: null;
content?: null;
}): void;
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source: string;
original: Pos;
name?: null;
content?: string | null;
}): void;
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source: string;
original: Pos;
name: string;
content?: string | null;
}): void;
/**
* Same as `addSegment`, but will only add the segment if it generates useful information in the
* resulting map. This only works correctly if segments are added **in order**, meaning you should
* not add a segment with a lower generated line/column than one that came before.
*/
export declare const maybeAddSegment: typeof addSegment;
/**
* Same as `addMapping`, but will only add the mapping if it generates useful information in the
* resulting map. This only works correctly if mappings are added **in order**, meaning you should
* not add a mapping with a lower generated line/column than one that came before.
*/
export declare const maybeAddMapping: typeof addMapping;
/**
* Adds/removes the content of the source file to the source map.
*/
export declare function setSourceContent(map: GenMapping, source: string, content: string | null): void;
export declare function setIgnore(map: GenMapping, source: string, ignore?: boolean): void;
/**
* Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export declare function toDecodedMap(map: GenMapping): DecodedSourceMap;
/**
* Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export declare function toEncodedMap(map: GenMapping): EncodedSourceMap;
/**
* Constructs a new GenMapping, using the already present mappings of the input.
*/
export declare function fromMap(input: SourceMapInput): GenMapping;
/**
* Returns an array of high-level mapping objects for every recorded segment, which could then be
* passed to the `source-map` library.
*/
export declare function allMappings(map: GenMapping): Mapping[];
//# sourceMappingURL=gen-mapping.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"gen-mapping.d.ts","sourceRoot":"","sources":["../src/gen-mapping.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,GAAG,EACH,OAAO,EAKR,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC;AAE5D,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAIF;;GAEG;AACH,qBAAa,UAAU;IACrB,QAAgB,MAAM,CAAmB;IACzC,QAAgB,QAAQ,CAAmB;IAC3C,QAAgB,eAAe,CAAoB;IACnD,QAAgB,SAAS,CAAuB;IAGhD,QAAgB,WAAW,CAAmB;IACtC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;gBAElC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAE,OAAY;CAW/C;AAoBD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,IAAI,EACb,UAAU,CAAC,EAAE,IAAI,EACjB,YAAY,CAAC,EAAE,IAAI,EACnB,IAAI,CAAC,EAAE,IAAI,EACX,OAAO,CAAC,EAAE,IAAI,GACb,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,IAAI,EACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,IAAI,CAAC;AAwBR;;;GAGG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE;IACP,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB,GACA,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE;IACP,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE;IACP,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,IAAI,CAAC;AAcR;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,UAqBpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,UAEpC,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAS9F;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAO,QAYvE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,gBAAgB,CAwB9D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,gBAAgB,CAO9D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,UAAU,CAYzD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,EAAE,CA0BtD"}

View File

@ -0,0 +1,33 @@
type Key = string | number | symbol;
/**
* SetArray acts like a `Set` (allowing only one occurrence of a string `key`), but provides the
* index of the `key` in the backing array.
*
* This is designed to allow synchronizing a second array with the contents of the backing array,
* like how in a sourcemap `sourcesContent[i]` is the source content associated with `source[i]`,
* and there are never duplicates.
*/
export declare class SetArray<T extends Key = Key> {
private _indexes;
array: readonly T[];
constructor();
}
/**
* Gets the index associated with `key` in the backing array, if it is already present.
*/
export declare function get<T extends Key>(setarr: SetArray<T>, key: T): number | undefined;
/**
* Puts `key` into the backing array, if it is not already present. Returns
* the index of the `key` in the backing array.
*/
export declare function put<T extends Key>(setarr: SetArray<T>, key: T): number;
/**
* Pops the last added item out of the SetArray.
*/
export declare function pop<T extends Key>(setarr: SetArray<T>): void;
/**
* Removes the key, if it exists in the set.
*/
export declare function remove<T extends Key>(setarr: SetArray<T>, key: T): void;
export {};
//# sourceMappingURL=set-array.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"set-array.d.ts","sourceRoot":"","sources":["../src/set-array.ts"],"names":[],"mappings":"AAAA,KAAK,GAAG,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpC;;;;;;;GAOG;AACH,qBAAa,QAAQ,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG;IACvC,QAAgB,QAAQ,CAAgC;IAChD,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;;CAM7B;AAeD;;GAEG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,SAAS,CAElF;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,CAStE;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAM5D;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,CAYvE"}

View File

@ -0,0 +1,33 @@
type Key = string | number | symbol;
/**
* SetArray acts like a `Set` (allowing only one occurrence of a string `key`), but provides the
* index of the `key` in the backing array.
*
* This is designed to allow synchronizing a second array with the contents of the backing array,
* like how in a sourcemap `sourcesContent[i]` is the source content associated with `source[i]`,
* and there are never duplicates.
*/
export declare class SetArray<T extends Key = Key> {
private _indexes;
array: readonly T[];
constructor();
}
/**
* Gets the index associated with `key` in the backing array, if it is already present.
*/
export declare function get<T extends Key>(setarr: SetArray<T>, key: T): number | undefined;
/**
* Puts `key` into the backing array, if it is not already present. Returns
* the index of the `key` in the backing array.
*/
export declare function put<T extends Key>(setarr: SetArray<T>, key: T): number;
/**
* Pops the last added item out of the SetArray.
*/
export declare function pop<T extends Key>(setarr: SetArray<T>): void;
/**
* Removes the key, if it exists in the set.
*/
export declare function remove<T extends Key>(setarr: SetArray<T>, key: T): void;
export {};
//# sourceMappingURL=set-array.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"set-array.d.ts","sourceRoot":"","sources":["../src/set-array.ts"],"names":[],"mappings":"AAAA,KAAK,GAAG,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpC;;;;;;;GAOG;AACH,qBAAa,QAAQ,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG;IACvC,QAAgB,QAAQ,CAAgC;IAChD,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;;CAM7B;AAeD;;GAEG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,SAAS,CAElF;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,CAStE;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAM5D;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,CAYvE"}

View File

@ -0,0 +1,13 @@
type GeneratedColumn = number;
type SourcesIndex = number;
type SourceLine = number;
type SourceColumn = number;
type NamesIndex = number;
export type SourceMapSegment = [GeneratedColumn] | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn] | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn, NamesIndex];
export declare const COLUMN = 0;
export declare const SOURCES_INDEX = 1;
export declare const SOURCE_LINE = 2;
export declare const SOURCE_COLUMN = 3;
export declare const NAMES_INDEX = 4;
export {};
//# sourceMappingURL=sourcemap-segment.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"sourcemap-segment.d.ts","sourceRoot":"","sources":["../src/sourcemap-segment.ts"],"names":[],"mappings":"AAAA,KAAK,eAAe,GAAG,MAAM,CAAC;AAC9B,KAAK,YAAY,GAAG,MAAM,CAAC;AAC3B,KAAK,UAAU,GAAG,MAAM,CAAC;AACzB,KAAK,YAAY,GAAG,MAAM,CAAC;AAC3B,KAAK,UAAU,GAAG,MAAM,CAAC;AAEzB,MAAM,MAAM,gBAAgB,GACxB,CAAC,eAAe,CAAC,GACjB,CAAC,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,CAAC,GACzD,CAAC,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;AAE1E,eAAO,MAAM,MAAM,IAAI,CAAC;AACxB,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,WAAW,IAAI,CAAC"}

View File

@ -0,0 +1,13 @@
type GeneratedColumn = number;
type SourcesIndex = number;
type SourceLine = number;
type SourceColumn = number;
type NamesIndex = number;
export type SourceMapSegment = [GeneratedColumn] | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn] | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn, NamesIndex];
export declare const COLUMN = 0;
export declare const SOURCES_INDEX = 1;
export declare const SOURCE_LINE = 2;
export declare const SOURCE_COLUMN = 3;
export declare const NAMES_INDEX = 4;
export {};
//# sourceMappingURL=sourcemap-segment.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"sourcemap-segment.d.ts","sourceRoot":"","sources":["../src/sourcemap-segment.ts"],"names":[],"mappings":"AAAA,KAAK,eAAe,GAAG,MAAM,CAAC;AAC9B,KAAK,YAAY,GAAG,MAAM,CAAC;AAC3B,KAAK,UAAU,GAAG,MAAM,CAAC;AACzB,KAAK,YAAY,GAAG,MAAM,CAAC;AAC3B,KAAK,UAAU,GAAG,MAAM,CAAC;AAEzB,MAAM,MAAM,gBAAgB,GACxB,CAAC,eAAe,CAAC,GACjB,CAAC,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,CAAC,GACzD,CAAC,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;AAE1E,eAAO,MAAM,MAAM,IAAI,CAAC;AACxB,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,WAAW,IAAI,CAAC"}

44
node_modules/@jridgewell/gen-mapping/types/types.d.cts generated vendored Normal file
View File

@ -0,0 +1,44 @@
import type { SourceMapSegment } from './sourcemap-segment.cts';
export interface SourceMapV3 {
file?: string | null;
names: readonly string[];
sourceRoot?: string;
sources: readonly (string | null)[];
sourcesContent?: readonly (string | null)[];
version: 3;
ignoreList?: readonly number[];
}
export interface EncodedSourceMap extends SourceMapV3 {
mappings: string;
}
export interface DecodedSourceMap extends SourceMapV3 {
mappings: readonly SourceMapSegment[][];
}
export interface Pos {
line: number;
column: number;
}
export interface OriginalPos extends Pos {
source: string;
}
export interface BindingExpressionRange {
start: Pos;
expression: string;
}
export type Mapping = {
generated: Pos;
source: undefined;
original: undefined;
name: undefined;
} | {
generated: Pos;
source: string;
original: Pos;
name: string;
} | {
generated: Pos;
source: string;
original: Pos;
name: undefined;
};
//# sourceMappingURL=types.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IACpC,cAAc,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAC5C,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,QAAQ,EAAE,MAAM,CAAC;CAGlB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,QAAQ,EAAE,SAAS,gBAAgB,EAAE,EAAE,CAAC;CAGzC;AAED,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAY,SAAQ,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,GAAG,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB;AAKD,MAAM,MAAM,OAAO,GACf;IACE,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;CACjB,GACD;IACE,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC"}

44
node_modules/@jridgewell/gen-mapping/types/types.d.mts generated vendored Normal file
View File

@ -0,0 +1,44 @@
import type { SourceMapSegment } from './sourcemap-segment.mts';
export interface SourceMapV3 {
file?: string | null;
names: readonly string[];
sourceRoot?: string;
sources: readonly (string | null)[];
sourcesContent?: readonly (string | null)[];
version: 3;
ignoreList?: readonly number[];
}
export interface EncodedSourceMap extends SourceMapV3 {
mappings: string;
}
export interface DecodedSourceMap extends SourceMapV3 {
mappings: readonly SourceMapSegment[][];
}
export interface Pos {
line: number;
column: number;
}
export interface OriginalPos extends Pos {
source: string;
}
export interface BindingExpressionRange {
start: Pos;
expression: string;
}
export type Mapping = {
generated: Pos;
source: undefined;
original: undefined;
name: undefined;
} | {
generated: Pos;
source: string;
original: Pos;
name: string;
} | {
generated: Pos;
source: string;
original: Pos;
name: undefined;
};
//# sourceMappingURL=types.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IACpC,cAAc,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAC5C,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,QAAQ,EAAE,MAAM,CAAC;CAGlB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,QAAQ,EAAE,SAAS,gBAAgB,EAAE,EAAE,CAAC;CAGzC;AAED,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAY,SAAQ,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,GAAG,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB;AAKD,MAAM,MAAM,OAAO,GACf;IACE,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;CACjB,GACD;IACE,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC"}

19
node_modules/@jridgewell/resolve-uri/LICENSE generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright 2019 Justin Ridgewell <jridgewell@google.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

40
node_modules/@jridgewell/resolve-uri/README.md generated vendored Normal file
View File

@ -0,0 +1,40 @@
# @jridgewell/resolve-uri
> Resolve a URI relative to an optional base URI
Resolve any combination of absolute URIs, protocol-realtive URIs, absolute paths, or relative paths.
## Installation
```sh
npm install @jridgewell/resolve-uri
```
## Usage
```typescript
function resolve(input: string, base?: string): string;
```
```js
import resolve from '@jridgewell/resolve-uri';
resolve('foo', 'https://example.com'); // => 'https://example.com/foo'
```
| Input | Base | Resolution | Explanation |
|-----------------------|-------------------------|--------------------------------|--------------------------------------------------------------|
| `https://example.com` | _any_ | `https://example.com/` | Input is normalized only |
| `//example.com` | `https://base.com/` | `https://example.com/` | Input inherits the base's protocol |
| `//example.com` | _rest_ | `//example.com/` | Input is normalized only |
| `/example` | `https://base.com/` | `https://base.com/example` | Input inherits the base's origin |
| `/example` | `//base.com/` | `//base.com/example` | Input inherits the base's host and remains protocol relative |
| `/example` | _rest_ | `/example` | Input is normalized only |
| `example` | `https://base.com/dir/` | `https://base.com/dir/example` | Input is joined with the base |
| `example` | `https://base.com/file` | `https://base.com/example` | Input is joined with the base without its file |
| `example` | `//base.com/dir/` | `//base.com/dir/example` | Input is joined with the base's last directory |
| `example` | `//base.com/file` | `//base.com/example` | Input is joined with the base without its file |
| `example` | `/base/dir/` | `/base/dir/example` | Input is joined with the base's last directory |
| `example` | `/base/file` | `/base/example` | Input is joined with the base without its file |
| `example` | `base/dir/` | `base/dir/example` | Input is joined with the base's last directory |
| `example` | `base/file` | `base/example` | Input is joined with the base without its file |

View File

@ -0,0 +1,232 @@
// Matches the scheme of a URL, eg "http://"
const schemeRegex = /^[\w+.-]+:\/\//;
/**
* Matches the parts of a URL:
* 1. Scheme, including ":", guaranteed.
* 2. User/password, including "@", optional.
* 3. Host, guaranteed.
* 4. Port, including ":", optional.
* 5. Path, including "/", optional.
* 6. Query, including "?", optional.
* 7. Hash, including "#", optional.
*/
const urlRegex = /^([\w+.-]+:)\/\/([^@/#?]*@)?([^:/#?]*)(:\d+)?(\/[^#?]*)?(\?[^#]*)?(#.*)?/;
/**
* File URLs are weird. They dont' need the regular `//` in the scheme, they may or may not start
* with a leading `/`, they can have a domain (but only if they don't start with a Windows drive).
*
* 1. Host, optional.
* 2. Path, which may include "/", guaranteed.
* 3. Query, including "?", optional.
* 4. Hash, including "#", optional.
*/
const fileRegex = /^file:(?:\/\/((?![a-z]:)[^/#?]*)?)?(\/?[^#?]*)(\?[^#]*)?(#.*)?/i;
function isAbsoluteUrl(input) {
return schemeRegex.test(input);
}
function isSchemeRelativeUrl(input) {
return input.startsWith('//');
}
function isAbsolutePath(input) {
return input.startsWith('/');
}
function isFileUrl(input) {
return input.startsWith('file:');
}
function isRelative(input) {
return /^[.?#]/.test(input);
}
function parseAbsoluteUrl(input) {
const match = urlRegex.exec(input);
return makeUrl(match[1], match[2] || '', match[3], match[4] || '', match[5] || '/', match[6] || '', match[7] || '');
}
function parseFileUrl(input) {
const match = fileRegex.exec(input);
const path = match[2];
return makeUrl('file:', '', match[1] || '', '', isAbsolutePath(path) ? path : '/' + path, match[3] || '', match[4] || '');
}
function makeUrl(scheme, user, host, port, path, query, hash) {
return {
scheme,
user,
host,
port,
path,
query,
hash,
type: 7 /* Absolute */,
};
}
function parseUrl(input) {
if (isSchemeRelativeUrl(input)) {
const url = parseAbsoluteUrl('http:' + input);
url.scheme = '';
url.type = 6 /* SchemeRelative */;
return url;
}
if (isAbsolutePath(input)) {
const url = parseAbsoluteUrl('http://foo.com' + input);
url.scheme = '';
url.host = '';
url.type = 5 /* AbsolutePath */;
return url;
}
if (isFileUrl(input))
return parseFileUrl(input);
if (isAbsoluteUrl(input))
return parseAbsoluteUrl(input);
const url = parseAbsoluteUrl('http://foo.com/' + input);
url.scheme = '';
url.host = '';
url.type = input
? input.startsWith('?')
? 3 /* Query */
: input.startsWith('#')
? 2 /* Hash */
: 4 /* RelativePath */
: 1 /* Empty */;
return url;
}
function stripPathFilename(path) {
// If a path ends with a parent directory "..", then it's a relative path with excess parent
// paths. It's not a file, so we can't strip it.
if (path.endsWith('/..'))
return path;
const index = path.lastIndexOf('/');
return path.slice(0, index + 1);
}
function mergePaths(url, base) {
normalizePath(base, base.type);
// If the path is just a "/", then it was an empty path to begin with (remember, we're a relative
// path).
if (url.path === '/') {
url.path = base.path;
}
else {
// Resolution happens relative to the base path's directory, not the file.
url.path = stripPathFilename(base.path) + url.path;
}
}
/**
* The path can have empty directories "//", unneeded parents "foo/..", or current directory
* "foo/.". We need to normalize to a standard representation.
*/
function normalizePath(url, type) {
const rel = type <= 4 /* RelativePath */;
const pieces = url.path.split('/');
// We need to preserve the first piece always, so that we output a leading slash. The item at
// pieces[0] is an empty string.
let pointer = 1;
// Positive is the number of real directories we've output, used for popping a parent directory.
// Eg, "foo/bar/.." will have a positive 2, and we can decrement to be left with just "foo".
let positive = 0;
// We need to keep a trailing slash if we encounter an empty directory (eg, splitting "foo/" will
// generate `["foo", ""]` pieces). And, if we pop a parent directory. But once we encounter a
// real directory, we won't need to append, unless the other conditions happen again.
let addTrailingSlash = false;
for (let i = 1; i < pieces.length; i++) {
const piece = pieces[i];
// An empty directory, could be a trailing slash, or just a double "//" in the path.
if (!piece) {
addTrailingSlash = true;
continue;
}
// If we encounter a real directory, then we don't need to append anymore.
addTrailingSlash = false;
// A current directory, which we can always drop.
if (piece === '.')
continue;
// A parent directory, we need to see if there are any real directories we can pop. Else, we
// have an excess of parents, and we'll need to keep the "..".
if (piece === '..') {
if (positive) {
addTrailingSlash = true;
positive--;
pointer--;
}
else if (rel) {
// If we're in a relativePath, then we need to keep the excess parents. Else, in an absolute
// URL, protocol relative URL, or an absolute path, we don't need to keep excess.
pieces[pointer++] = piece;
}
continue;
}
// We've encountered a real directory. Move it to the next insertion pointer, which accounts for
// any popped or dropped directories.
pieces[pointer++] = piece;
positive++;
}
let path = '';
for (let i = 1; i < pointer; i++) {
path += '/' + pieces[i];
}
if (!path || (addTrailingSlash && !path.endsWith('/..'))) {
path += '/';
}
url.path = path;
}
/**
* Attempts to resolve `input` URL/path relative to `base`.
*/
function resolve(input, base) {
if (!input && !base)
return '';
const url = parseUrl(input);
let inputType = url.type;
if (base && inputType !== 7 /* Absolute */) {
const baseUrl = parseUrl(base);
const baseType = baseUrl.type;
switch (inputType) {
case 1 /* Empty */:
url.hash = baseUrl.hash;
// fall through
case 2 /* Hash */:
url.query = baseUrl.query;
// fall through
case 3 /* Query */:
case 4 /* RelativePath */:
mergePaths(url, baseUrl);
// fall through
case 5 /* AbsolutePath */:
// The host, user, and port are joined, you can't copy one without the others.
url.user = baseUrl.user;
url.host = baseUrl.host;
url.port = baseUrl.port;
// fall through
case 6 /* SchemeRelative */:
// The input doesn't have a schema at least, so we need to copy at least that over.
url.scheme = baseUrl.scheme;
}
if (baseType > inputType)
inputType = baseType;
}
normalizePath(url, inputType);
const queryHash = url.query + url.hash;
switch (inputType) {
// This is impossible, because of the empty checks at the start of the function.
// case UrlType.Empty:
case 2 /* Hash */:
case 3 /* Query */:
return queryHash;
case 4 /* RelativePath */: {
// The first char is always a "/", and we need it to be relative.
const path = url.path.slice(1);
if (!path)
return queryHash || '.';
if (isRelative(base || input) && !isRelative(path)) {
// If base started with a leading ".", or there is no base and input started with a ".",
// then we need to ensure that the relative path starts with a ".". We don't know if
// relative starts with a "..", though, so check before prepending.
return './' + path + queryHash;
}
return path + queryHash;
}
case 5 /* AbsolutePath */:
return url.path + queryHash;
default:
return url.scheme + '//' + url.user + url.host + url.port + url.path + queryHash;
}
}
export { resolve as default };
//# sourceMappingURL=resolve-uri.mjs.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,240 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.resolveURI = factory());
})(this, (function () { 'use strict';
// Matches the scheme of a URL, eg "http://"
const schemeRegex = /^[\w+.-]+:\/\//;
/**
* Matches the parts of a URL:
* 1. Scheme, including ":", guaranteed.
* 2. User/password, including "@", optional.
* 3. Host, guaranteed.
* 4. Port, including ":", optional.
* 5. Path, including "/", optional.
* 6. Query, including "?", optional.
* 7. Hash, including "#", optional.
*/
const urlRegex = /^([\w+.-]+:)\/\/([^@/#?]*@)?([^:/#?]*)(:\d+)?(\/[^#?]*)?(\?[^#]*)?(#.*)?/;
/**
* File URLs are weird. They dont' need the regular `//` in the scheme, they may or may not start
* with a leading `/`, they can have a domain (but only if they don't start with a Windows drive).
*
* 1. Host, optional.
* 2. Path, which may include "/", guaranteed.
* 3. Query, including "?", optional.
* 4. Hash, including "#", optional.
*/
const fileRegex = /^file:(?:\/\/((?![a-z]:)[^/#?]*)?)?(\/?[^#?]*)(\?[^#]*)?(#.*)?/i;
function isAbsoluteUrl(input) {
return schemeRegex.test(input);
}
function isSchemeRelativeUrl(input) {
return input.startsWith('//');
}
function isAbsolutePath(input) {
return input.startsWith('/');
}
function isFileUrl(input) {
return input.startsWith('file:');
}
function isRelative(input) {
return /^[.?#]/.test(input);
}
function parseAbsoluteUrl(input) {
const match = urlRegex.exec(input);
return makeUrl(match[1], match[2] || '', match[3], match[4] || '', match[5] || '/', match[6] || '', match[7] || '');
}
function parseFileUrl(input) {
const match = fileRegex.exec(input);
const path = match[2];
return makeUrl('file:', '', match[1] || '', '', isAbsolutePath(path) ? path : '/' + path, match[3] || '', match[4] || '');
}
function makeUrl(scheme, user, host, port, path, query, hash) {
return {
scheme,
user,
host,
port,
path,
query,
hash,
type: 7 /* Absolute */,
};
}
function parseUrl(input) {
if (isSchemeRelativeUrl(input)) {
const url = parseAbsoluteUrl('http:' + input);
url.scheme = '';
url.type = 6 /* SchemeRelative */;
return url;
}
if (isAbsolutePath(input)) {
const url = parseAbsoluteUrl('http://foo.com' + input);
url.scheme = '';
url.host = '';
url.type = 5 /* AbsolutePath */;
return url;
}
if (isFileUrl(input))
return parseFileUrl(input);
if (isAbsoluteUrl(input))
return parseAbsoluteUrl(input);
const url = parseAbsoluteUrl('http://foo.com/' + input);
url.scheme = '';
url.host = '';
url.type = input
? input.startsWith('?')
? 3 /* Query */
: input.startsWith('#')
? 2 /* Hash */
: 4 /* RelativePath */
: 1 /* Empty */;
return url;
}
function stripPathFilename(path) {
// If a path ends with a parent directory "..", then it's a relative path with excess parent
// paths. It's not a file, so we can't strip it.
if (path.endsWith('/..'))
return path;
const index = path.lastIndexOf('/');
return path.slice(0, index + 1);
}
function mergePaths(url, base) {
normalizePath(base, base.type);
// If the path is just a "/", then it was an empty path to begin with (remember, we're a relative
// path).
if (url.path === '/') {
url.path = base.path;
}
else {
// Resolution happens relative to the base path's directory, not the file.
url.path = stripPathFilename(base.path) + url.path;
}
}
/**
* The path can have empty directories "//", unneeded parents "foo/..", or current directory
* "foo/.". We need to normalize to a standard representation.
*/
function normalizePath(url, type) {
const rel = type <= 4 /* RelativePath */;
const pieces = url.path.split('/');
// We need to preserve the first piece always, so that we output a leading slash. The item at
// pieces[0] is an empty string.
let pointer = 1;
// Positive is the number of real directories we've output, used for popping a parent directory.
// Eg, "foo/bar/.." will have a positive 2, and we can decrement to be left with just "foo".
let positive = 0;
// We need to keep a trailing slash if we encounter an empty directory (eg, splitting "foo/" will
// generate `["foo", ""]` pieces). And, if we pop a parent directory. But once we encounter a
// real directory, we won't need to append, unless the other conditions happen again.
let addTrailingSlash = false;
for (let i = 1; i < pieces.length; i++) {
const piece = pieces[i];
// An empty directory, could be a trailing slash, or just a double "//" in the path.
if (!piece) {
addTrailingSlash = true;
continue;
}
// If we encounter a real directory, then we don't need to append anymore.
addTrailingSlash = false;
// A current directory, which we can always drop.
if (piece === '.')
continue;
// A parent directory, we need to see if there are any real directories we can pop. Else, we
// have an excess of parents, and we'll need to keep the "..".
if (piece === '..') {
if (positive) {
addTrailingSlash = true;
positive--;
pointer--;
}
else if (rel) {
// If we're in a relativePath, then we need to keep the excess parents. Else, in an absolute
// URL, protocol relative URL, or an absolute path, we don't need to keep excess.
pieces[pointer++] = piece;
}
continue;
}
// We've encountered a real directory. Move it to the next insertion pointer, which accounts for
// any popped or dropped directories.
pieces[pointer++] = piece;
positive++;
}
let path = '';
for (let i = 1; i < pointer; i++) {
path += '/' + pieces[i];
}
if (!path || (addTrailingSlash && !path.endsWith('/..'))) {
path += '/';
}
url.path = path;
}
/**
* Attempts to resolve `input` URL/path relative to `base`.
*/
function resolve(input, base) {
if (!input && !base)
return '';
const url = parseUrl(input);
let inputType = url.type;
if (base && inputType !== 7 /* Absolute */) {
const baseUrl = parseUrl(base);
const baseType = baseUrl.type;
switch (inputType) {
case 1 /* Empty */:
url.hash = baseUrl.hash;
// fall through
case 2 /* Hash */:
url.query = baseUrl.query;
// fall through
case 3 /* Query */:
case 4 /* RelativePath */:
mergePaths(url, baseUrl);
// fall through
case 5 /* AbsolutePath */:
// The host, user, and port are joined, you can't copy one without the others.
url.user = baseUrl.user;
url.host = baseUrl.host;
url.port = baseUrl.port;
// fall through
case 6 /* SchemeRelative */:
// The input doesn't have a schema at least, so we need to copy at least that over.
url.scheme = baseUrl.scheme;
}
if (baseType > inputType)
inputType = baseType;
}
normalizePath(url, inputType);
const queryHash = url.query + url.hash;
switch (inputType) {
// This is impossible, because of the empty checks at the start of the function.
// case UrlType.Empty:
case 2 /* Hash */:
case 3 /* Query */:
return queryHash;
case 4 /* RelativePath */: {
// The first char is always a "/", and we need it to be relative.
const path = url.path.slice(1);
if (!path)
return queryHash || '.';
if (isRelative(base || input) && !isRelative(path)) {
// If base started with a leading ".", or there is no base and input started with a ".",
// then we need to ensure that the relative path starts with a ".". We don't know if
// relative starts with a "..", though, so check before prepending.
return './' + path + queryHash;
}
return path + queryHash;
}
case 5 /* AbsolutePath */:
return url.path + queryHash;
default:
return url.scheme + '//' + url.user + url.host + url.port + url.path + queryHash;
}
}
return resolve;
}));
//# sourceMappingURL=resolve-uri.umd.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,4 @@
/**
* Attempts to resolve `input` URL/path relative to `base`.
*/
export default function resolve(input: string, base: string | undefined): string;

69
node_modules/@jridgewell/resolve-uri/package.json generated vendored Normal file
View File

@ -0,0 +1,69 @@
{
"name": "@jridgewell/resolve-uri",
"version": "3.1.2",
"description": "Resolve a URI relative to an optional base URI",
"keywords": [
"resolve",
"uri",
"url",
"path"
],
"author": "Justin Ridgewell <justin@ridgewell.name>",
"license": "MIT",
"repository": "https://github.com/jridgewell/resolve-uri",
"main": "dist/resolve-uri.umd.js",
"module": "dist/resolve-uri.mjs",
"types": "dist/types/resolve-uri.d.ts",
"exports": {
".": [
{
"types": "./dist/types/resolve-uri.d.ts",
"browser": "./dist/resolve-uri.umd.js",
"require": "./dist/resolve-uri.umd.js",
"import": "./dist/resolve-uri.mjs"
},
"./dist/resolve-uri.umd.js"
],
"./package.json": "./package.json"
},
"files": [
"dist"
],
"engines": {
"node": ">=6.0.0"
},
"scripts": {
"prebuild": "rm -rf dist",
"build": "run-s -n build:*",
"build:rollup": "rollup -c rollup.config.js",
"build:ts": "tsc --project tsconfig.build.json",
"lint": "run-s -n lint:*",
"lint:prettier": "npm run test:lint:prettier -- --write",
"lint:ts": "npm run test:lint:ts -- --fix",
"pretest": "run-s build:rollup",
"test": "run-s -n test:lint test:only",
"test:debug": "mocha --inspect-brk",
"test:lint": "run-s -n test:lint:*",
"test:lint:prettier": "prettier --check '{src,test}/**/*.ts'",
"test:lint:ts": "eslint '{src,test}/**/*.ts'",
"test:only": "mocha",
"test:coverage": "c8 mocha",
"test:watch": "mocha --watch",
"prepublishOnly": "npm run preversion",
"preversion": "run-s test build"
},
"devDependencies": {
"@jridgewell/resolve-uri-latest": "npm:@jridgewell/resolve-uri@*",
"@rollup/plugin-typescript": "8.3.0",
"@typescript-eslint/eslint-plugin": "5.10.0",
"@typescript-eslint/parser": "5.10.0",
"c8": "7.11.0",
"eslint": "8.7.0",
"eslint-config-prettier": "8.3.0",
"mocha": "9.2.0",
"npm-run-all": "4.1.5",
"prettier": "2.5.1",
"rollup": "2.66.0",
"typescript": "4.5.5"
}
}

19
node_modules/@jridgewell/sourcemap-codec/LICENSE generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright 2024 Justin Ridgewell <justin@ridgewell.name>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

264
node_modules/@jridgewell/sourcemap-codec/README.md generated vendored Normal file
View File

@ -0,0 +1,264 @@
# @jridgewell/sourcemap-codec
Encode/decode the `mappings` property of a [sourcemap](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit).
## Why?
Sourcemaps are difficult to generate and manipulate, because the `mappings` property the part that actually links the generated code back to the original source is encoded using an obscure method called [Variable-length quantity](https://en.wikipedia.org/wiki/Variable-length_quantity). On top of that, each segment in the mapping contains offsets rather than absolute indices, which means that you can't look at a segment in isolation you have to understand the whole sourcemap.
This package makes the process slightly easier.
## Installation
```bash
npm install @jridgewell/sourcemap-codec
```
## Usage
```js
import { encode, decode } from '@jridgewell/sourcemap-codec';
var decoded = decode( ';EAEEA,EAAE,EAAC,CAAE;ECQY,UACC' );
assert.deepEqual( decoded, [
// the first line (of the generated code) has no mappings,
// as shown by the starting semi-colon (which separates lines)
[],
// the second line contains four (comma-separated) segments
[
// segments are encoded as you'd expect:
// [ generatedCodeColumn, sourceIndex, sourceCodeLine, sourceCodeColumn, nameIndex ]
// i.e. the first segment begins at column 2, and maps back to the second column
// of the second line (both zero-based) of the 0th source, and uses the 0th
// name in the `map.names` array
[ 2, 0, 2, 2, 0 ],
// the remaining segments are 4-length rather than 5-length,
// because they don't map a name
[ 4, 0, 2, 4 ],
[ 6, 0, 2, 5 ],
[ 7, 0, 2, 7 ]
],
// the final line contains two segments
[
[ 2, 1, 10, 19 ],
[ 12, 1, 11, 20 ]
]
]);
var encoded = encode( decoded );
assert.equal( encoded, ';EAEEA,EAAE,EAAC,CAAE;ECQY,UACC' );
```
## Benchmarks
```
node v20.10.0
amp.js.map - 45120 segments
Decode Memory Usage:
local code 5815135 bytes
@jridgewell/sourcemap-codec 1.4.15 5868160 bytes
sourcemap-codec 5492584 bytes
source-map-0.6.1 13569984 bytes
source-map-0.8.0 6390584 bytes
chrome dev tools 8011136 bytes
Smallest memory usage is sourcemap-codec
Decode speed:
decode: local code x 492 ops/sec ±1.22% (90 runs sampled)
decode: @jridgewell/sourcemap-codec 1.4.15 x 499 ops/sec ±1.16% (89 runs sampled)
decode: sourcemap-codec x 376 ops/sec ±1.66% (89 runs sampled)
decode: source-map-0.6.1 x 34.99 ops/sec ±0.94% (48 runs sampled)
decode: source-map-0.8.0 x 351 ops/sec ±0.07% (95 runs sampled)
chrome dev tools x 165 ops/sec ±0.91% (86 runs sampled)
Fastest is decode: @jridgewell/sourcemap-codec 1.4.15
Encode Memory Usage:
local code 444248 bytes
@jridgewell/sourcemap-codec 1.4.15 623024 bytes
sourcemap-codec 8696280 bytes
source-map-0.6.1 8745176 bytes
source-map-0.8.0 8736624 bytes
Smallest memory usage is local code
Encode speed:
encode: local code x 796 ops/sec ±0.11% (97 runs sampled)
encode: @jridgewell/sourcemap-codec 1.4.15 x 795 ops/sec ±0.25% (98 runs sampled)
encode: sourcemap-codec x 231 ops/sec ±0.83% (86 runs sampled)
encode: source-map-0.6.1 x 166 ops/sec ±0.57% (86 runs sampled)
encode: source-map-0.8.0 x 203 ops/sec ±0.45% (88 runs sampled)
Fastest is encode: local code,encode: @jridgewell/sourcemap-codec 1.4.15
***
babel.min.js.map - 347793 segments
Decode Memory Usage:
local code 35424960 bytes
@jridgewell/sourcemap-codec 1.4.15 35424696 bytes
sourcemap-codec 36033464 bytes
source-map-0.6.1 62253704 bytes
source-map-0.8.0 43843920 bytes
chrome dev tools 45111400 bytes
Smallest memory usage is @jridgewell/sourcemap-codec 1.4.15
Decode speed:
decode: local code x 38.18 ops/sec ±5.44% (52 runs sampled)
decode: @jridgewell/sourcemap-codec 1.4.15 x 38.36 ops/sec ±5.02% (52 runs sampled)
decode: sourcemap-codec x 34.05 ops/sec ±4.45% (47 runs sampled)
decode: source-map-0.6.1 x 4.31 ops/sec ±2.76% (15 runs sampled)
decode: source-map-0.8.0 x 55.60 ops/sec ±0.13% (73 runs sampled)
chrome dev tools x 16.94 ops/sec ±3.78% (46 runs sampled)
Fastest is decode: source-map-0.8.0
Encode Memory Usage:
local code 2606016 bytes
@jridgewell/sourcemap-codec 1.4.15 2626440 bytes
sourcemap-codec 21152576 bytes
source-map-0.6.1 25023928 bytes
source-map-0.8.0 25256448 bytes
Smallest memory usage is local code
Encode speed:
encode: local code x 127 ops/sec ±0.18% (83 runs sampled)
encode: @jridgewell/sourcemap-codec 1.4.15 x 128 ops/sec ±0.26% (83 runs sampled)
encode: sourcemap-codec x 29.31 ops/sec ±2.55% (53 runs sampled)
encode: source-map-0.6.1 x 18.85 ops/sec ±3.19% (36 runs sampled)
encode: source-map-0.8.0 x 19.34 ops/sec ±1.97% (36 runs sampled)
Fastest is encode: @jridgewell/sourcemap-codec 1.4.15
***
preact.js.map - 1992 segments
Decode Memory Usage:
local code 261696 bytes
@jridgewell/sourcemap-codec 1.4.15 244296 bytes
sourcemap-codec 302816 bytes
source-map-0.6.1 939176 bytes
source-map-0.8.0 336 bytes
chrome dev tools 587368 bytes
Smallest memory usage is source-map-0.8.0
Decode speed:
decode: local code x 17,782 ops/sec ±0.32% (97 runs sampled)
decode: @jridgewell/sourcemap-codec 1.4.15 x 17,863 ops/sec ±0.40% (100 runs sampled)
decode: sourcemap-codec x 12,453 ops/sec ±0.27% (101 runs sampled)
decode: source-map-0.6.1 x 1,288 ops/sec ±1.05% (96 runs sampled)
decode: source-map-0.8.0 x 9,289 ops/sec ±0.27% (101 runs sampled)
chrome dev tools x 4,769 ops/sec ±0.18% (100 runs sampled)
Fastest is decode: @jridgewell/sourcemap-codec 1.4.15
Encode Memory Usage:
local code 262944 bytes
@jridgewell/sourcemap-codec 1.4.15 25544 bytes
sourcemap-codec 323048 bytes
source-map-0.6.1 507808 bytes
source-map-0.8.0 507480 bytes
Smallest memory usage is @jridgewell/sourcemap-codec 1.4.15
Encode speed:
encode: local code x 24,207 ops/sec ±0.79% (95 runs sampled)
encode: @jridgewell/sourcemap-codec 1.4.15 x 24,288 ops/sec ±0.48% (96 runs sampled)
encode: sourcemap-codec x 6,761 ops/sec ±0.21% (100 runs sampled)
encode: source-map-0.6.1 x 5,374 ops/sec ±0.17% (99 runs sampled)
encode: source-map-0.8.0 x 5,633 ops/sec ±0.32% (99 runs sampled)
Fastest is encode: @jridgewell/sourcemap-codec 1.4.15,encode: local code
***
react.js.map - 5726 segments
Decode Memory Usage:
local code 678816 bytes
@jridgewell/sourcemap-codec 1.4.15 678816 bytes
sourcemap-codec 816400 bytes
source-map-0.6.1 2288864 bytes
source-map-0.8.0 721360 bytes
chrome dev tools 1012512 bytes
Smallest memory usage is local code
Decode speed:
decode: local code x 6,178 ops/sec ±0.19% (98 runs sampled)
decode: @jridgewell/sourcemap-codec 1.4.15 x 6,261 ops/sec ±0.22% (100 runs sampled)
decode: sourcemap-codec x 4,472 ops/sec ±0.90% (99 runs sampled)
decode: source-map-0.6.1 x 449 ops/sec ±0.31% (95 runs sampled)
decode: source-map-0.8.0 x 3,219 ops/sec ±0.13% (100 runs sampled)
chrome dev tools x 1,743 ops/sec ±0.20% (99 runs sampled)
Fastest is decode: @jridgewell/sourcemap-codec 1.4.15
Encode Memory Usage:
local code 140960 bytes
@jridgewell/sourcemap-codec 1.4.15 159808 bytes
sourcemap-codec 969304 bytes
source-map-0.6.1 930520 bytes
source-map-0.8.0 930248 bytes
Smallest memory usage is local code
Encode speed:
encode: local code x 8,013 ops/sec ±0.19% (100 runs sampled)
encode: @jridgewell/sourcemap-codec 1.4.15 x 7,989 ops/sec ±0.20% (101 runs sampled)
encode: sourcemap-codec x 2,472 ops/sec ±0.21% (99 runs sampled)
encode: source-map-0.6.1 x 2,200 ops/sec ±0.17% (99 runs sampled)
encode: source-map-0.8.0 x 2,220 ops/sec ±0.37% (99 runs sampled)
Fastest is encode: local code
***
vscode.map - 2141001 segments
Decode Memory Usage:
local code 198955264 bytes
@jridgewell/sourcemap-codec 1.4.15 199175352 bytes
sourcemap-codec 199102688 bytes
source-map-0.6.1 386323432 bytes
source-map-0.8.0 244116432 bytes
chrome dev tools 293734280 bytes
Smallest memory usage is local code
Decode speed:
decode: local code x 3.90 ops/sec ±22.21% (15 runs sampled)
decode: @jridgewell/sourcemap-codec 1.4.15 x 3.95 ops/sec ±23.53% (15 runs sampled)
decode: sourcemap-codec x 3.82 ops/sec ±17.94% (14 runs sampled)
decode: source-map-0.6.1 x 0.61 ops/sec ±7.81% (6 runs sampled)
decode: source-map-0.8.0 x 9.54 ops/sec ±0.28% (28 runs sampled)
chrome dev tools x 2.18 ops/sec ±10.58% (10 runs sampled)
Fastest is decode: source-map-0.8.0
Encode Memory Usage:
local code 13509880 bytes
@jridgewell/sourcemap-codec 1.4.15 13537648 bytes
sourcemap-codec 32540104 bytes
source-map-0.6.1 127531040 bytes
source-map-0.8.0 127535312 bytes
Smallest memory usage is local code
Encode speed:
encode: local code x 20.10 ops/sec ±0.19% (38 runs sampled)
encode: @jridgewell/sourcemap-codec 1.4.15 x 20.26 ops/sec ±0.32% (38 runs sampled)
encode: sourcemap-codec x 5.44 ops/sec ±1.64% (18 runs sampled)
encode: source-map-0.6.1 x 2.30 ops/sec ±4.79% (10 runs sampled)
encode: source-map-0.8.0 x 2.46 ops/sec ±6.53% (10 runs sampled)
Fastest is encode: @jridgewell/sourcemap-codec 1.4.15
```
# License
MIT

View File

@ -0,0 +1,467 @@
// src/vlq.ts
var comma = ",".charCodeAt(0);
var semicolon = ";".charCodeAt(0);
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var intToChar = new Uint8Array(64);
var charToInt = new Uint8Array(128);
for (let i = 0; i < chars.length; i++) {
const c = chars.charCodeAt(i);
intToChar[i] = c;
charToInt[c] = i;
}
function decodeInteger(reader) {
let value = 0;
let shift = 0;
let integer = 0;
do {
const c = reader.next();
integer = charToInt[c];
value |= (integer & 31) << shift;
shift += 5;
} while (integer & 32);
return value;
}
function decodeSign(num) {
return num & 1 ? -2147483648 | -(num >>> 1) : num >>> 1;
}
function encodeInteger(builder, num) {
do {
let clamped = num & 31;
num >>>= 5;
if (num > 0) clamped |= 32;
builder.write(intToChar[clamped]);
} while (num > 0);
}
function encodeSign(num) {
return num < 0 ? -num << 1 | 1 : num << 1;
}
function hasMoreVlq(reader, max) {
if (reader.pos >= max) return false;
return reader.peek() !== comma;
}
// src/strings.ts
var bufLength = 1024 * 16;
var td = typeof TextDecoder !== "undefined" ? /* @__PURE__ */ new TextDecoder() : typeof Buffer !== "undefined" ? {
decode(buf) {
const out = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);
return out.toString();
}
} : {
decode(buf) {
let out = "";
for (let i = 0; i < buf.length; i++) {
out += String.fromCharCode(buf[i]);
}
return out;
}
};
var StringWriter = class {
constructor() {
this.pos = 0;
this.out = "";
this.buffer = new Uint8Array(bufLength);
}
write(v) {
const { buffer } = this;
buffer[this.pos++] = v;
if (this.pos === bufLength) {
this.out += td.decode(buffer);
this.pos = 0;
}
}
flush() {
const { buffer, out, pos } = this;
return pos > 0 ? out + td.decode(buffer.subarray(0, pos)) : out;
}
};
var StringReader = class {
constructor(buffer) {
this.pos = 0;
this.buffer = buffer;
}
next() {
return this.buffer.charCodeAt(this.pos++);
}
peek() {
return this.buffer.charCodeAt(this.pos);
}
indexOf(char) {
const { buffer, pos } = this;
const idx = buffer.indexOf(char, pos);
return idx === -1 ? buffer.length : idx;
}
};
// src/scopes.ts
var EMPTY = [];
function decodeOriginalScopes(input) {
const { length } = input;
const reader = new StringReader(input);
const scopes = [];
const stack = [];
let line = 0;
for (; reader.pos < length; reader.pos++) {
line += decodeSign(decodeInteger(reader));
const column = decodeSign(decodeInteger(reader));
if (!hasMoreVlq(reader, length)) {
const last = stack.pop();
last[2] = line;
last[3] = column;
continue;
}
const kind = decodeSign(decodeInteger(reader));
const fields = decodeSign(decodeInteger(reader));
const hasName = fields & 1;
const scope = hasName ? [line, column, 0, 0, kind, decodeSign(decodeInteger(reader))] : [line, column, 0, 0, kind];
let vars = EMPTY;
if (hasMoreVlq(reader, length)) {
vars = [];
do {
const varsIndex = decodeSign(decodeInteger(reader));
vars.push(varsIndex);
} while (hasMoreVlq(reader, length));
}
scope.vars = vars;
scopes.push(scope);
stack.push(scope);
}
return scopes;
}
function encodeOriginalScopes(scopes) {
const writer = new StringWriter();
for (let i = 0; i < scopes.length; ) {
i = _encodeOriginalScopes(scopes, i, writer, [0]);
}
return writer.flush();
}
function _encodeOriginalScopes(scopes, index, writer, state) {
const scope = scopes[index];
const { 0: startLine, 1: startColumn, 2: endLine, 3: endColumn, 4: kind, vars } = scope;
if (index > 0) writer.write(comma);
encodeInteger(writer, encodeSign(startLine - state[0]));
state[0] = startLine;
encodeInteger(writer, encodeSign(startColumn));
encodeInteger(writer, encodeSign(kind));
const fields = scope.length === 6 ? 1 : 0;
encodeInteger(writer, encodeSign(fields));
if (scope.length === 6) encodeInteger(writer, encodeSign(scope[5]));
for (const v of vars) {
encodeInteger(writer, encodeSign(v));
}
for (index++; index < scopes.length; ) {
const next = scopes[index];
const { 0: l, 1: c } = next;
if (l > endLine || l === endLine && c >= endColumn) {
break;
}
index = _encodeOriginalScopes(scopes, index, writer, state);
}
writer.write(comma);
encodeInteger(writer, encodeSign(endLine - state[0]));
state[0] = endLine;
encodeInteger(writer, encodeSign(endColumn));
return index;
}
function decodeGeneratedRanges(input) {
const { length } = input;
const reader = new StringReader(input);
const ranges = [];
const stack = [];
let genLine = 0;
let definitionSourcesIndex = 0;
let definitionScopeIndex = 0;
let callsiteSourcesIndex = 0;
let callsiteLine = 0;
let callsiteColumn = 0;
let bindingLine = 0;
let bindingColumn = 0;
do {
const semi = reader.indexOf(";");
let genColumn = 0;
for (; reader.pos < semi; reader.pos++) {
genColumn += decodeSign(decodeInteger(reader));
if (!hasMoreVlq(reader, semi)) {
const last = stack.pop();
last[2] = genLine;
last[3] = genColumn;
continue;
}
const fields = decodeSign(decodeInteger(reader));
const hasDefinition = fields & 1;
const hasCallsite = fields & 2;
const hasScope = fields & 4;
let callsite = null;
let bindings = EMPTY;
let range;
if (hasDefinition) {
const defSourcesIndex = definitionSourcesIndex + decodeSign(decodeInteger(reader));
definitionScopeIndex = decodeSign(decodeInteger(reader)) + (definitionSourcesIndex === defSourcesIndex ? definitionScopeIndex : 0);
definitionSourcesIndex = defSourcesIndex;
range = [genLine, genColumn, 0, 0, defSourcesIndex, definitionScopeIndex];
} else {
range = [genLine, genColumn, 0, 0];
}
range.isScope = !!hasScope;
if (hasCallsite) {
const prevCsi = callsiteSourcesIndex;
const prevLine = callsiteLine;
callsiteSourcesIndex += decodeSign(decodeInteger(reader));
const sameSource = prevCsi === callsiteSourcesIndex;
callsiteLine = (sameSource ? callsiteLine : 0) + decodeSign(decodeInteger(reader));
callsiteColumn = (sameSource && prevLine === callsiteLine ? callsiteColumn : 0) + decodeSign(decodeInteger(reader));
callsite = [callsiteSourcesIndex, callsiteLine, callsiteColumn];
}
range.callsite = callsite;
if (hasMoreVlq(reader, semi)) {
bindings = [];
do {
bindingLine = genLine;
bindingColumn = genColumn;
const expressionsCount = decodeSign(decodeInteger(reader));
let expressionRanges;
if (expressionsCount < -1) {
expressionRanges = [[decodeSign(decodeInteger(reader))]];
for (let i = -1; i > expressionsCount; i--) {
const prevBl = bindingLine;
bindingLine += decodeSign(decodeInteger(reader));
bindingColumn = (bindingLine === prevBl ? bindingColumn : 0) + decodeSign(decodeInteger(reader));
const expression = decodeSign(decodeInteger(reader));
expressionRanges.push([expression, bindingLine, bindingColumn]);
}
} else {
expressionRanges = [[expressionsCount]];
}
bindings.push(expressionRanges);
} while (hasMoreVlq(reader, semi));
}
range.bindings = bindings;
ranges.push(range);
stack.push(range);
}
genLine++;
reader.pos = semi + 1;
} while (reader.pos < length);
return ranges;
}
function encodeGeneratedRanges(ranges) {
if (ranges.length === 0) return "";
const writer = new StringWriter();
for (let i = 0; i < ranges.length; ) {
i = _encodeGeneratedRanges(ranges, i, writer, [0, 0, 0, 0, 0, 0, 0]);
}
return writer.flush();
}
function _encodeGeneratedRanges(ranges, index, writer, state) {
const range = ranges[index];
const {
0: startLine,
1: startColumn,
2: endLine,
3: endColumn,
isScope,
callsite,
bindings
} = range;
if (state[0] < startLine) {
catchupLine(writer, state[0], startLine);
state[0] = startLine;
state[1] = 0;
} else if (index > 0) {
writer.write(comma);
}
encodeInteger(writer, encodeSign(range[1] - state[1]));
state[1] = range[1];
const fields = (range.length === 6 ? 1 : 0) | (callsite ? 2 : 0) | (isScope ? 4 : 0);
encodeInteger(writer, encodeSign(fields));
if (range.length === 6) {
const { 4: sourcesIndex, 5: scopesIndex } = range;
if (sourcesIndex !== state[2]) {
state[3] = 0;
}
encodeInteger(writer, encodeSign(sourcesIndex - state[2]));
state[2] = sourcesIndex;
encodeInteger(writer, encodeSign(scopesIndex - state[3]));
state[3] = scopesIndex;
}
if (callsite) {
const { 0: sourcesIndex, 1: callLine, 2: callColumn } = range.callsite;
if (sourcesIndex !== state[4]) {
state[5] = 0;
state[6] = 0;
} else if (callLine !== state[5]) {
state[6] = 0;
}
encodeInteger(writer, encodeSign(sourcesIndex - state[4]));
state[4] = sourcesIndex;
encodeInteger(writer, encodeSign(callLine - state[5]));
state[5] = callLine;
encodeInteger(writer, encodeSign(callColumn - state[6]));
state[6] = callColumn;
}
if (bindings) {
for (const binding of bindings) {
if (binding.length > 1) encodeInteger(writer, encodeSign(-binding.length));
const expression = binding[0][0];
encodeInteger(writer, encodeSign(expression));
let bindingStartLine = startLine;
let bindingStartColumn = startColumn;
for (let i = 1; i < binding.length; i++) {
const expRange = binding[i];
encodeInteger(writer, encodeSign(expRange[1] - bindingStartLine));
bindingStartLine = expRange[1];
encodeInteger(writer, encodeSign(expRange[2] - bindingStartColumn));
bindingStartColumn = expRange[2];
encodeInteger(writer, encodeSign(expRange[0]));
}
}
}
for (index++; index < ranges.length; ) {
const next = ranges[index];
const { 0: l, 1: c } = next;
if (l > endLine || l === endLine && c >= endColumn) {
break;
}
index = _encodeGeneratedRanges(ranges, index, writer, state);
}
if (state[0] < endLine) {
catchupLine(writer, state[0], endLine);
state[0] = endLine;
state[1] = 0;
} else {
writer.write(comma);
}
encodeInteger(writer, encodeSign(endColumn - state[1]));
state[1] = endColumn;
return index;
}
function catchupLine(writer, lastLine, line) {
do {
writer.write(semicolon);
} while (++lastLine < line);
}
// src/range-mappings.ts
function decodeRangeMappings(input) {
const { length } = input;
const reader = new StringReader(input);
const rangeMappings = [];
do {
const semi = reader.indexOf(";");
const indices = [];
let index = 0;
while (reader.pos < semi) {
index += decodeInteger(reader);
indices.push(index);
}
rangeMappings.push(indices);
reader.pos = semi + 1;
} while (reader.pos <= length);
return rangeMappings;
}
function encodeRangeMappings(decoded) {
if (decoded.length === 0) return "";
const writer = new StringWriter();
for (let i = 0; i < decoded.length; i++) {
const indices = decoded[i];
if (i > 0) writer.write(semicolon);
let index = 0;
for (let j = 0; j < indices.length; j++) {
const offset = indices[j];
encodeInteger(writer, offset - index);
index = offset;
}
}
return writer.flush();
}
// src/sourcemap-codec.ts
function decode(mappings) {
const { length } = mappings;
const reader = new StringReader(mappings);
const decoded = [];
let genColumn = 0;
let sourcesIndex = 0;
let sourceLine = 0;
let sourceColumn = 0;
let namesIndex = 0;
do {
const semi = reader.indexOf(";");
const line = [];
let sorted = true;
let lastCol = 0;
genColumn = 0;
while (reader.pos < semi) {
let seg;
genColumn += decodeSign(decodeInteger(reader));
if (genColumn < lastCol) sorted = false;
lastCol = genColumn;
if (hasMoreVlq(reader, semi)) {
sourcesIndex += decodeSign(decodeInteger(reader));
sourceLine += decodeSign(decodeInteger(reader));
sourceColumn += decodeSign(decodeInteger(reader));
if (hasMoreVlq(reader, semi)) {
namesIndex += decodeSign(decodeInteger(reader));
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex];
} else {
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn];
}
} else {
seg = [genColumn];
}
line.push(seg);
reader.pos++;
}
if (!sorted) sort(line);
decoded.push(line);
reader.pos = semi + 1;
} while (reader.pos <= length);
return decoded;
}
function sort(line) {
line.sort(sortComparator);
}
function sortComparator(a, b) {
return a[0] - b[0];
}
function encode(decoded) {
const writer = new StringWriter();
let sourcesIndex = 0;
let sourceLine = 0;
let sourceColumn = 0;
let namesIndex = 0;
for (let i = 0; i < decoded.length; i++) {
const line = decoded[i];
if (i > 0) writer.write(semicolon);
if (line.length === 0) continue;
let genColumn = 0;
for (let j = 0; j < line.length; j++) {
const segment = line[j];
if (j > 0) writer.write(comma);
encodeInteger(writer, encodeSign(segment[0] - genColumn));
genColumn = segment[0];
if (segment.length === 1) continue;
encodeInteger(writer, encodeSign(segment[1] - sourcesIndex));
encodeInteger(writer, encodeSign(segment[2] - sourceLine));
encodeInteger(writer, encodeSign(segment[3] - sourceColumn));
sourcesIndex = segment[1];
sourceLine = segment[2];
sourceColumn = segment[3];
if (segment.length === 4) continue;
encodeInteger(writer, encodeSign(segment[4] - namesIndex));
namesIndex = segment[4];
}
}
return writer.flush();
}
export {
decode,
decodeGeneratedRanges,
decodeOriginalScopes,
decodeRangeMappings,
encode,
encodeGeneratedRanges,
encodeOriginalScopes,
encodeRangeMappings
};
//# sourceMappingURL=sourcemap-codec.mjs.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,508 @@
(function (global, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
factory(module);
module.exports = def(module);
} else if (typeof define === 'function' && define.amd) {
define(['module'], function(mod) {
factory.apply(this, arguments);
mod.exports = def(mod);
});
} else {
const mod = { exports: {} };
factory(mod);
global = typeof globalThis !== 'undefined' ? globalThis : global || self;
global.sourcemapCodec = def(mod);
}
function def(m) { return 'default' in m.exports ? m.exports.default : m.exports; }
})(this, (function (module) {
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/sourcemap-codec.ts
var sourcemap_codec_exports = {};
__export(sourcemap_codec_exports, {
decode: () => decode,
decodeGeneratedRanges: () => decodeGeneratedRanges,
decodeOriginalScopes: () => decodeOriginalScopes,
decodeRangeMappings: () => decodeRangeMappings,
encode: () => encode,
encodeGeneratedRanges: () => encodeGeneratedRanges,
encodeOriginalScopes: () => encodeOriginalScopes,
encodeRangeMappings: () => encodeRangeMappings
});
module.exports = __toCommonJS(sourcemap_codec_exports);
// src/vlq.ts
var comma = ",".charCodeAt(0);
var semicolon = ";".charCodeAt(0);
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var intToChar = new Uint8Array(64);
var charToInt = new Uint8Array(128);
for (let i = 0; i < chars.length; i++) {
const c = chars.charCodeAt(i);
intToChar[i] = c;
charToInt[c] = i;
}
function decodeInteger(reader) {
let value = 0;
let shift = 0;
let integer = 0;
do {
const c = reader.next();
integer = charToInt[c];
value |= (integer & 31) << shift;
shift += 5;
} while (integer & 32);
return value;
}
function decodeSign(num) {
return num & 1 ? -2147483648 | -(num >>> 1) : num >>> 1;
}
function encodeInteger(builder, num) {
do {
let clamped = num & 31;
num >>>= 5;
if (num > 0) clamped |= 32;
builder.write(intToChar[clamped]);
} while (num > 0);
}
function encodeSign(num) {
return num < 0 ? -num << 1 | 1 : num << 1;
}
function hasMoreVlq(reader, max) {
if (reader.pos >= max) return false;
return reader.peek() !== comma;
}
// src/strings.ts
var bufLength = 1024 * 16;
var td = typeof TextDecoder !== "undefined" ? /* @__PURE__ */ new TextDecoder() : typeof Buffer !== "undefined" ? {
decode(buf) {
const out = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);
return out.toString();
}
} : {
decode(buf) {
let out = "";
for (let i = 0; i < buf.length; i++) {
out += String.fromCharCode(buf[i]);
}
return out;
}
};
var StringWriter = class {
constructor() {
this.pos = 0;
this.out = "";
this.buffer = new Uint8Array(bufLength);
}
write(v) {
const { buffer } = this;
buffer[this.pos++] = v;
if (this.pos === bufLength) {
this.out += td.decode(buffer);
this.pos = 0;
}
}
flush() {
const { buffer, out, pos } = this;
return pos > 0 ? out + td.decode(buffer.subarray(0, pos)) : out;
}
};
var StringReader = class {
constructor(buffer) {
this.pos = 0;
this.buffer = buffer;
}
next() {
return this.buffer.charCodeAt(this.pos++);
}
peek() {
return this.buffer.charCodeAt(this.pos);
}
indexOf(char) {
const { buffer, pos } = this;
const idx = buffer.indexOf(char, pos);
return idx === -1 ? buffer.length : idx;
}
};
// src/scopes.ts
var EMPTY = [];
function decodeOriginalScopes(input) {
const { length } = input;
const reader = new StringReader(input);
const scopes = [];
const stack = [];
let line = 0;
for (; reader.pos < length; reader.pos++) {
line += decodeSign(decodeInteger(reader));
const column = decodeSign(decodeInteger(reader));
if (!hasMoreVlq(reader, length)) {
const last = stack.pop();
last[2] = line;
last[3] = column;
continue;
}
const kind = decodeSign(decodeInteger(reader));
const fields = decodeSign(decodeInteger(reader));
const hasName = fields & 1;
const scope = hasName ? [line, column, 0, 0, kind, decodeSign(decodeInteger(reader))] : [line, column, 0, 0, kind];
let vars = EMPTY;
if (hasMoreVlq(reader, length)) {
vars = [];
do {
const varsIndex = decodeSign(decodeInteger(reader));
vars.push(varsIndex);
} while (hasMoreVlq(reader, length));
}
scope.vars = vars;
scopes.push(scope);
stack.push(scope);
}
return scopes;
}
function encodeOriginalScopes(scopes) {
const writer = new StringWriter();
for (let i = 0; i < scopes.length; ) {
i = _encodeOriginalScopes(scopes, i, writer, [0]);
}
return writer.flush();
}
function _encodeOriginalScopes(scopes, index, writer, state) {
const scope = scopes[index];
const { 0: startLine, 1: startColumn, 2: endLine, 3: endColumn, 4: kind, vars } = scope;
if (index > 0) writer.write(comma);
encodeInteger(writer, encodeSign(startLine - state[0]));
state[0] = startLine;
encodeInteger(writer, encodeSign(startColumn));
encodeInteger(writer, encodeSign(kind));
const fields = scope.length === 6 ? 1 : 0;
encodeInteger(writer, encodeSign(fields));
if (scope.length === 6) encodeInteger(writer, encodeSign(scope[5]));
for (const v of vars) {
encodeInteger(writer, encodeSign(v));
}
for (index++; index < scopes.length; ) {
const next = scopes[index];
const { 0: l, 1: c } = next;
if (l > endLine || l === endLine && c >= endColumn) {
break;
}
index = _encodeOriginalScopes(scopes, index, writer, state);
}
writer.write(comma);
encodeInteger(writer, encodeSign(endLine - state[0]));
state[0] = endLine;
encodeInteger(writer, encodeSign(endColumn));
return index;
}
function decodeGeneratedRanges(input) {
const { length } = input;
const reader = new StringReader(input);
const ranges = [];
const stack = [];
let genLine = 0;
let definitionSourcesIndex = 0;
let definitionScopeIndex = 0;
let callsiteSourcesIndex = 0;
let callsiteLine = 0;
let callsiteColumn = 0;
let bindingLine = 0;
let bindingColumn = 0;
do {
const semi = reader.indexOf(";");
let genColumn = 0;
for (; reader.pos < semi; reader.pos++) {
genColumn += decodeSign(decodeInteger(reader));
if (!hasMoreVlq(reader, semi)) {
const last = stack.pop();
last[2] = genLine;
last[3] = genColumn;
continue;
}
const fields = decodeSign(decodeInteger(reader));
const hasDefinition = fields & 1;
const hasCallsite = fields & 2;
const hasScope = fields & 4;
let callsite = null;
let bindings = EMPTY;
let range;
if (hasDefinition) {
const defSourcesIndex = definitionSourcesIndex + decodeSign(decodeInteger(reader));
definitionScopeIndex = decodeSign(decodeInteger(reader)) + (definitionSourcesIndex === defSourcesIndex ? definitionScopeIndex : 0);
definitionSourcesIndex = defSourcesIndex;
range = [genLine, genColumn, 0, 0, defSourcesIndex, definitionScopeIndex];
} else {
range = [genLine, genColumn, 0, 0];
}
range.isScope = !!hasScope;
if (hasCallsite) {
const prevCsi = callsiteSourcesIndex;
const prevLine = callsiteLine;
callsiteSourcesIndex += decodeSign(decodeInteger(reader));
const sameSource = prevCsi === callsiteSourcesIndex;
callsiteLine = (sameSource ? callsiteLine : 0) + decodeSign(decodeInteger(reader));
callsiteColumn = (sameSource && prevLine === callsiteLine ? callsiteColumn : 0) + decodeSign(decodeInteger(reader));
callsite = [callsiteSourcesIndex, callsiteLine, callsiteColumn];
}
range.callsite = callsite;
if (hasMoreVlq(reader, semi)) {
bindings = [];
do {
bindingLine = genLine;
bindingColumn = genColumn;
const expressionsCount = decodeSign(decodeInteger(reader));
let expressionRanges;
if (expressionsCount < -1) {
expressionRanges = [[decodeSign(decodeInteger(reader))]];
for (let i = -1; i > expressionsCount; i--) {
const prevBl = bindingLine;
bindingLine += decodeSign(decodeInteger(reader));
bindingColumn = (bindingLine === prevBl ? bindingColumn : 0) + decodeSign(decodeInteger(reader));
const expression = decodeSign(decodeInteger(reader));
expressionRanges.push([expression, bindingLine, bindingColumn]);
}
} else {
expressionRanges = [[expressionsCount]];
}
bindings.push(expressionRanges);
} while (hasMoreVlq(reader, semi));
}
range.bindings = bindings;
ranges.push(range);
stack.push(range);
}
genLine++;
reader.pos = semi + 1;
} while (reader.pos < length);
return ranges;
}
function encodeGeneratedRanges(ranges) {
if (ranges.length === 0) return "";
const writer = new StringWriter();
for (let i = 0; i < ranges.length; ) {
i = _encodeGeneratedRanges(ranges, i, writer, [0, 0, 0, 0, 0, 0, 0]);
}
return writer.flush();
}
function _encodeGeneratedRanges(ranges, index, writer, state) {
const range = ranges[index];
const {
0: startLine,
1: startColumn,
2: endLine,
3: endColumn,
isScope,
callsite,
bindings
} = range;
if (state[0] < startLine) {
catchupLine(writer, state[0], startLine);
state[0] = startLine;
state[1] = 0;
} else if (index > 0) {
writer.write(comma);
}
encodeInteger(writer, encodeSign(range[1] - state[1]));
state[1] = range[1];
const fields = (range.length === 6 ? 1 : 0) | (callsite ? 2 : 0) | (isScope ? 4 : 0);
encodeInteger(writer, encodeSign(fields));
if (range.length === 6) {
const { 4: sourcesIndex, 5: scopesIndex } = range;
if (sourcesIndex !== state[2]) {
state[3] = 0;
}
encodeInteger(writer, encodeSign(sourcesIndex - state[2]));
state[2] = sourcesIndex;
encodeInteger(writer, encodeSign(scopesIndex - state[3]));
state[3] = scopesIndex;
}
if (callsite) {
const { 0: sourcesIndex, 1: callLine, 2: callColumn } = range.callsite;
if (sourcesIndex !== state[4]) {
state[5] = 0;
state[6] = 0;
} else if (callLine !== state[5]) {
state[6] = 0;
}
encodeInteger(writer, encodeSign(sourcesIndex - state[4]));
state[4] = sourcesIndex;
encodeInteger(writer, encodeSign(callLine - state[5]));
state[5] = callLine;
encodeInteger(writer, encodeSign(callColumn - state[6]));
state[6] = callColumn;
}
if (bindings) {
for (const binding of bindings) {
if (binding.length > 1) encodeInteger(writer, encodeSign(-binding.length));
const expression = binding[0][0];
encodeInteger(writer, encodeSign(expression));
let bindingStartLine = startLine;
let bindingStartColumn = startColumn;
for (let i = 1; i < binding.length; i++) {
const expRange = binding[i];
encodeInteger(writer, encodeSign(expRange[1] - bindingStartLine));
bindingStartLine = expRange[1];
encodeInteger(writer, encodeSign(expRange[2] - bindingStartColumn));
bindingStartColumn = expRange[2];
encodeInteger(writer, encodeSign(expRange[0]));
}
}
}
for (index++; index < ranges.length; ) {
const next = ranges[index];
const { 0: l, 1: c } = next;
if (l > endLine || l === endLine && c >= endColumn) {
break;
}
index = _encodeGeneratedRanges(ranges, index, writer, state);
}
if (state[0] < endLine) {
catchupLine(writer, state[0], endLine);
state[0] = endLine;
state[1] = 0;
} else {
writer.write(comma);
}
encodeInteger(writer, encodeSign(endColumn - state[1]));
state[1] = endColumn;
return index;
}
function catchupLine(writer, lastLine, line) {
do {
writer.write(semicolon);
} while (++lastLine < line);
}
// src/range-mappings.ts
function decodeRangeMappings(input) {
const { length } = input;
const reader = new StringReader(input);
const rangeMappings = [];
do {
const semi = reader.indexOf(";");
const indices = [];
let index = 0;
while (reader.pos < semi) {
index += decodeInteger(reader);
indices.push(index);
}
rangeMappings.push(indices);
reader.pos = semi + 1;
} while (reader.pos <= length);
return rangeMappings;
}
function encodeRangeMappings(decoded) {
if (decoded.length === 0) return "";
const writer = new StringWriter();
for (let i = 0; i < decoded.length; i++) {
const indices = decoded[i];
if (i > 0) writer.write(semicolon);
let index = 0;
for (let j = 0; j < indices.length; j++) {
const offset = indices[j];
encodeInteger(writer, offset - index);
index = offset;
}
}
return writer.flush();
}
// src/sourcemap-codec.ts
function decode(mappings) {
const { length } = mappings;
const reader = new StringReader(mappings);
const decoded = [];
let genColumn = 0;
let sourcesIndex = 0;
let sourceLine = 0;
let sourceColumn = 0;
let namesIndex = 0;
do {
const semi = reader.indexOf(";");
const line = [];
let sorted = true;
let lastCol = 0;
genColumn = 0;
while (reader.pos < semi) {
let seg;
genColumn += decodeSign(decodeInteger(reader));
if (genColumn < lastCol) sorted = false;
lastCol = genColumn;
if (hasMoreVlq(reader, semi)) {
sourcesIndex += decodeSign(decodeInteger(reader));
sourceLine += decodeSign(decodeInteger(reader));
sourceColumn += decodeSign(decodeInteger(reader));
if (hasMoreVlq(reader, semi)) {
namesIndex += decodeSign(decodeInteger(reader));
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex];
} else {
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn];
}
} else {
seg = [genColumn];
}
line.push(seg);
reader.pos++;
}
if (!sorted) sort(line);
decoded.push(line);
reader.pos = semi + 1;
} while (reader.pos <= length);
return decoded;
}
function sort(line) {
line.sort(sortComparator);
}
function sortComparator(a, b) {
return a[0] - b[0];
}
function encode(decoded) {
const writer = new StringWriter();
let sourcesIndex = 0;
let sourceLine = 0;
let sourceColumn = 0;
let namesIndex = 0;
for (let i = 0; i < decoded.length; i++) {
const line = decoded[i];
if (i > 0) writer.write(semicolon);
if (line.length === 0) continue;
let genColumn = 0;
for (let j = 0; j < line.length; j++) {
const segment = line[j];
if (j > 0) writer.write(comma);
encodeInteger(writer, encodeSign(segment[0] - genColumn));
genColumn = segment[0];
if (segment.length === 1) continue;
encodeInteger(writer, encodeSign(segment[1] - sourcesIndex));
encodeInteger(writer, encodeSign(segment[2] - sourceLine));
encodeInteger(writer, encodeSign(segment[3] - sourceColumn));
sourcesIndex = segment[1];
sourceLine = segment[2];
sourceColumn = segment[3];
if (segment.length === 4) continue;
encodeInteger(writer, encodeSign(segment[4] - namesIndex));
namesIndex = segment[4];
}
}
return writer.flush();
}
}));
//# sourceMappingURL=sourcemap-codec.umd.js.map

File diff suppressed because one or more lines are too long

64
node_modules/@jridgewell/sourcemap-codec/package.json generated vendored Normal file
View File

@ -0,0 +1,64 @@
{
"name": "@jridgewell/sourcemap-codec",
"version": "1.6.0",
"description": "Encode/decode sourcemap mappings",
"keywords": [
"sourcemap",
"vlq"
],
"main": "dist/sourcemap-codec.umd.js",
"module": "dist/sourcemap-codec.mjs",
"types": "types/sourcemap-codec.d.cts",
"files": [
"dist",
"src",
"types"
],
"exports": {
".": [
{
"import": {
"types": "./types/sourcemap-codec.d.mts",
"default": "./dist/sourcemap-codec.mjs"
},
"default": {
"types": "./types/sourcemap-codec.d.cts",
"default": "./dist/sourcemap-codec.umd.js"
}
},
"./dist/sourcemap-codec.umd.js"
],
"./package.json": "./package.json"
},
"scripts": {
"benchmark": "run-s build:code benchmark:*",
"benchmark:install": "cd benchmark && npm install",
"benchmark:only": "node --expose-gc benchmark/index.js",
"build": "run-s -n build:code build:types",
"build:code": "node ../../esbuild.mjs sourcemap-codec.ts",
"build:types": "run-s build:types:force build:types:emit build:types:mts",
"build:types:force": "rimraf tsconfig.build.tsbuildinfo",
"build:types:emit": "tsc --project tsconfig.build.json",
"build:types:mts": "node ../../mts-types.mjs",
"clean": "run-s -n clean:code clean:types",
"clean:code": "tsc --build --clean tsconfig.build.json",
"clean:types": "rimraf dist types",
"test": "run-s -n test:types test:only test:format",
"test:format": "prettier --check '{src,test}/**/*.ts'",
"test:only": "mocha",
"test:types": "eslint '{src,test}/**/*.ts'",
"test:watch": "mocha --watch",
"lint": "run-s -n lint:types lint:format",
"lint:format": "npm run test:format -- --write",
"lint:types": "npm run test:types -- --fix",
"prepublishOnly": "npm run-s -n build test"
},
"homepage": "https://github.com/jridgewell/sourcemaps/tree/main/packages/sourcemap-codec",
"repository": {
"type": "git",
"url": "git+https://github.com/jridgewell/sourcemaps.git",
"directory": "packages/sourcemap-codec"
},
"author": "Justin Ridgewell <justin@ridgewell.name>",
"license": "MIT"
}

View File

@ -0,0 +1,47 @@
import { StringReader, StringWriter } from './strings';
import { decodeInteger, encodeInteger, semicolon } from './vlq';
export type MappingIndex = number;
export type RangeMappings = MappingIndex[][];
export function decodeRangeMappings(input: string): RangeMappings {
const { length } = input;
const reader = new StringReader(input);
const rangeMappings: RangeMappings = [];
do {
const semi = reader.indexOf(';');
const indices: MappingIndex[] = [];
let index = 0;
while (reader.pos < semi) {
index += decodeInteger(reader);
indices.push(index);
}
rangeMappings.push(indices);
reader.pos = semi + 1;
} while (reader.pos <= length);
return rangeMappings;
}
export function encodeRangeMappings(decoded: RangeMappings): string {
if (decoded.length === 0) return '';
const writer = new StringWriter();
for (let i = 0; i < decoded.length; i++) {
const indices = decoded[i];
if (i > 0) writer.write(semicolon);
let index = 0;
for (let j = 0; j < indices.length; j++) {
const offset = indices[j];
encodeInteger(writer, offset - index);
index = offset;
}
}
return writer.flush();
}

365
node_modules/@jridgewell/sourcemap-codec/src/scopes.ts generated vendored Normal file
View File

@ -0,0 +1,365 @@
import { StringReader, StringWriter } from './strings';
import {
comma,
decodeInteger,
decodeSign,
encodeInteger,
encodeSign,
hasMoreVlq,
semicolon,
} from './vlq';
const EMPTY: any[] = [];
type Line = number;
type Column = number;
type Kind = number;
type Name = number;
type Var = number;
type SourcesIndex = number;
type ScopesIndex = number;
type Mix<A, B, O> = (A & O) | (B & O);
export type OriginalScope = Mix<
[Line, Column, Line, Column, Kind],
[Line, Column, Line, Column, Kind, Name],
{ vars: Var[] }
>;
export type GeneratedRange = Mix<
[Line, Column, Line, Column],
[Line, Column, Line, Column, SourcesIndex, ScopesIndex],
{
callsite: CallSite | null;
bindings: Binding[];
isScope: boolean;
}
>;
export type CallSite = [SourcesIndex, Line, Column];
type Binding = BindingExpressionRange[];
export type BindingExpressionRange = [Name] | [Name, Line, Column];
export function decodeOriginalScopes(input: string): OriginalScope[] {
const { length } = input;
const reader = new StringReader(input);
const scopes: OriginalScope[] = [];
const stack: OriginalScope[] = [];
let line = 0;
for (; reader.pos < length; reader.pos++) {
line += decodeSign(decodeInteger(reader));
const column = decodeSign(decodeInteger(reader));
if (!hasMoreVlq(reader, length)) {
const last = stack.pop()!;
last[2] = line;
last[3] = column;
continue;
}
const kind = decodeSign(decodeInteger(reader));
const fields = decodeSign(decodeInteger(reader));
const hasName = fields & 0b0001;
const scope: OriginalScope = (
hasName
? [line, column, 0, 0, kind, decodeSign(decodeInteger(reader))]
: [line, column, 0, 0, kind]
) as OriginalScope;
let vars: Var[] = EMPTY;
if (hasMoreVlq(reader, length)) {
vars = [];
do {
const varsIndex = decodeSign(decodeInteger(reader));
vars.push(varsIndex);
} while (hasMoreVlq(reader, length));
}
scope.vars = vars;
scopes.push(scope);
stack.push(scope);
}
return scopes;
}
export function encodeOriginalScopes(scopes: OriginalScope[]): string {
const writer = new StringWriter();
for (let i = 0; i < scopes.length; ) {
i = _encodeOriginalScopes(scopes, i, writer, [0]);
}
return writer.flush();
}
function _encodeOriginalScopes(
scopes: OriginalScope[],
index: number,
writer: StringWriter,
state: [
number, // GenColumn
],
): number {
const scope = scopes[index];
const { 0: startLine, 1: startColumn, 2: endLine, 3: endColumn, 4: kind, vars } = scope;
if (index > 0) writer.write(comma);
encodeInteger(writer, encodeSign(startLine - state[0]));
state[0] = startLine;
encodeInteger(writer, encodeSign(startColumn));
encodeInteger(writer, encodeSign(kind));
const fields = scope.length === 6 ? 0b0001 : 0;
encodeInteger(writer, encodeSign(fields));
if (scope.length === 6) encodeInteger(writer, encodeSign(scope[5]));
for (const v of vars) {
encodeInteger(writer, encodeSign(v));
}
for (index++; index < scopes.length; ) {
const next = scopes[index];
const { 0: l, 1: c } = next;
if (l > endLine || (l === endLine && c >= endColumn)) {
break;
}
index = _encodeOriginalScopes(scopes, index, writer, state);
}
writer.write(comma);
encodeInteger(writer, encodeSign(endLine - state[0]));
state[0] = endLine;
encodeInteger(writer, encodeSign(endColumn));
return index;
}
export function decodeGeneratedRanges(input: string): GeneratedRange[] {
const { length } = input;
const reader = new StringReader(input);
const ranges: GeneratedRange[] = [];
const stack: GeneratedRange[] = [];
let genLine = 0;
let definitionSourcesIndex = 0;
let definitionScopeIndex = 0;
let callsiteSourcesIndex = 0;
let callsiteLine = 0;
let callsiteColumn = 0;
let bindingLine = 0;
let bindingColumn = 0;
do {
const semi = reader.indexOf(';');
let genColumn = 0;
for (; reader.pos < semi; reader.pos++) {
genColumn += decodeSign(decodeInteger(reader));
if (!hasMoreVlq(reader, semi)) {
const last = stack.pop()!;
last[2] = genLine;
last[3] = genColumn;
continue;
}
const fields = decodeSign(decodeInteger(reader));
const hasDefinition = fields & 0b0001;
const hasCallsite = fields & 0b0010;
const hasScope = fields & 0b0100;
let callsite: CallSite | null = null;
let bindings: Binding[] = EMPTY;
let range: GeneratedRange;
if (hasDefinition) {
const defSourcesIndex = definitionSourcesIndex + decodeSign(decodeInteger(reader));
definitionScopeIndex =
decodeSign(decodeInteger(reader)) +
(definitionSourcesIndex === defSourcesIndex ? definitionScopeIndex : 0);
definitionSourcesIndex = defSourcesIndex;
range = [genLine, genColumn, 0, 0, defSourcesIndex, definitionScopeIndex] as GeneratedRange;
} else {
range = [genLine, genColumn, 0, 0] as GeneratedRange;
}
range.isScope = !!hasScope;
if (hasCallsite) {
const prevCsi = callsiteSourcesIndex;
const prevLine = callsiteLine;
callsiteSourcesIndex += decodeSign(decodeInteger(reader));
const sameSource = prevCsi === callsiteSourcesIndex;
callsiteLine = (sameSource ? callsiteLine : 0) + decodeSign(decodeInteger(reader));
callsiteColumn =
(sameSource && prevLine === callsiteLine ? callsiteColumn : 0) +
decodeSign(decodeInteger(reader));
callsite = [callsiteSourcesIndex, callsiteLine, callsiteColumn];
}
range.callsite = callsite;
if (hasMoreVlq(reader, semi)) {
bindings = [];
do {
bindingLine = genLine;
bindingColumn = genColumn;
const expressionsCount = decodeSign(decodeInteger(reader));
let expressionRanges: BindingExpressionRange[];
if (expressionsCount < -1) {
expressionRanges = [[decodeSign(decodeInteger(reader))]];
for (let i = -1; i > expressionsCount; i--) {
const prevBl = bindingLine;
bindingLine += decodeSign(decodeInteger(reader));
bindingColumn =
(bindingLine === prevBl ? bindingColumn : 0) + decodeSign(decodeInteger(reader));
const expression = decodeSign(decodeInteger(reader));
expressionRanges.push([expression, bindingLine, bindingColumn]);
}
} else {
expressionRanges = [[expressionsCount]];
}
bindings.push(expressionRanges);
} while (hasMoreVlq(reader, semi));
}
range.bindings = bindings;
ranges.push(range);
stack.push(range);
}
genLine++;
reader.pos = semi + 1;
} while (reader.pos < length);
return ranges;
}
export function encodeGeneratedRanges(ranges: GeneratedRange[]): string {
if (ranges.length === 0) return '';
const writer = new StringWriter();
for (let i = 0; i < ranges.length; ) {
i = _encodeGeneratedRanges(ranges, i, writer, [0, 0, 0, 0, 0, 0, 0]);
}
return writer.flush();
}
function _encodeGeneratedRanges(
ranges: GeneratedRange[],
index: number,
writer: StringWriter,
state: [
number, // GenLine
number, // GenColumn
number, // DefSourcesIndex
number, // DefScopesIndex
number, // CallSourcesIndex
number, // CallLine
number, // CallColumn
],
): number {
const range = ranges[index];
const {
0: startLine,
1: startColumn,
2: endLine,
3: endColumn,
isScope,
callsite,
bindings,
} = range;
if (state[0] < startLine) {
catchupLine(writer, state[0], startLine);
state[0] = startLine;
state[1] = 0;
} else if (index > 0) {
writer.write(comma);
}
encodeInteger(writer, encodeSign(range[1] - state[1]));
state[1] = range[1];
const fields =
(range.length === 6 ? 0b0001 : 0) | (callsite ? 0b0010 : 0) | (isScope ? 0b0100 : 0);
encodeInteger(writer, encodeSign(fields));
if (range.length === 6) {
const { 4: sourcesIndex, 5: scopesIndex } = range;
if (sourcesIndex !== state[2]) {
state[3] = 0;
}
encodeInteger(writer, encodeSign(sourcesIndex - state[2]));
state[2] = sourcesIndex;
encodeInteger(writer, encodeSign(scopesIndex - state[3]));
state[3] = scopesIndex;
}
if (callsite) {
const { 0: sourcesIndex, 1: callLine, 2: callColumn } = range.callsite!;
if (sourcesIndex !== state[4]) {
state[5] = 0;
state[6] = 0;
} else if (callLine !== state[5]) {
state[6] = 0;
}
encodeInteger(writer, encodeSign(sourcesIndex - state[4]));
state[4] = sourcesIndex;
encodeInteger(writer, encodeSign(callLine - state[5]));
state[5] = callLine;
encodeInteger(writer, encodeSign(callColumn - state[6]));
state[6] = callColumn;
}
if (bindings) {
for (const binding of bindings) {
if (binding.length > 1) encodeInteger(writer, encodeSign(-binding.length));
const expression = binding[0][0];
encodeInteger(writer, encodeSign(expression));
let bindingStartLine = startLine;
let bindingStartColumn = startColumn;
for (let i = 1; i < binding.length; i++) {
const expRange = binding[i];
encodeInteger(writer, encodeSign(expRange[1]! - bindingStartLine));
bindingStartLine = expRange[1]!;
encodeInteger(writer, encodeSign(expRange[2]! - bindingStartColumn));
bindingStartColumn = expRange[2]!;
encodeInteger(writer, encodeSign(expRange[0]!));
}
}
}
for (index++; index < ranges.length; ) {
const next = ranges[index];
const { 0: l, 1: c } = next;
if (l > endLine || (l === endLine && c >= endColumn)) {
break;
}
index = _encodeGeneratedRanges(ranges, index, writer, state);
}
if (state[0] < endLine) {
catchupLine(writer, state[0], endLine);
state[0] = endLine;
state[1] = 0;
} else {
writer.write(comma);
}
encodeInteger(writer, encodeSign(endColumn - state[1]));
state[1] = endColumn;
return index;
}
function catchupLine(writer: StringWriter, lastLine: number, line: number) {
do {
writer.write(semicolon);
} while (++lastLine < line);
}

View File

@ -0,0 +1,127 @@
import {
comma,
decodeInteger,
decodeSign,
encodeInteger,
encodeSign,
hasMoreVlq,
semicolon,
} from './vlq';
import { StringWriter, StringReader } from './strings';
export {
decodeOriginalScopes,
encodeOriginalScopes,
decodeGeneratedRanges,
encodeGeneratedRanges,
} from './scopes';
export type { OriginalScope, GeneratedRange, CallSite, BindingExpressionRange } from './scopes';
export { decodeRangeMappings, encodeRangeMappings } from './range-mappings';
export type { MappingIndex, RangeMappings } from './range-mappings';
export type SourceMapSegment =
| [number]
| [number, number, number, number]
| [number, number, number, number, number];
export type SourceMapLine = SourceMapSegment[];
export type SourceMapMappings = SourceMapLine[];
export function decode(mappings: string): SourceMapMappings {
const { length } = mappings;
const reader = new StringReader(mappings);
const decoded: SourceMapMappings = [];
let genColumn = 0;
let sourcesIndex = 0;
let sourceLine = 0;
let sourceColumn = 0;
let namesIndex = 0;
do {
const semi = reader.indexOf(';');
const line: SourceMapLine = [];
let sorted = true;
let lastCol = 0;
genColumn = 0;
while (reader.pos < semi) {
let seg: SourceMapSegment;
genColumn += decodeSign(decodeInteger(reader));
if (genColumn < lastCol) sorted = false;
lastCol = genColumn;
if (hasMoreVlq(reader, semi)) {
sourcesIndex += decodeSign(decodeInteger(reader));
sourceLine += decodeSign(decodeInteger(reader));
sourceColumn += decodeSign(decodeInteger(reader));
if (hasMoreVlq(reader, semi)) {
namesIndex += decodeSign(decodeInteger(reader));
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex];
} else {
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn];
}
} else {
seg = [genColumn];
}
line.push(seg);
reader.pos++;
}
if (!sorted) sort(line);
decoded.push(line);
reader.pos = semi + 1;
} while (reader.pos <= length);
return decoded;
}
function sort(line: SourceMapSegment[]) {
line.sort(sortComparator);
}
function sortComparator(a: SourceMapSegment, b: SourceMapSegment): number {
return a[0] - b[0];
}
export function encode(decoded: SourceMapMappings): string;
export function encode(decoded: Readonly<SourceMapMappings>): string;
export function encode(decoded: Readonly<SourceMapMappings>): string {
const writer = new StringWriter();
let sourcesIndex = 0;
let sourceLine = 0;
let sourceColumn = 0;
let namesIndex = 0;
for (let i = 0; i < decoded.length; i++) {
const line = decoded[i];
if (i > 0) writer.write(semicolon);
if (line.length === 0) continue;
let genColumn = 0;
for (let j = 0; j < line.length; j++) {
const segment = line[j];
if (j > 0) writer.write(comma);
encodeInteger(writer, encodeSign(segment[0] - genColumn));
genColumn = segment[0];
if (segment.length === 1) continue;
encodeInteger(writer, encodeSign(segment[1] - sourcesIndex));
encodeInteger(writer, encodeSign(segment[2] - sourceLine));
encodeInteger(writer, encodeSign(segment[3] - sourceColumn));
sourcesIndex = segment[1];
sourceLine = segment[2];
sourceColumn = segment[3];
if (segment.length === 4) continue;
encodeInteger(writer, encodeSign(segment[4] - namesIndex));
namesIndex = segment[4];
}
}
return writer.flush();
}

View File

@ -0,0 +1,65 @@
const bufLength = 1024 * 16;
// Provide a fallback for older environments.
const td =
typeof TextDecoder !== 'undefined'
? /* #__PURE__ */ new TextDecoder()
: typeof Buffer !== 'undefined'
? {
decode(buf: Uint8Array): string {
const out = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);
return out.toString();
},
}
: {
decode(buf: Uint8Array): string {
let out = '';
for (let i = 0; i < buf.length; i++) {
out += String.fromCharCode(buf[i]);
}
return out;
},
};
export class StringWriter {
pos = 0;
private out = '';
private buffer = new Uint8Array(bufLength);
write(v: number): void {
const { buffer } = this;
buffer[this.pos++] = v;
if (this.pos === bufLength) {
this.out += td.decode(buffer);
this.pos = 0;
}
}
flush(): string {
const { buffer, out, pos } = this;
return pos > 0 ? out + td.decode(buffer.subarray(0, pos)) : out;
}
}
export class StringReader {
pos = 0;
declare private buffer: string;
constructor(buffer: string) {
this.buffer = buffer;
}
next(): number {
return this.buffer.charCodeAt(this.pos++);
}
peek(): number {
return this.buffer.charCodeAt(this.pos);
}
indexOf(char: string): number {
const { buffer, pos } = this;
const idx = buffer.indexOf(char, pos);
return idx === -1 ? buffer.length : idx;
}
}

51
node_modules/@jridgewell/sourcemap-codec/src/vlq.ts generated vendored Normal file
View File

@ -0,0 +1,51 @@
import type { StringReader, StringWriter } from './strings';
export const comma = ','.charCodeAt(0);
export const semicolon = ';'.charCodeAt(0);
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const intToChar = new Uint8Array(64); // 64 possible chars.
const charToInt = new Uint8Array(128); // z is 122 in ASCII
for (let i = 0; i < chars.length; i++) {
const c = chars.charCodeAt(i);
intToChar[i] = c;
charToInt[c] = i;
}
export function decodeInteger(reader: StringReader): number {
let value = 0;
let shift = 0;
let integer = 0;
do {
const c = reader.next();
integer = charToInt[c];
value |= (integer & 31) << shift;
shift += 5;
} while (integer & 32);
return value;
}
export function decodeSign(num: number): number {
return num & 1 ? -0x80000000 | -(num >>> 1) : num >>> 1;
}
export function encodeInteger(builder: StringWriter, num: number) {
do {
let clamped = num & 0b011111;
num >>>= 5;
if (num > 0) clamped |= 0b100000;
builder.write(intToChar[clamped]);
} while (num > 0);
}
export function encodeSign(num: number): number {
return num < 0 ? (-num << 1) | 1 : num << 1;
}
export function hasMoreVlq(reader: StringReader, max: number) {
if (reader.pos >= max) return false;
return reader.peek() !== comma;
}

View File

@ -0,0 +1,5 @@
export type MappingIndex = number;
export type RangeMappings = MappingIndex[][];
export declare function decodeRangeMappings(input: string): RangeMappings;
export declare function encodeRangeMappings(decoded: RangeMappings): string;
//# sourceMappingURL=range-mappings.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"range-mappings.d.ts","sourceRoot":"","sources":["../src/range-mappings.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAClC,MAAM,MAAM,aAAa,GAAG,YAAY,EAAE,EAAE,CAAC;AAE7C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAoBhE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAkBlE"}

View File

@ -0,0 +1,5 @@
export type MappingIndex = number;
export type RangeMappings = MappingIndex[][];
export declare function decodeRangeMappings(input: string): RangeMappings;
export declare function encodeRangeMappings(decoded: RangeMappings): string;
//# sourceMappingURL=range-mappings.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"range-mappings.d.ts","sourceRoot":"","sources":["../src/range-mappings.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAClC,MAAM,MAAM,aAAa,GAAG,YAAY,EAAE,EAAE,CAAC;AAE7C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAoBhE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAkBlE"}

View File

@ -0,0 +1,50 @@
type Line = number;
type Column = number;
type Kind = number;
type Name = number;
type Var = number;
type SourcesIndex = number;
type ScopesIndex = number;
type Mix<A, B, O> = (A & O) | (B & O);
export type OriginalScope = Mix<[
Line,
Column,
Line,
Column,
Kind
], [
Line,
Column,
Line,
Column,
Kind,
Name
], {
vars: Var[];
}>;
export type GeneratedRange = Mix<[
Line,
Column,
Line,
Column
], [
Line,
Column,
Line,
Column,
SourcesIndex,
ScopesIndex
], {
callsite: CallSite | null;
bindings: Binding[];
isScope: boolean;
}>;
export type CallSite = [SourcesIndex, Line, Column];
type Binding = BindingExpressionRange[];
export type BindingExpressionRange = [Name] | [Name, Line, Column];
export declare function decodeOriginalScopes(input: string): OriginalScope[];
export declare function encodeOriginalScopes(scopes: OriginalScope[]): string;
export declare function decodeGeneratedRanges(input: string): GeneratedRange[];
export declare function encodeGeneratedRanges(ranges: GeneratedRange[]): string;
export {};
//# sourceMappingURL=scopes.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../src/scopes.ts"],"names":[],"mappings":"AAaA,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,MAAM,GAAG,MAAM,CAAC;AACrB,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,GAAG,GAAG,MAAM,CAAC;AAClB,KAAK,YAAY,GAAG,MAAM,CAAC;AAC3B,KAAK,WAAW,GAAG,MAAM,CAAC;AAE1B,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAEtC,MAAM,MAAM,aAAa,GAAG,GAAG,CAC7B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,IAAI;CAAC,EAClC;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,IAAI;CAAC,EACxC;IAAE,IAAI,EAAE,GAAG,EAAE,CAAA;CAAE,CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,GAAG,CAC9B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;CAAC,EAC5B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,YAAY;IAAE,WAAW;CAAC,EACvD;IACE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB,CACF,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACpD,KAAK,OAAO,GAAG,sBAAsB,EAAE,CAAC;AACxC,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAEnE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,EAAE,CA2CnE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAQpE;AA6CD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE,CAmGrE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAUtE"}

View File

@ -0,0 +1,50 @@
type Line = number;
type Column = number;
type Kind = number;
type Name = number;
type Var = number;
type SourcesIndex = number;
type ScopesIndex = number;
type Mix<A, B, O> = (A & O) | (B & O);
export type OriginalScope = Mix<[
Line,
Column,
Line,
Column,
Kind
], [
Line,
Column,
Line,
Column,
Kind,
Name
], {
vars: Var[];
}>;
export type GeneratedRange = Mix<[
Line,
Column,
Line,
Column
], [
Line,
Column,
Line,
Column,
SourcesIndex,
ScopesIndex
], {
callsite: CallSite | null;
bindings: Binding[];
isScope: boolean;
}>;
export type CallSite = [SourcesIndex, Line, Column];
type Binding = BindingExpressionRange[];
export type BindingExpressionRange = [Name] | [Name, Line, Column];
export declare function decodeOriginalScopes(input: string): OriginalScope[];
export declare function encodeOriginalScopes(scopes: OriginalScope[]): string;
export declare function decodeGeneratedRanges(input: string): GeneratedRange[];
export declare function encodeGeneratedRanges(ranges: GeneratedRange[]): string;
export {};
//# sourceMappingURL=scopes.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../src/scopes.ts"],"names":[],"mappings":"AAaA,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,MAAM,GAAG,MAAM,CAAC;AACrB,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,GAAG,GAAG,MAAM,CAAC;AAClB,KAAK,YAAY,GAAG,MAAM,CAAC;AAC3B,KAAK,WAAW,GAAG,MAAM,CAAC;AAE1B,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAEtC,MAAM,MAAM,aAAa,GAAG,GAAG,CAC7B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,IAAI;CAAC,EAClC;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,IAAI;CAAC,EACxC;IAAE,IAAI,EAAE,GAAG,EAAE,CAAA;CAAE,CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,GAAG,CAC9B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;CAAC,EAC5B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,YAAY;IAAE,WAAW;CAAC,EACvD;IACE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB,CACF,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACpD,KAAK,OAAO,GAAG,sBAAsB,EAAE,CAAC;AACxC,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAEnE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,EAAE,CA2CnE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAQpE;AA6CD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE,CAmGrE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAUtE"}

Some files were not shown because too many files have changed in this diff Show More