0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-01-06 22:10:10 -05:00

[ci] format

This commit is contained in:
Matthew Phillips 2024-07-17 17:38:27 +00:00 committed by astrobot-houston
parent d495df5361
commit b46a78e1c9
26 changed files with 1602 additions and 1319 deletions

View file

@ -43,7 +43,21 @@ html {
-o-tab-size: 4; -o-tab-size: 4;
tab-size: 4; tab-size: 4;
/* 3 */ /* 3 */
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-family:
ui-sans-serif,
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
Arial,
'Noto Sans',
sans-serif,
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
'Noto Color Emoji';
/* 4 */ /* 4 */
} }
@ -124,7 +138,8 @@ code,
kbd, kbd,
samp, samp,
pre { pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
'Courier New', monospace;
/* 1 */ /* 1 */
font-size: 1em; font-size: 1em;
/* 2 */ /* 2 */
@ -347,14 +362,16 @@ textarea {
2. Set the default placeholder color to the user's configured gray 400 color. 2. Set the default placeholder color to the user's configured gray 400 color.
*/ */
input::-moz-placeholder, textarea::-moz-placeholder { input::-moz-placeholder,
textarea::-moz-placeholder {
opacity: 1; opacity: 1;
/* 1 */ /* 1 */
color: #9ca3af; color: #9ca3af;
/* 2 */ /* 2 */
} }
input:-ms-input-placeholder, textarea:-ms-input-placeholder { input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
opacity: 1; opacity: 1;
/* 1 */ /* 1 */
color: #9ca3af; color: #9ca3af;
@ -374,7 +391,7 @@ Set the default cursor for buttons.
*/ */
button, button,
[role="button"] { [role='button'] {
cursor: pointer; cursor: pointer;
} }
@ -424,7 +441,21 @@ Ensure the default browser behavior of the `hidden` attribute.
display: none; display: none;
} }
[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { [type='text'],
[type='email'],
[type='url'],
[type='password'],
[type='number'],
[type='date'],
[type='datetime-local'],
[type='month'],
[type='search'],
[type='tel'],
[type='time'],
[type='week'],
[multiple],
textarea,
select {
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
appearance: none; appearance: none;
@ -441,30 +472,49 @@ Ensure the default browser behavior of the `hidden` attribute.
--tw-shadow: 0 0 #0000; --tw-shadow: 0 0 #0000;
} }
[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { [type='text']:focus,
[type='email']:focus,
[type='url']:focus,
[type='password']:focus,
[type='number']:focus,
[type='date']:focus,
[type='datetime-local']:focus,
[type='month']:focus,
[type='search']:focus,
[type='tel']:focus,
[type='time']:focus,
[type='week']:focus,
[multiple]:focus,
textarea:focus,
select:focus {
outline: 2px solid transparent; outline: 2px solid transparent;
outline-offset: 2px; outline-offset: 2px;
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); --tw-ring-inset: var(--tw-empty, /*!*/ /*!*/);
--tw-ring-offset-width: 0px; --tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff; --tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb; --tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width))
var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
border-color: #2563eb; border-color: #2563eb;
} }
input::-moz-placeholder, textarea::-moz-placeholder { input::-moz-placeholder,
textarea::-moz-placeholder {
color: #6b7280; color: #6b7280;
opacity: 1; opacity: 1;
} }
input:-ms-input-placeholder, textarea:-ms-input-placeholder { input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
color: #6b7280; color: #6b7280;
opacity: 1; opacity: 1;
} }
input::placeholder,textarea::placeholder { input::placeholder,
textarea::placeholder {
color: #6b7280; color: #6b7280;
opacity: 1; opacity: 1;
} }
@ -477,7 +527,15 @@ input::placeholder,textarea::placeholder {
min-height: 1.5em; min-height: 1.5em;
} }
::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { ::-webkit-datetime-edit,
::-webkit-datetime-edit-year-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-minute-field,
::-webkit-datetime-edit-second-field,
::-webkit-datetime-edit-millisecond-field,
::-webkit-datetime-edit-meridiem-field {
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
} }
@ -502,7 +560,8 @@ select {
color-adjust: unset; color-adjust: unset;
} }
[type='checkbox'],[type='radio'] { [type='checkbox'],
[type='radio'] {
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
appearance: none; appearance: none;
@ -534,19 +593,23 @@ select {
border-radius: 100%; border-radius: 100%;
} }
[type='checkbox']:focus,[type='radio']:focus { [type='checkbox']:focus,
[type='radio']:focus {
outline: 2px solid transparent; outline: 2px solid transparent;
outline-offset: 2px; outline-offset: 2px;
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); --tw-ring-inset: var(--tw-empty, /*!*/ /*!*/);
--tw-ring-offset-width: 2px; --tw-ring-offset-width: 2px;
--tw-ring-offset-color: #fff; --tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb; --tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width))
var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
} }
[type='checkbox']:checked,[type='radio']:checked { [type='checkbox']:checked,
[type='radio']:checked {
border-color: transparent; border-color: transparent;
background-color: currentColor; background-color: currentColor;
background-size: 100% 100%; background-size: 100% 100%;
@ -562,7 +625,10 @@ select {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
} }
[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { [type='checkbox']:checked:hover,
[type='checkbox']:checked:focus,
[type='radio']:checked:hover,
[type='radio']:checked:focus {
border-color: transparent; border-color: transparent;
background-color: currentColor; background-color: currentColor;
} }
@ -576,7 +642,8 @@ select {
background-repeat: no-repeat; background-repeat: no-repeat;
} }
[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { [type='checkbox']:indeterminate:hover,
[type='checkbox']:indeterminate:focus {
border-color: transparent; border-color: transparent;
background-color: currentColor; background-color: currentColor;
} }
@ -599,11 +666,18 @@ body {
font-family: Poppins, sans-serif; font-family: Poppins, sans-serif;
} }
h1, h2, h3, h4, h5, h6 { h1,
h2,
h3,
h4,
h5,
h6 {
font-family: Roboto, sans-serif; font-family: Roboto, sans-serif;
} }
*, ::before, ::after { *,
::before,
::after {
--tw-translate-x: 0; --tw-translate-x: 0;
--tw-translate-y: 0; --tw-translate-y: 0;
--tw-rotate: 0; --tw-rotate: 0;
@ -694,8 +768,10 @@ h1, h2, h3, h4, h5, h6 {
} }
.color-selector input:checked + label { .color-selector input:checked + label {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width))
var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
--tw-ring-opacity: 1; --tw-ring-opacity: 1;
--tw-ring-color: rgb(253 61 87 / var(--tw-ring-opacity)); --tw-ring-color: rgb(253 61 87 / var(--tw-ring-opacity));
@ -736,8 +812,10 @@ h1, h2, h3, h4, h5, h6 {
.input-box:focus { .input-box:focus {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(253 61 87 / var(--tw-border-opacity)); border-color: rgb(253 61 87 / var(--tw-border-opacity));
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width))
var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
} }
@ -1546,7 +1624,7 @@ h1, h2, h3, h4, h5, h6 {
} }
.leading-3 { .leading-3 {
line-height: .75rem; line-height: 0.75rem;
} }
.tracking-wider { .tracking-wider {
@ -1635,25 +1713,53 @@ h1, h2, h3, h4, h5, h6 {
.shadow-sm { .shadow-sm {
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
} }
.shadow-md { .shadow-md {
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
} }
.shadow { .shadow {
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
} }
.transition { .transition {
transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-text-decoration-color, -webkit-backdrop-filter; transition-property:
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; color,
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-text-decoration-color, -webkit-backdrop-filter; background-color,
border-color,
fill,
stroke,
opacity,
box-shadow,
transform,
filter,
-webkit-text-decoration-color,
-webkit-backdrop-filter;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke,
opacity, box-shadow, transform, filter, backdrop-filter;
transition-property:
color,
background-color,
border-color,
text-decoration-color,
fill,
stroke,
opacity,
box-shadow,
transform,
filter,
backdrop-filter,
-webkit-text-decoration-color,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms; transition-duration: 150ms;
} }
@ -1717,8 +1823,10 @@ h1, h2, h3, h4, h5, h6 {
} }
.focus\:ring-0:focus { .focus\:ring-0:focus {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width))
var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
} }

View file

@ -1 +1,11 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} {
"name": "",
"short_name": "",
"icons": [
{ "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View file

@ -1,4 +1,4 @@
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"); @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;

View file

@ -5,17 +5,21 @@ const { placeholder } = Astro.props;
let wishlist = 0; let wishlist = 0;
let cart = 0; let cart = 0;
if(!placeholder) { if (!placeholder) {
await new Promise(resolve => setTimeout(resolve, 3000)); await new Promise((resolve) => setTimeout(resolve, 3000));
} }
--- ---
<a href="#" class="text-center text-gray-700 hover:text-primary transition relative"> <a href="#" class="text-center text-gray-700 hover:text-primary transition relative">
<div class="text-2xl"> <div class="text-2xl">
<i class="fa-regular fa-heart"></i> <i class="fa-regular fa-heart"></i>
</div> </div>
<div class="text-xs leading-3">Wishlist</div> <div class="text-xs leading-3">Wishlist</div>
<div <div
class="absolute right-0 -top-1 w-5 h-5 rounded-full flex items-center justify-center bg-primary text-white text-xs">{ wishlist }</div> class="absolute right-0 -top-1 w-5 h-5 rounded-full flex items-center justify-center bg-primary text-white text-xs"
>
{wishlist}
</div>
</a> </a>
<a href="#" class="text-center text-gray-700 hover:text-primary transition relative"> <a href="#" class="text-center text-gray-700 hover:text-primary transition relative">
<div class="text-2xl"> <div class="text-2xl">

View file

@ -4,41 +4,48 @@ import AddToCart from '../components/AddToCart';
import PersonalBar from '../components/PersonalBar.astro'; import PersonalBar from '../components/PersonalBar.astro';
import '@fortawesome/fontawesome-free/css/all.min.css'; import '@fortawesome/fontawesome-free/css/all.min.css';
--- ---
<!DOCTYPE html>
<html lang="en">
<head> <!doctype html>
<meta charset="UTF-8"> <html lang="en">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Product - Ecommerce Tailwind</title> <title>Product - Ecommerce Tailwind</title>
<link rel="shortcut icon" href="../assets/images/favicon/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="../assets/images/favicon/favicon.ico" type="image/x-icon" />
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link <link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&family=Roboto:wght@400;500;700&display=swap" href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&family=Roboto:wght@400;500;700&display=swap"
rel="stylesheet"> rel="stylesheet"
</head> />
</head>
<body> <body>
<!-- header --> <!-- header -->
<header class="py-4 shadow-sm bg-white"> <header class="py-4 shadow-sm bg-white">
<div class="container flex items-center justify-between"> <div class="container flex items-center justify-between">
<a href="index.html"> <a href="index.html">
<img src="../assets/images/logo.svg" alt="Logo" class="w-32"> <img src="../assets/images/logo.svg" alt="Logo" class="w-32" />
</a> </a>
<div class="w-full max-w-xl relative flex"> <div class="w-full max-w-xl relative flex">
<span class="absolute left-4 top-3 text-lg text-gray-400"> <span class="absolute left-4 top-3 text-lg text-gray-400">
<i class="fa-solid fa-magnifying-glass"></i> <i class="fa-solid fa-magnifying-glass"></i>
</span> </span>
<input type="text" name="search" id="search" <input
type="text"
name="search"
id="search"
class="w-full border border-primary border-r-0 pl-12 py-3 pr-3 rounded-l-md focus:outline-none" class="w-full border border-primary border-r-0 pl-12 py-3 pr-3 rounded-l-md focus:outline-none"
placeholder="search"> placeholder="search"
/>
<button <button
class="bg-primary border border-primary text-white px-8 rounded-r-md hover:bg-transparent hover:text-primary transition">Search</button> class="bg-primary border border-primary text-white px-8 rounded-r-md hover:bg-transparent hover:text-primary transition"
>Search</button
>
</div> </div>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
@ -61,29 +68,50 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<!-- dropdown --> <!-- dropdown -->
<div <div
class="absolute w-full left-0 top-full bg-white shadow-md py-3 divide-y divide-gray-300 divide-dashed opacity-0 group-hover:opacity-100 transition duration-300 invisible group-hover:visible"> class="absolute w-full left-0 top-full bg-white shadow-md py-3 divide-y divide-gray-300 divide-dashed opacity-0 group-hover:opacity-100 transition duration-300 invisible group-hover:visible"
>
<a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition"> <a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition">
<img src="../assets/images/icons/sofa.svg" alt="sofa" class="w-5 h-5 object-contain"> <img
src="../assets/images/icons/sofa.svg"
alt="sofa"
class="w-5 h-5 object-contain"
/>
<span class="ml-6 text-gray-600 text-sm">Sofa</span> <span class="ml-6 text-gray-600 text-sm">Sofa</span>
</a> </a>
<a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition"> <a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition">
<img src="../assets/images/icons/terrace.svg" alt="terrace" class="w-5 h-5 object-contain"> <img
src="../assets/images/icons/terrace.svg"
alt="terrace"
class="w-5 h-5 object-contain"
/>
<span class="ml-6 text-gray-600 text-sm">Terarce</span> <span class="ml-6 text-gray-600 text-sm">Terarce</span>
</a> </a>
<a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition"> <a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition">
<img src="../assets/images/icons/bed.svg" alt="bed" class="w-5 h-5 object-contain"> <img src="../assets/images/icons/bed.svg" alt="bed" class="w-5 h-5 object-contain" />
<span class="ml-6 text-gray-600 text-sm">Bed</span> <span class="ml-6 text-gray-600 text-sm">Bed</span>
</a> </a>
<a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition"> <a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition">
<img src="../assets/images/icons/office.svg" alt="office" class="w-5 h-5 object-contain"> <img
src="../assets/images/icons/office.svg"
alt="office"
class="w-5 h-5 object-contain"
/>
<span class="ml-6 text-gray-600 text-sm">office</span> <span class="ml-6 text-gray-600 text-sm">office</span>
</a> </a>
<a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition"> <a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition">
<img src="../assets/images/icons/outdoor-cafe.svg" alt="outdoor" class="w-5 h-5 object-contain"> <img
src="../assets/images/icons/outdoor-cafe.svg"
alt="outdoor"
class="w-5 h-5 object-contain"
/>
<span class="ml-6 text-gray-600 text-sm">Outdoor</span> <span class="ml-6 text-gray-600 text-sm">Outdoor</span>
</a> </a>
<a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition"> <a href="#" class="flex items-center px-6 py-3 hover:bg-gray-100 transition">
<img src="../assets/images/icons/bed-2.svg" alt="Mattress" class="w-5 h-5 object-contain"> <img
src="../assets/images/icons/bed-2.svg"
alt="Mattress"
class="w-5 h-5 object-contain"
/>
<span class="ml-6 text-gray-600 text-sm">Mattress</span> <span class="ml-6 text-gray-600 text-sm">Mattress</span>
</a> </a>
</div> </div>
@ -117,14 +145,33 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<!-- product-detail --> <!-- product-detail -->
<div class="container grid grid-cols-2 gap-6"> <div class="container grid grid-cols-2 gap-6">
<div> <div>
<img src="../assets/images/products/product1.jpg" alt="product" class="w-full"> <img src="../assets/images/products/product1.jpg" alt="product" class="w-full" />
<div class="grid grid-cols-5 gap-4 mt-4"> <div class="grid grid-cols-5 gap-4 mt-4">
<img src="../assets/images/products/product2.jpg" alt="product2" <img
class="w-full cursor-pointer border border-primary"> src="../assets/images/products/product2.jpg"
<img src="../assets/images/products/product3.jpg" alt="product2" class="w-full cursor-pointer border"> alt="product2"
<img src="../assets/images/products/product4.jpg" alt="product2" class="w-full cursor-pointer border"> class="w-full cursor-pointer border border-primary"
<img src="../assets/images/products/product5.jpg" alt="product2" class="w-full cursor-pointer border"> />
<img src="../assets/images/products/product6.jpg" alt="product2" class="w-full cursor-pointer border"> <img
src="../assets/images/products/product3.jpg"
alt="product2"
class="w-full cursor-pointer border"
/>
<img
src="../assets/images/products/product4.jpg"
alt="product2"
class="w-full cursor-pointer border"
/>
<img
src="../assets/images/products/product5.jpg"
alt="product2"
class="w-full cursor-pointer border"
/>
<img
src="../assets/images/products/product6.jpg"
alt="product2"
class="w-full cursor-pointer border"
/>
</div> </div>
</div> </div>
@ -163,37 +210,54 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<p class="text-base text-gray-400 line-through">$55.00</p> <p class="text-base text-gray-400 line-through">$55.00</p>
</div> </div>
<p class="mt-4 text-gray-600">Lorem ipsum dolor sit amet consectetur adipisicing elit. Eos eius eum <p class="mt-4 text-gray-600">
reprehenderit dolore vel mollitia optio consequatur hic asperiores inventore suscipit, velit Lorem ipsum dolor sit amet consectetur adipisicing elit. Eos eius eum reprehenderit dolore
consequuntur, voluptate doloremque iure necessitatibus adipisci magnam porro.</p> vel mollitia optio consequatur hic asperiores inventore suscipit, velit consequuntur,
voluptate doloremque iure necessitatibus adipisci magnam porro.
</p>
<div class="pt-4"> <div class="pt-4">
<h3 class="text-sm text-gray-800 uppercase mb-1">Size</h3> <h3 class="text-sm text-gray-800 uppercase mb-1">Size</h3>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="size-selector"> <div class="size-selector">
<input type="radio" name="size" id="size-xs" class="hidden"> <input type="radio" name="size" id="size-xs" class="hidden" />
<label for="size-xs" <label
class="text-xs border border-gray-200 rounded-sm h-6 w-6 flex items-center justify-center cursor-pointer shadow-sm text-gray-600">XS</label> for="size-xs"
class="text-xs border border-gray-200 rounded-sm h-6 w-6 flex items-center justify-center cursor-pointer shadow-sm text-gray-600"
>XS</label
>
</div> </div>
<div class="size-selector"> <div class="size-selector">
<input type="radio" name="size" id="size-sm" class="hidden"> <input type="radio" name="size" id="size-sm" class="hidden" />
<label for="size-sm" <label
class="text-xs border border-gray-200 rounded-sm h-6 w-6 flex items-center justify-center cursor-pointer shadow-sm text-gray-600">S</label> for="size-sm"
class="text-xs border border-gray-200 rounded-sm h-6 w-6 flex items-center justify-center cursor-pointer shadow-sm text-gray-600"
>S</label
>
</div> </div>
<div class="size-selector"> <div class="size-selector">
<input type="radio" name="size" id="size-m" class="hidden"> <input type="radio" name="size" id="size-m" class="hidden" />
<label for="size-m" <label
class="text-xs border border-gray-200 rounded-sm h-6 w-6 flex items-center justify-center cursor-pointer shadow-sm text-gray-600">M</label> for="size-m"
class="text-xs border border-gray-200 rounded-sm h-6 w-6 flex items-center justify-center cursor-pointer shadow-sm text-gray-600"
>M</label
>
</div> </div>
<div class="size-selector"> <div class="size-selector">
<input type="radio" name="size" id="size-l" class="hidden"> <input type="radio" name="size" id="size-l" class="hidden" />
<label for="size-l" <label
class="text-xs border border-gray-200 rounded-sm h-6 w-6 flex items-center justify-center cursor-pointer shadow-sm text-gray-600">L</label> for="size-l"
class="text-xs border border-gray-200 rounded-sm h-6 w-6 flex items-center justify-center cursor-pointer shadow-sm text-gray-600"
>L</label
>
</div> </div>
<div class="size-selector"> <div class="size-selector">
<input type="radio" name="size" id="size-xl" class="hidden"> <input type="radio" name="size" id="size-xl" class="hidden" />
<label for="size-xl" <label
class="text-xs border border-gray-200 rounded-sm h-6 w-6 flex items-center justify-center cursor-pointer shadow-sm text-gray-600">XL</label> for="size-xl"
class="text-xs border border-gray-200 rounded-sm h-6 w-6 flex items-center justify-center cursor-pointer shadow-sm text-gray-600"
>XL</label
>
</div> </div>
</div> </div>
</div> </div>
@ -202,55 +266,73 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<h3 class="text-xl text-gray-800 mb-3 uppercase font-medium">Color</h3> <h3 class="text-xl text-gray-800 mb-3 uppercase font-medium">Color</h3>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="color-selector"> <div class="color-selector">
<input type="radio" name="color" id="red" class="hidden"> <input type="radio" name="color" id="red" class="hidden" />
<label for="red" <label
for="red"
class="border border-gray-200 rounded-sm h-6 w-6 cursor-pointer shadow-sm block" class="border border-gray-200 rounded-sm h-6 w-6 cursor-pointer shadow-sm block"
style="background-color: #fc3d57;"></label> style="background-color: #fc3d57;"></label>
</div> </div>
<div class="color-selector"> <div class="color-selector">
<input type="radio" name="color" id="black" class="hidden"> <input type="radio" name="color" id="black" class="hidden" />
<label for="black" <label
for="black"
class="border border-gray-200 rounded-sm h-6 w-6 cursor-pointer shadow-sm block" class="border border-gray-200 rounded-sm h-6 w-6 cursor-pointer shadow-sm block"
style="background-color: #000;"></label> style="background-color: #000;"></label>
</div> </div>
<div class="color-selector"> <div class="color-selector">
<input type="radio" name="color" id="white" class="hidden"> <input type="radio" name="color" id="white" class="hidden" />
<label for="white" <label
for="white"
class="border border-gray-200 rounded-sm h-6 w-6 cursor-pointer shadow-sm block" class="border border-gray-200 rounded-sm h-6 w-6 cursor-pointer shadow-sm block"
style="background-color: #fff;"></label> style="background-color: #fff;"></label>
</div> </div>
</div> </div>
</div> </div>
<div class="mt-4"> <div class="mt-4">
<h3 class="text-sm text-gray-800 uppercase mb-1">Quantity</h3> <h3 class="text-sm text-gray-800 uppercase mb-1">Quantity</h3>
<div class="flex border border-gray-300 text-gray-600 divide-x divide-gray-300 w-max"> <div class="flex border border-gray-300 text-gray-600 divide-x divide-gray-300 w-max">
<div class="h-8 w-8 text-xl flex items-center justify-center cursor-pointer select-none">-</div> <div
class="h-8 w-8 text-xl flex items-center justify-center cursor-pointer select-none"
>
-
</div>
<div class="h-8 w-8 text-base flex items-center justify-center">4</div> <div class="h-8 w-8 text-base flex items-center justify-center">4</div>
<div class="h-8 w-8 text-xl flex items-center justify-center cursor-pointer select-none">+</div> <div
class="h-8 w-8 text-xl flex items-center justify-center cursor-pointer select-none"
>
+
</div>
</div> </div>
</div> </div>
<div class="mt-6 flex gap-3 border-b border-gray-200 pb-5 pt-5"> <div class="mt-6 flex gap-3 border-b border-gray-200 pb-5 pt-5">
<AddToCart client:load /> <AddToCart client:load />
<a href="#" <a
class="border border-gray-300 text-gray-600 px-8 py-2 font-medium rounded uppercase flex items-center gap-2 hover:text-primary transition"> href="#"
class="border border-gray-300 text-gray-600 px-8 py-2 font-medium rounded uppercase flex items-center gap-2 hover:text-primary transition"
>
<i class="fa-solid fa-heart"></i> Wishlist <i class="fa-solid fa-heart"></i> Wishlist
</a> </a>
</div> </div>
<div class="flex gap-3 mt-4"> <div class="flex gap-3 mt-4">
<a href="#" <a
class="text-gray-400 hover:text-gray-500 h-8 w-8 rounded-full border border-gray-300 flex items-center justify-center"> href="#"
class="text-gray-400 hover:text-gray-500 h-8 w-8 rounded-full border border-gray-300 flex items-center justify-center"
>
<i class="fa-brands fa-facebook-f"></i> <i class="fa-brands fa-facebook-f"></i>
</a> </a>
<a href="#" <a
class="text-gray-400 hover:text-gray-500 h-8 w-8 rounded-full border border-gray-300 flex items-center justify-center"> href="#"
class="text-gray-400 hover:text-gray-500 h-8 w-8 rounded-full border border-gray-300 flex items-center justify-center"
>
<i class="fa-brands fa-twitter"></i> <i class="fa-brands fa-twitter"></i>
</a> </a>
<a href="#" <a
class="text-gray-400 hover:text-gray-500 h-8 w-8 rounded-full border border-gray-300 flex items-center justify-center"> href="#"
class="text-gray-400 hover:text-gray-500 h-8 w-8 rounded-full border border-gray-300 flex items-center justify-center"
>
<i class="fa-brands fa-instagram"></i> <i class="fa-brands fa-instagram"></i>
</a> </a>
</div> </div>
@ -260,32 +342,40 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<!-- description --> <!-- description -->
<div class="container pb-16"> <div class="container pb-16">
<h3 class="border-b border-gray-200 font-roboto text-gray-800 pb-3 font-medium">Product details</h3> <h3 class="border-b border-gray-200 font-roboto text-gray-800 pb-3 font-medium">
Product details
</h3>
<div class="w-3/5 pt-6"> <div class="w-3/5 pt-6">
<div class="text-gray-600"> <div class="text-gray-600">
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Tenetur necessitatibus deleniti natus <p>
dolore cum maiores suscipit optio itaque voluptatibus veritatis tempora iste facilis non aut Lorem, ipsum dolor sit amet consectetur adipisicing elit. Tenetur necessitatibus
sapiente dolor quisquam, ex ab.</p> deleniti natus dolore cum maiores suscipit optio itaque voluptatibus veritatis tempora
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorum, quae accusantium voluptatem iste facilis non aut sapiente dolor quisquam, ex ab.
blanditiis sapiente voluptatum. Autem ab, dolorum assumenda earum veniam eius illo fugiat possimus </p>
illum dolor totam, ducimus excepturi.</p> <p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Error quia modi ut expedita! Iure molestiae Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorum, quae accusantium
labore cumque nobis quasi fuga, quibusdam rem? Temporibus consectetur corrupti rerum veritatis voluptatem blanditiis sapiente voluptatum. Autem ab, dolorum assumenda earum veniam eius
numquam labore amet.</p> illo fugiat possimus illum dolor totam, ducimus excepturi.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Error quia modi ut expedita!
Iure molestiae labore cumque nobis quasi fuga, quibusdam rem? Temporibus consectetur
corrupti rerum veritatis numquam labore amet.
</p>
</div> </div>
<table class="table-auto border-collapse w-full text-left text-gray-600 text-sm mt-6"> <table class="table-auto border-collapse w-full text-left text-gray-600 text-sm mt-6">
<tr> <tr>
<th class="py-2 px-4 border border-gray-300 w-40 font-medium">Color</th> <th class="py-2 px-4 border border-gray-300 w-40 font-medium">Color</th>
<th class="py-2 px-4 border border-gray-300 ">Blank, Brown, Red</th> <th class="py-2 px-4 border border-gray-300">Blank, Brown, Red</th>
</tr> </tr>
<tr> <tr>
<th class="py-2 px-4 border border-gray-300 w-40 font-medium">Material</th> <th class="py-2 px-4 border border-gray-300 w-40 font-medium">Material</th>
<th class="py-2 px-4 border border-gray-300 ">Latex</th> <th class="py-2 px-4 border border-gray-300">Latex</th>
</tr> </tr>
<tr> <tr>
<th class="py-2 px-4 border border-gray-300 w-40 font-medium">Weight</th> <th class="py-2 px-4 border border-gray-300 w-40 font-medium">Weight</th>
<th class="py-2 px-4 border border-gray-300 ">55kg</th> <th class="py-2 px-4 border border-gray-300">55kg</th>
</tr> </tr>
</table> </table>
</div> </div>
@ -298,25 +388,34 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<div class="grid grid-cols-4 gap-6"> <div class="grid grid-cols-4 gap-6">
<div class="bg-white shadow rounded overflow-hidden group"> <div class="bg-white shadow rounded overflow-hidden group">
<div class="relative"> <div class="relative">
<img src="../assets/images/products/product1.jpg" alt="product 1" class="w-full"> <img src="../assets/images/products/product1.jpg" alt="product 1" class="w-full" />
<div class="absolute inset-0 bg-black bg-opacity-40 flex items-center <div
justify-center gap-2 opacity-0 group-hover:opacity-100 transition"> class="absolute inset-0 bg-black bg-opacity-40 flex items-center
<a href="#" justify-center gap-2 opacity-0 group-hover:opacity-100 transition"
>
<a
href="#"
class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition" class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition"
title="view product"> title="view product"
>
<i class="fa-solid fa-magnifying-glass"></i> <i class="fa-solid fa-magnifying-glass"></i>
</a> </a>
<a href="#" <a
href="#"
class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition" class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition"
title="add to wishlist"> title="add to wishlist"
>
<i class="fa-solid fa-heart"></i> <i class="fa-solid fa-heart"></i>
</a> </a>
</div> </div>
</div> </div>
<div class="pt-4 pb-3 px-4"> <div class="pt-4 pb-3 px-4">
<a href="#"> <a href="#">
<h4 class="uppercase font-medium text-xl mb-2 text-gray-800 hover:text-primary transition">Guyer <h4
Chair</h4> class="uppercase font-medium text-xl mb-2 text-gray-800 hover:text-primary transition"
>
Guyer Chair
</h4>
</a> </a>
<div class="flex items-baseline mb-1 space-x-2"> <div class="flex items-baseline mb-1 space-x-2">
<p class="text-xl text-primary font-semibold">$45.00</p> <p class="text-xl text-primary font-semibold">$45.00</p>
@ -337,25 +436,34 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
</div> </div>
<div class="bg-white shadow rounded overflow-hidden group"> <div class="bg-white shadow rounded overflow-hidden group">
<div class="relative"> <div class="relative">
<img src="../assets/images/products/product4.jpg" alt="product 1" class="w-full"> <img src="../assets/images/products/product4.jpg" alt="product 1" class="w-full" />
<div class="absolute inset-0 bg-black bg-opacity-40 flex items-center <div
justify-center gap-2 opacity-0 group-hover:opacity-100 transition"> class="absolute inset-0 bg-black bg-opacity-40 flex items-center
<a href="#" justify-center gap-2 opacity-0 group-hover:opacity-100 transition"
>
<a
href="#"
class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition" class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition"
title="view product"> title="view product"
>
<i class="fa-solid fa-magnifying-glass"></i> <i class="fa-solid fa-magnifying-glass"></i>
</a> </a>
<a href="#" <a
href="#"
class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition" class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition"
title="add to wishlist"> title="add to wishlist"
>
<i class="fa-solid fa-heart"></i> <i class="fa-solid fa-heart"></i>
</a> </a>
</div> </div>
</div> </div>
<div class="pt-4 pb-3 px-4"> <div class="pt-4 pb-3 px-4">
<a href="#"> <a href="#">
<h4 class="uppercase font-medium text-xl mb-2 text-gray-800 hover:text-primary transition">Bed <h4
King Size</h4> class="uppercase font-medium text-xl mb-2 text-gray-800 hover:text-primary transition"
>
Bed King Size
</h4>
</a> </a>
<div class="flex items-baseline mb-1 space-x-2"> <div class="flex items-baseline mb-1 space-x-2">
<p class="text-xl text-primary font-semibold">$45.00</p> <p class="text-xl text-primary font-semibold">$45.00</p>
@ -376,25 +484,34 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
</div> </div>
<div class="bg-white shadow rounded overflow-hidden group"> <div class="bg-white shadow rounded overflow-hidden group">
<div class="relative"> <div class="relative">
<img src="../assets/images/products/product2.jpg" alt="product 1" class="w-full"> <img src="../assets/images/products/product2.jpg" alt="product 1" class="w-full" />
<div class="absolute inset-0 bg-black bg-opacity-40 flex items-center <div
justify-center gap-2 opacity-0 group-hover:opacity-100 transition"> class="absolute inset-0 bg-black bg-opacity-40 flex items-center
<a href="#" justify-center gap-2 opacity-0 group-hover:opacity-100 transition"
>
<a
href="#"
class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition" class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition"
title="view product"> title="view product"
>
<i class="fa-solid fa-magnifying-glass"></i> <i class="fa-solid fa-magnifying-glass"></i>
</a> </a>
<a href="#" <a
href="#"
class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition" class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition"
title="add to wishlist"> title="add to wishlist"
>
<i class="fa-solid fa-heart"></i> <i class="fa-solid fa-heart"></i>
</a> </a>
</div> </div>
</div> </div>
<div class="pt-4 pb-3 px-4"> <div class="pt-4 pb-3 px-4">
<a href="#"> <a href="#">
<h4 class="uppercase font-medium text-xl mb-2 text-gray-800 hover:text-primary transition"> <h4
Couple Sofa</h4> class="uppercase font-medium text-xl mb-2 text-gray-800 hover:text-primary transition"
>
Couple Sofa
</h4>
</a> </a>
<div class="flex items-baseline mb-1 space-x-2"> <div class="flex items-baseline mb-1 space-x-2">
<p class="text-xl text-primary font-semibold">$45.00</p> <p class="text-xl text-primary font-semibold">$45.00</p>
@ -415,25 +532,34 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
</div> </div>
<div class="bg-white shadow rounded overflow-hidden group"> <div class="bg-white shadow rounded overflow-hidden group">
<div class="relative"> <div class="relative">
<img src="../assets/images/products/product3.jpg" alt="product 1" class="w-full"> <img src="../assets/images/products/product3.jpg" alt="product 1" class="w-full" />
<div class="absolute inset-0 bg-black bg-opacity-40 flex items-center <div
justify-center gap-2 opacity-0 group-hover:opacity-100 transition"> class="absolute inset-0 bg-black bg-opacity-40 flex items-center
<a href="#" justify-center gap-2 opacity-0 group-hover:opacity-100 transition"
>
<a
href="#"
class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition" class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition"
title="view product"> title="view product"
>
<i class="fa-solid fa-magnifying-glass"></i> <i class="fa-solid fa-magnifying-glass"></i>
</a> </a>
<a href="#" <a
href="#"
class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition" class="text-white text-lg w-9 h-8 rounded-full bg-primary flex items-center justify-center hover:bg-gray-800 transition"
title="add to wishlist"> title="add to wishlist"
>
<i class="fa-solid fa-heart"></i> <i class="fa-solid fa-heart"></i>
</a> </a>
</div> </div>
</div> </div>
<div class="pt-4 pb-3 px-4"> <div class="pt-4 pb-3 px-4">
<a href="#"> <a href="#">
<h4 class="uppercase font-medium text-xl mb-2 text-gray-800 hover:text-primary transition"> <h4
Mattrass X</h4> class="uppercase font-medium text-xl mb-2 text-gray-800 hover:text-primary transition"
>
Mattrass X
</h4>
</a> </a>
<div class="flex items-baseline mb-1 space-x-2"> <div class="flex items-baseline mb-1 space-x-2">
<p class="text-xl text-primary font-semibold">$45.00</p> <p class="text-xl text-primary font-semibold">$45.00</p>
@ -460,19 +586,22 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<footer class="bg-white pt-16 pb-12 border-t border-gray-100"> <footer class="bg-white pt-16 pb-12 border-t border-gray-100">
<div class="container grid grid-cols-3"> <div class="container grid grid-cols-3">
<div class="col-span-1 space-y-8 mr-2"> <div class="col-span-1 space-y-8 mr-2">
<img src="../assets/images/logo.svg" alt="logo" class="w-30"> <img src="../assets/images/logo.svg" alt="logo" class="w-30" />
<div class="mr-2"> <div class="mr-2">
<p class="text-gray-500"> <p class="text-gray-500">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia, hic? Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia, hic?
</p> </p>
</div> </div>
<div class="flex space-x-6"> <div class="flex space-x-6">
<a href="#" class="text-gray-400 hover:text-gray-500"><i <a href="#" class="text-gray-400 hover:text-gray-500"
class="fa-brands fa-facebook-square"></i></a> ><i class="fa-brands fa-facebook-square"></i></a
<a href="#" class="text-gray-400 hover:text-gray-500"><i >
class="fa-brands fa-instagram-square"></i></a> <a href="#" class="text-gray-400 hover:text-gray-500"
<a href="#" class="text-gray-400 hover:text-gray-500"><i ><i class="fa-brands fa-instagram-square"></i></a
class="fa-brands fa-twitter-square"></i></a> >
<a href="#" class="text-gray-400 hover:text-gray-500"
><i class="fa-brands fa-twitter-square"></i></a
>
<a href="#" class="text-gray-400 hover:text-gray-500"> <a href="#" class="text-gray-400 hover:text-gray-500">
<i class="fa-brands fa-github-square"></i> <i class="fa-brands fa-github-square"></i>
</a> </a>
@ -482,7 +611,9 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<div class="col-span-2 grid grid-cols-2 gap-8"> <div class="col-span-2 grid grid-cols-2 gap-8">
<div class="grid grid-cols-2 gap-8"> <div class="grid grid-cols-2 gap-8">
<div> <div>
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Solutions</h3> <h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">
Solutions
</h3>
<div class="mt-4 space-y-4"> <div class="mt-4 space-y-4">
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Marketing</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Marketing</a>
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Analitycs</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Analitycs</a>
@ -495,7 +626,9 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Support</h3> <h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Support</h3>
<div class="mt-4 space-y-4"> <div class="mt-4 space-y-4">
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Pricing</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Pricing</a>
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Documentation</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block"
>Documentation</a
>
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Guides</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Guides</a>
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">API Status</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block">API Status</a>
</div> </div>
@ -503,7 +636,9 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
</div> </div>
<div class="grid grid-cols-2 gap-8"> <div class="grid grid-cols-2 gap-8">
<div> <div>
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Solutions</h3> <h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">
Solutions
</h3>
<div class="mt-4 space-y-4"> <div class="mt-4 space-y-4">
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Marketing</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Marketing</a>
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Analitycs</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Analitycs</a>
@ -516,7 +651,9 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Support</h3> <h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Support</h3>
<div class="mt-4 space-y-4"> <div class="mt-4 space-y-4">
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Pricing</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Pricing</a>
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Documentation</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block"
>Documentation</a
>
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Guides</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block">Guides</a>
<a href="#" class="text-base text-gray-500 hover:text-gray-900 block">API Status</a> <a href="#" class="text-base text-gray-500 hover:text-gray-900 block">API Status</a>
</div> </div>
@ -532,12 +669,10 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
<div class="container flex items-center justify-between"> <div class="container flex items-center justify-between">
<p class="text-white">&copy; TailCommerce - All Right Reserved</p> <p class="text-white">&copy; TailCommerce - All Right Reserved</p>
<div> <div>
<img src="../assets/images/methods.png" alt="methods" class="h-5"> <img src="../assets/images/methods.png" alt="methods" class="h-5" />
</div> </div>
</div> </div>
</div> </div>
<!-- ./copyright --> <!-- ./copyright -->
</body>
</body>
</html> </html>

View file

@ -3377,7 +3377,6 @@ export interface SSRMetadata {
headInTree: boolean; headInTree: boolean;
extraHead: string[]; extraHead: string[];
propagators: Set<AstroComponentInstance>; propagators: Set<AstroComponentInstance>;
} }
/* Preview server stuff */ /* Preview server stuff */

View file

@ -11,6 +11,7 @@ import { appendForwardSlash, removeFileExtension } from '../core/path.js';
import { isServerLikeOutput } from '../core/util.js'; import { isServerLikeOutput } from '../core/util.js';
import { rootRelativePath } from '../core/viteUtils.js'; import { rootRelativePath } from '../core/viteUtils.js';
import type { AstroPluginMetadata } from '../vite-plugin-astro/index.js'; import type { AstroPluginMetadata } from '../vite-plugin-astro/index.js';
import { createDefaultAstroMetadata } from '../vite-plugin-astro/metadata.js';
import { import {
CONTENT_FLAG, CONTENT_FLAG,
CONTENT_RENDER_FLAG, CONTENT_RENDER_FLAG,
@ -30,7 +31,6 @@ import {
getEntryType, getEntryType,
getExtGlob, getExtGlob,
} from './utils.js'; } from './utils.js';
import { createDefaultAstroMetadata } from '../vite-plugin-astro/metadata.js';
interface AstroContentVirtualModPluginParams { interface AstroContentVirtualModPluginParams {
settings: AstroSettings; settings: AstroSettings;

View file

@ -99,11 +99,11 @@ export class AppPipeline extends Pipeline {
} }
async getModuleForRoute(route: RouteData): Promise<SinglePageBuiltModule> { async getModuleForRoute(route: RouteData): Promise<SinglePageBuiltModule> {
for(const defaultRoute of this.defaultRoutes) { for (const defaultRoute of this.defaultRoutes) {
if(route.component === defaultRoute.component) { if (route.component === defaultRoute.component) {
return { return {
page: () => Promise.resolve(defaultRoute.instance), page: () => Promise.resolve(defaultRoute.instance),
renderers: [] renderers: [],
}; };
} }
} }

View file

@ -84,7 +84,13 @@ export type SSRManifestI18n = {
export type SerializedSSRManifest = Omit< export type SerializedSSRManifest = Omit<
SSRManifest, SSRManifest,
'middleware' | 'routes' | 'assets' | 'componentMetadata' | 'inlinedScripts' | 'clientDirectives' | 'serverIslandNameMap' | 'middleware'
| 'routes'
| 'assets'
| 'componentMetadata'
| 'inlinedScripts'
| 'clientDirectives'
| 'serverIslandNameMap'
> & { > & {
routes: SerializedRouteInfo[]; routes: SerializedRouteInfo[];
assets: string[]; assets: string[];

View file

@ -272,8 +272,8 @@ export class BuildPipeline extends Pipeline {
return await entry.page(); return await entry.page();
} }
for(const route of this.defaultRoutes) { for (const route of this.defaultRoutes) {
if(route.component === routeData.component) { if (route.component === routeData.component) {
return route.instance; return route.instance;
} }
} }

View file

@ -4,6 +4,7 @@ import type { Plugin as VitePlugin } from 'vite';
import type { AstroAdapter, AstroSettings } from '../../../@types/astro.js'; import type { AstroAdapter, AstroSettings } from '../../../@types/astro.js';
import { isFunctionPerRouteEnabled } from '../../../integrations/hooks.js'; import { isFunctionPerRouteEnabled } from '../../../integrations/hooks.js';
import { routeIsRedirect } from '../../redirects/index.js'; import { routeIsRedirect } from '../../redirects/index.js';
import { VIRTUAL_ISLAND_MAP_ID } from '../../server-islands/vite-plugin-server-islands.js';
import { isServerLikeOutput } from '../../util.js'; import { isServerLikeOutput } from '../../util.js';
import { addRollupInput } from '../add-rollup-input.js'; import { addRollupInput } from '../add-rollup-input.js';
import type { BuildInternals } from '../internal.js'; import type { BuildInternals } from '../internal.js';
@ -13,7 +14,6 @@ import { SSR_MANIFEST_VIRTUAL_MODULE_ID } from './plugin-manifest.js';
import { MIDDLEWARE_MODULE_ID } from './plugin-middleware.js'; import { MIDDLEWARE_MODULE_ID } from './plugin-middleware.js';
import { ASTRO_PAGE_MODULE_ID } from './plugin-pages.js'; import { ASTRO_PAGE_MODULE_ID } from './plugin-pages.js';
import { RENDERERS_MODULE_ID } from './plugin-renderers.js'; import { RENDERERS_MODULE_ID } from './plugin-renderers.js';
import { VIRTUAL_ISLAND_MAP_ID } from '../../server-islands/vite-plugin-server-islands.js';
import { getComponentFromVirtualModulePageName, getVirtualModulePageName } from './util.js'; import { getComponentFromVirtualModulePageName, getVirtualModulePageName } from './util.js';
export const SSR_VIRTUAL_MODULE_ID = '@astrojs-ssr-virtual-entry'; export const SSR_VIRTUAL_MODULE_ID = '@astrojs-ssr-virtual-entry';
@ -250,7 +250,9 @@ function generateSSRCode(settings: AstroSettings, adapter: AstroAdapter, middlew
`import { manifest as defaultManifest } from '${SSR_MANIFEST_VIRTUAL_MODULE_ID}';`, `import { manifest as defaultManifest } from '${SSR_MANIFEST_VIRTUAL_MODULE_ID}';`,
`import * as serverEntrypointModule from '${adapter.serverEntrypoint}';`, `import * as serverEntrypointModule from '${adapter.serverEntrypoint}';`,
edgeMiddleware ? `` : `import { onRequest as middleware } from '${middlewareId}';`, edgeMiddleware ? `` : `import { onRequest as middleware } from '${middlewareId}';`,
settings.config.experimental.serverIslands ? `import { serverIslandMap } from '${VIRTUAL_ISLAND_MAP_ID}';` : '' settings.config.experimental.serverIslands
? `import { serverIslandMap } from '${VIRTUAL_ISLAND_MAP_ID}';`
: '',
]; ];
const contents = [ const contents = [

View file

@ -540,7 +540,10 @@ export const AstroConfigSchema = z.object({
}) })
.strict() .strict()
.optional(), .optional(),
serverIslands: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.serverIslands), serverIslands: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.experimental.serverIslands),
}) })
.strict( .strict(
`Invalid or outdated experimental feature.\nCheck for incorrect spelling or outdated Astro version.\nSee https://docs.astro.build/en/reference/configuration-reference/#experimental-flags for a list of all current experiments.` `Invalid or outdated experimental feature.\nCheck for incorrect spelling or outdated Astro version.\nSee https://docs.astro.build/en/reference/configuration-reference/#experimental-flags for a list of all current experiments.`

View file

@ -36,8 +36,8 @@ import { vitePluginSSRManifest } from '../vite-plugin-ssr-manifest/index.js';
import type { Logger } from './logger/core.js'; import type { Logger } from './logger/core.js';
import { createViteLogger } from './logger/vite.js'; import { createViteLogger } from './logger/vite.js';
import { vitePluginMiddleware } from './middleware/vite-plugin.js'; import { vitePluginMiddleware } from './middleware/vite-plugin.js';
import { vitePluginServerIslands } from './server-islands/vite-plugin-server-islands.js';
import { joinPaths } from './path.js'; import { joinPaths } from './path.js';
import { vitePluginServerIslands } from './server-islands/vite-plugin-server-islands.js';
import { isObject } from './util.js'; import { isObject } from './util.js';
interface CreateViteOptions { interface CreateViteOptions {

View file

@ -1,7 +1,16 @@
import type { ComponentInstance, ManifestData, SSRManifest, } from "../../@types/astro.js"; import type { ComponentInstance, ManifestData, SSRManifest } from '../../@types/astro.js';
import { DEFAULT_404_COMPONENT } from "../constants.js"; import { DEFAULT_404_COMPONENT } from '../constants.js';
import { ensureServerIslandRoute, createEndpoint as createServerIslandEndpoint, SERVER_ISLAND_ROUTE, SERVER_ISLAND_COMPONENT } from "../server-islands/endpoint.js"; import {
import { ensure404Route, default404Instance, DEFAULT_404_ROUTE } from './astro-designed-error-pages.js'; SERVER_ISLAND_COMPONENT,
SERVER_ISLAND_ROUTE,
createEndpoint as createServerIslandEndpoint,
ensureServerIslandRoute,
} from '../server-islands/endpoint.js';
import {
DEFAULT_404_ROUTE,
default404Instance,
ensure404Route,
} from './astro-designed-error-pages.js';
export function injectDefaultRoutes(manifest: ManifestData) { export function injectDefaultRoutes(manifest: ManifestData) {
ensure404Route(manifest); ensure404Route(manifest);
@ -14,7 +23,7 @@ type DefaultRouteParams = {
matchesComponent(filePath: URL): boolean; matchesComponent(filePath: URL): boolean;
route: string; route: string;
component: string; component: string;
} };
export function createDefaultRoutes(manifest: SSRManifest, root: URL): DefaultRouteParams[] { export function createDefaultRoutes(manifest: SSRManifest, root: URL): DefaultRouteParams[] {
return [ return [
@ -29,6 +38,6 @@ export function createDefaultRoutes(manifest: SSRManifest, root: URL): DefaultRo
matchesComponent: (filePath) => filePath.href === new URL(SERVER_ISLAND_COMPONENT, root).href, matchesComponent: (filePath) => filePath.href === new URL(SERVER_ISLAND_COMPONENT, root).href,
route: SERVER_ISLAND_ROUTE, route: SERVER_ISLAND_ROUTE,
component: SERVER_ISLAND_COMPONENT, component: SERVER_ISLAND_COMPONENT,
} },
]; ];
} }

View file

@ -1,5 +1,15 @@
import { renderComponent, renderTemplate, type AstroComponentFactory, type ComponentSlots } from '../../runtime/server/index.js'; import type {
import type { ComponentInstance, ManifestData, RouteData, SSRManifest } from '../../@types/astro.js'; ComponentInstance,
ManifestData,
RouteData,
SSRManifest,
} from '../../@types/astro.js';
import {
type AstroComponentFactory,
type ComponentSlots,
renderComponent,
renderTemplate,
} from '../../runtime/server/index.js';
import { createSlotValueFromString } from '../../runtime/server/render/slot.js'; import { createSlotValueFromString } from '../../runtime/server/render/slot.js';
export const SERVER_ISLAND_ROUTE = '/_server-islands/[name]'; export const SERVER_ISLAND_ROUTE = '/_server-islands/[name]';
@ -17,7 +27,7 @@ export function ensureServerIslandRoute(manifest: ManifestData) {
params: ['name'], params: ['name'],
segments: [ segments: [
[{ content: '_server-islands', dynamic: false, spread: false }], [{ content: '_server-islands', dynamic: false, spread: false }],
[{ content: 'name', dynamic: true, spread: false }] [{ content: 'name', dynamic: true, spread: false }],
], ],
// eslint-disable-next-line // eslint-disable-next-line
pattern: /^\/_server-islands\/([^/]+?)$/, pattern: /^\/_server-islands\/([^/]+?)$/,
@ -25,7 +35,7 @@ export function ensureServerIslandRoute(manifest: ManifestData) {
isIndex: false, isIndex: false,
fallbackRoutes: [], fallbackRoutes: [],
route: SERVER_ISLAND_ROUTE, route: SERVER_ISLAND_ROUTE,
} };
manifest.routes.push(route); manifest.routes.push(route);
} }
@ -34,7 +44,7 @@ type RenderOptions = {
componentExport: string; componentExport: string;
props: Record<string, any>; props: Record<string, any>;
slots: Record<string, string>; slots: Record<string, string>;
} };
export function createEndpoint(manifest: SSRManifest) { export function createEndpoint(manifest: SSRManifest) {
const page: AstroComponentFactory = async (result) => { const page: AstroComponentFactory = async (result) => {
@ -42,19 +52,19 @@ export function createEndpoint(manifest: SSRManifest) {
const request = result.request; const request = result.request;
const raw = await request.text(); const raw = await request.text();
const data = JSON.parse(raw) as RenderOptions; const data = JSON.parse(raw) as RenderOptions;
if(!params.name) { if (!params.name) {
return new Response(null, { return new Response(null, {
status: 400, status: 400,
statusText: 'Bad request' statusText: 'Bad request',
}); });
} }
const componentId = params.name; const componentId = params.name;
const imp = manifest.serverIslandMap?.get(componentId); const imp = manifest.serverIslandMap?.get(componentId);
if(!imp) { if (!imp) {
return new Response(null, { return new Response(null, {
status: 404, status: 404,
statusText: 'Not found' statusText: 'Not found',
}); });
} }
@ -63,12 +73,12 @@ export function createEndpoint(manifest: SSRManifest) {
const Component = (componentModule as any)[data.componentExport]; const Component = (componentModule as any)[data.componentExport];
const slots: ComponentSlots = {}; const slots: ComponentSlots = {};
for(const prop in data.slots) { for (const prop in data.slots) {
slots[prop] = createSlotValueFromString(data.slots[prop]); slots[prop] = createSlotValueFromString(data.slots[prop]);
} }
return renderTemplate`${renderComponent(result, 'Component', Component, props, slots)}`; return renderTemplate`${renderComponent(result, 'Component', Component, props, slots)}`;
} };
page.isAstroComponentFactory = true; page.isAstroComponentFactory = true;
@ -79,4 +89,3 @@ export function createEndpoint(manifest: SSRManifest) {
return instance; return instance;
} }

View file

@ -1,10 +1,10 @@
import type { AstroPluginMetadata } from '../../vite-plugin-astro/index.js';
import type { AstroSettings } from '../../@types/astro.js';
import type { ConfigEnv, ViteDevServer, Plugin as VitePlugin } from 'vite'; import type { ConfigEnv, ViteDevServer, Plugin as VitePlugin } from 'vite';
import type { AstroSettings } from '../../@types/astro.js';
import type { AstroPluginMetadata } from '../../vite-plugin-astro/index.js';
export const VIRTUAL_ISLAND_MAP_ID = '@astro-server-islands'; export const VIRTUAL_ISLAND_MAP_ID = '@astro-server-islands';
export const RESOLVED_VIRTUAL_ISLAND_MAP_ID = '\0' + VIRTUAL_ISLAND_MAP_ID; export const RESOLVED_VIRTUAL_ISLAND_MAP_ID = '\0' + VIRTUAL_ISLAND_MAP_ID;
const serverIslandPlaceholder = '\'$$server-islands$$\''; const serverIslandPlaceholder = "'$$server-islands$$'";
export function vitePluginServerIslands({ settings }: { settings: AstroSettings }): VitePlugin { export function vitePluginServerIslands({ settings }: { settings: AstroSettings }): VitePlugin {
let command: ConfigEnv['command'] = 'serve'; let command: ConfigEnv['command'] = 'serve';
@ -20,29 +20,29 @@ export function vitePluginServerIslands({ settings }: { settings: AstroSettings
viteServer = _server; viteServer = _server;
}, },
resolveId(name) { resolveId(name) {
if(name === VIRTUAL_ISLAND_MAP_ID) { if (name === VIRTUAL_ISLAND_MAP_ID) {
return RESOLVED_VIRTUAL_ISLAND_MAP_ID; return RESOLVED_VIRTUAL_ISLAND_MAP_ID;
} }
}, },
load(id) { load(id) {
if(id === RESOLVED_VIRTUAL_ISLAND_MAP_ID) { if (id === RESOLVED_VIRTUAL_ISLAND_MAP_ID) {
return `export const serverIslandMap = ${serverIslandPlaceholder};`; return `export const serverIslandMap = ${serverIslandPlaceholder};`;
} }
}, },
transform(_code, id) { transform(_code, id) {
if(id.endsWith('.astro')) { if (id.endsWith('.astro')) {
const info = this.getModuleInfo(id); const info = this.getModuleInfo(id);
if(info?.meta) { if (info?.meta) {
const astro = info.meta.astro as AstroPluginMetadata['astro'] | undefined; const astro = info.meta.astro as AstroPluginMetadata['astro'] | undefined;
if(astro?.serverComponents.length) { if (astro?.serverComponents.length) {
for(const comp of astro.serverComponents) { for (const comp of astro.serverComponents) {
if(!settings.serverIslandNameMap.has(comp.resolvedPath)) { if (!settings.serverIslandNameMap.has(comp.resolvedPath)) {
let name = comp.localName; let name = comp.localName;
let idx = 1; let idx = 1;
while(true) { while (true) {
// Name not taken, let's use it. // Name not taken, let's use it.
if(!settings.serverIslandMap.has(name)) { if (!settings.serverIslandMap.has(name)) {
break; break;
} }
// Increment a number onto the name: Avatar -> Avatar1 // Increment a number onto the name: Avatar -> Avatar1
@ -57,12 +57,12 @@ export function vitePluginServerIslands({ settings }: { settings: AstroSettings
}); });
// Build mode // Build mode
if(command === 'build') { if (command === 'build') {
let referenceId = this.emitFile({ let referenceId = this.emitFile({
type: 'chunk', type: 'chunk',
id: comp.specifier, id: comp.specifier,
importer: id, importer: id,
name: comp.localName name: comp.localName,
}); });
referenceIdMap.set(comp.resolvedPath, referenceId); referenceIdMap.set(comp.resolvedPath, referenceId);
@ -74,17 +74,17 @@ export function vitePluginServerIslands({ settings }: { settings: AstroSettings
} }
}, },
renderChunk(code) { renderChunk(code) {
if(code.includes(serverIslandPlaceholder)) { if (code.includes(serverIslandPlaceholder)) {
let mapSource = 'new Map(['; let mapSource = 'new Map([';
for(let [resolvedPath, referenceId] of referenceIdMap) { for (let [resolvedPath, referenceId] of referenceIdMap) {
const fileName = this.getFileName(referenceId); const fileName = this.getFileName(referenceId);
const islandName = settings.serverIslandNameMap.get(resolvedPath)!; const islandName = settings.serverIslandNameMap.get(resolvedPath)!;
mapSource += `\n\t['${islandName}', () => import('./${fileName}')],` mapSource += `\n\t['${islandName}', () => import('./${fileName}')],`;
} }
mapSource += '\n]);'; mapSource += '\n]);';
referenceIdMap.clear(); referenceIdMap.clear();
return code.replace(serverIslandPlaceholder, mapSource); return code.replace(serverIslandPlaceholder, mapSource);
} }
}, },
} };
} }

View file

@ -3,8 +3,8 @@ import * as t from '@babel/types';
import { AstroError } from '../core/errors/errors.js'; import { AstroError } from '../core/errors/errors.js';
import { AstroErrorData } from '../core/errors/index.js'; import { AstroErrorData } from '../core/errors/index.js';
import { resolvePath } from '../core/viteUtils.js'; import { resolvePath } from '../core/viteUtils.js';
import type { PluginMetadata } from '../vite-plugin-astro/types.js';
import { createDefaultAstroMetadata } from '../vite-plugin-astro/metadata.js'; import { createDefaultAstroMetadata } from '../vite-plugin-astro/metadata.js';
import type { PluginMetadata } from '../vite-plugin-astro/types.js';
const ClientOnlyPlaceholder = 'astro-client-only'; const ClientOnlyPlaceholder = 'astro-client-only';

View file

@ -26,7 +26,6 @@ export async function renderEndpoint(
); );
} }
if (handler === undefined) { if (handler === undefined) {
logger.warn( logger.warn(
'router', 'router',
`No API Route handler exists for the method "${method}" for the route "${url.pathname}".\n` + `No API Route handler exists for the method "${method}" for the route "${url.pathname}".\n` +

View file

@ -6,7 +6,11 @@ export type AstroFactoryReturnValue = RenderTemplateResult | Response | HeadAndC
// The callback passed to to $$createComponent // The callback passed to to $$createComponent
export interface AstroComponentFactory { export interface AstroComponentFactory {
(result: SSRResult, props: any, slots: any): AstroFactoryReturnValue | Promise<AstroFactoryReturnValue>; (
result: SSRResult,
props: any,
slots: any
): AstroFactoryReturnValue | Promise<AstroFactoryReturnValue>;
isAstroComponentFactory?: boolean; isAstroComponentFactory?: boolean;
moduleId?: string | undefined; moduleId?: string | undefined;
propagation?: PropagationHint; propagation?: PropagationHint;

View file

@ -27,9 +27,9 @@ import {
} from './common.js'; } from './common.js';
import { componentIsHTMLElement, renderHTMLElement } from './dom.js'; import { componentIsHTMLElement, renderHTMLElement } from './dom.js';
import { maybeRenderHead } from './head.js'; import { maybeRenderHead } from './head.js';
import { containsServerDirective, renderServerIsland } from './server-islands.js';
import { type ComponentSlots, renderSlotToString, renderSlots } from './slot.js'; import { type ComponentSlots, renderSlotToString, renderSlots } from './slot.js';
import { formatList, internalSpreadAttributes, renderElement, voidElementNames } from './util.js'; import { formatList, internalSpreadAttributes, renderElement, voidElementNames } from './util.js';
import { containsServerDirective, renderServerIsland } from './server-islands.js';
const needsHeadRenderingSymbol = Symbol.for('astro.needsHeadRendering'); const needsHeadRenderingSymbol = Symbol.for('astro.needsHeadRendering');
const rendererAliases = new Map([['solid', 'solid-js']]); const rendererAliases = new Map([['solid', 'solid-js']]);
@ -474,7 +474,7 @@ function renderAstroComponent(
props: Record<string | number, any>, props: Record<string | number, any>,
slots: any = {} slots: any = {}
): RenderInstance { ): RenderInstance {
if(containsServerDirective(props)) { if (containsServerDirective(props)) {
return renderServerIsland(result, displayName, props, slots); return renderServerIsland(result, displayName, props, slots);
} }

View file

@ -1,18 +1,16 @@
import type { import type { SSRResult } from '../../../@types/astro.js';
SSRResult, import { renderChild } from './any.js';
} from '../../../@types/astro.js'; import type { RenderInstance } from './common.js';
import { renderChild } from "./any.js"; import { type ComponentSlots, renderSlotToString } from './slot.js';
import type { RenderInstance } from "./common.js";
import { renderSlotToString, type ComponentSlots } from "./slot.js";
const internalProps = new Set([ const internalProps = new Set([
'server:component-path', 'server:component-path',
'server:component-export', 'server:component-export',
'server:component-directive', 'server:component-directive',
'server:defer' 'server:defer',
]); ]);
export function containsServerDirective(props: Record<string | number, any>,) { export function containsServerDirective(props: Record<string | number, any>) {
return 'server:component-directive' in props; return 'server:component-directive' in props;
} }
@ -20,7 +18,7 @@ export function renderServerIsland(
result: SSRResult, result: SSRResult,
_displayName: string, _displayName: string,
props: Record<string | number, any>, props: Record<string | number, any>,
slots: ComponentSlots, slots: ComponentSlots
): RenderInstance { ): RenderInstance {
return { return {
async render(destination) { async render(destination) {
@ -28,23 +26,23 @@ export function renderServerIsland(
const componentExport = props['server:component-export']; const componentExport = props['server:component-export'];
const componentId = result.serverIslandNameMap.get(componentPath); const componentId = result.serverIslandNameMap.get(componentPath);
if(!componentId) { if (!componentId) {
throw new Error(`Could not find server component name`); throw new Error(`Could not find server component name`);
} }
// Remove internal props // Remove internal props
for(const key of Object.keys(props)) { for (const key of Object.keys(props)) {
if(internalProps.has(key)) { if (internalProps.has(key)) {
delete props[key]; delete props[key];
} }
} }
destination.write('<!--server-island-start-->') destination.write('<!--server-island-start-->');
// Render the slots // Render the slots
const renderedSlots: Record<string, string> = {}; const renderedSlots: Record<string, string> = {};
for(const name in slots) { for (const name in slots) {
if(name !== 'fallback') { if (name !== 'fallback') {
const content = await renderSlotToString(result, slots[name]); const content = await renderSlotToString(result, slots[name]);
renderedSlots[name] = content.toString(); renderedSlots[name] = content.toString();
} else { } else {
@ -83,7 +81,7 @@ if(response.status === 200 && response.headers.get('content-type') === 'text/htm
script.before(frag); script.before(frag);
} }
script.remove(); script.remove();
</script>`) </script>`);
} },
} };
} }

View file

@ -105,7 +105,7 @@ export async function renderSlots(
} }
export function createSlotValueFromString(content: string): ComponentSlotValue { export function createSlotValueFromString(content: string): ComponentSlotValue {
return function() { return function () {
return renderTemplate`${unescapeHTML(content)}`; return renderTemplate`${unescapeHTML(content)}`;
}; };
} }

View file

@ -158,8 +158,8 @@ export class DevPipeline extends Pipeline {
const { loader } = this; const { loader } = this;
// First check built-in routes // First check built-in routes
for(const route of this.defaultRoutes) { for (const route of this.defaultRoutes) {
if(route.matchesComponent(filePath)) { if (route.matchesComponent(filePath)) {
return route.instance; return route.instance;
} }
} }
@ -226,8 +226,8 @@ export class DevPipeline extends Pipeline {
rewriteKnownRoute(route: string, sourceRoute: RouteData): ComponentInstance { rewriteKnownRoute(route: string, sourceRoute: RouteData): ComponentInstance {
if (isServerLikeOutput(this.config) && sourceRoute.prerender) { if (isServerLikeOutput(this.config) && sourceRoute.prerender) {
for(let def of this.defaultRoutes) { for (let def of this.defaultRoutes) {
if(route === def.route) { if (route === def.route) {
return def.instance; return def.instance;
} }
} }

View file

@ -9,8 +9,8 @@ import { AstroError, AstroErrorData } from '../core/errors/index.js';
import { patchOverlay } from '../core/errors/overlay.js'; import { patchOverlay } from '../core/errors/overlay.js';
import type { Logger } from '../core/logger/core.js'; import type { Logger } from '../core/logger/core.js';
import { createViteLoader } from '../core/module-loader/index.js'; import { createViteLoader } from '../core/module-loader/index.js';
import { createRouteManifest } from '../core/routing/index.js';
import { injectDefaultRoutes } from '../core/routing/default.js'; import { injectDefaultRoutes } from '../core/routing/default.js';
import { createRouteManifest } from '../core/routing/index.js';
import { toRoutingStrategy } from '../i18n/utils.js'; import { toRoutingStrategy } from '../i18n/utils.js';
import { baseMiddleware } from './base.js'; import { baseMiddleware } from './base.js';
import { createController } from './controller.js'; import { createController } from './controller.js';
@ -36,7 +36,7 @@ export default function createVitePluginAstroServer({
const loader = createViteLoader(viteServer); const loader = createViteLoader(viteServer);
const manifest = createDevelopmentManifest(settings); const manifest = createDevelopmentManifest(settings);
let manifestData: ManifestData = injectDefaultRoutes( let manifestData: ManifestData = injectDefaultRoutes(
createRouteManifest({ settings, fsMod }, logger), createRouteManifest({ settings, fsMod }, logger)
); );
const pipeline = DevPipeline.create(manifestData, { loader, logger, manifest, settings }); const pipeline = DevPipeline.create(manifestData, { loader, logger, manifest, settings });
const controller = createController({ loader }); const controller = createController({ loader });
@ -46,9 +46,7 @@ export default function createVitePluginAstroServer({
function rebuildManifest(needsManifestRebuild: boolean) { function rebuildManifest(needsManifestRebuild: boolean) {
pipeline.clearRouteCache(); pipeline.clearRouteCache();
if (needsManifestRebuild) { if (needsManifestRebuild) {
manifestData = injectDefaultRoutes( manifestData = injectDefaultRoutes(createRouteManifest({ settings }, logger));
createRouteManifest({ settings }, logger),
);
pipeline.setManifestData(manifestData); pipeline.setManifestData(manifestData);
} }
} }

View file

@ -13,9 +13,9 @@ import { AstroError, AstroErrorData } from '../core/errors/index.js';
import type { Logger } from '../core/logger/core.js'; import type { Logger } from '../core/logger/core.js';
import { isMarkdownFile } from '../core/util.js'; import { isMarkdownFile } from '../core/util.js';
import { shorthash } from '../runtime/server/shorthash.js'; import { shorthash } from '../runtime/server/shorthash.js';
import { createDefaultAstroMetadata } from '../vite-plugin-astro/metadata.js';
import { getFileInfo } from '../vite-plugin-utils/index.js'; import { getFileInfo } from '../vite-plugin-utils/index.js';
import { type MarkdownImagePath, getMarkdownCodeForImages } from './images.js'; import { type MarkdownImagePath, getMarkdownCodeForImages } from './images.js';
import { createDefaultAstroMetadata } from '../vite-plugin-astro/metadata.js';
interface AstroPluginOptions { interface AstroPluginOptions {
settings: AstroSettings; settings: AstroSettings;

View file

@ -1,4 +1,3 @@
import assert from 'node:assert/strict'; import assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test'; import { after, before, describe, it } from 'node:test';
import * as cheerio from 'cheerio'; import * as cheerio from 'cheerio';
@ -93,7 +92,7 @@ describe('Server islands', () => {
componentExport: 'default', componentExport: 'default',
props: {}, props: {},
slots: {}, slots: {},
}) }),
}); });
return app.render(request); return app.render(request);
} }