Desarrollar con Mercury
Introducción
Entendemos como desarrollar cuando vamos a trabajar en una aplicación (site) que usa Mercury como frontend.
Requerimientos
- NodeJS 16
- Google Artifact Registry
- YARN
Instalación de requerimientos
NodeJS
nvm para poder instalar y cambiar fácilmente entre versiones de NodeJS.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
nvm install 16
Google Artifact Registry
Tener NPM configurado para poder descargar paquetes de repositorio privado de google e instalar gestor de dependencias para Javascript.
npx google-artifactregistry-auth
npm install -g npm@9.4.0
YARN
YARN (alternativa a NPM, la cual es la usada en el proyecto)
npm install -g yarn
Preparación de entorno local
1. Clonar proyecto
Tras clonar el repositorio del proyecto donde se esté desarrollando el frontal del site con Mercury, por ejemplo:
git clone git@bitbucket.org:etailers/eshop-horeca-front.git
La estructura de directorios sería como la siguiente:

Trabajaremos en el directorio mercury que es donde se encuentran los ficheros de nuestra aplicación. Dentro de este directorio encontramos:
- public
- Contiene los ficheros de acceso público: imágenes, tipografías, etc…
- src
- Contiene los archivos de nuestra aplicación: páginas, componentes, servicios, etc…
Los detalles de esta estructura de directorios los iremos viendo en la documentación.
2. Instalar paquetes
Una vez tengamos descargada la aplicación iremos al root del proyecto e instalaremos todos los paquetes:
yarn preinstall
yarn
3. Configurar Mercury
Copiaremos la configuración local de ejemplo y haremos los ajustes necesaris para que funcione con nuestro entorno.
cp mercury/.env.local.dist mercury/.env.local
Para trabajar contra una aplicación que tengamos en local:
NODE_TLS_REJECT_UNAUTHORIZED=0
NEXT_DEV_PUBLIC_API_ENDPOINT=http://localhost:3000/api/
NEXT_PUBLIC_IMAGE_DOMAIN=magento.test
NEXT_PUBLIC_API_ENDPOINT=https://magento.test/graphql
TET_API_SECRET=<token_configurado_en_la_configuracion_del_ecomm>
4 - Iniciar proyecto
yarn dev
5 - Acceder a la aplicación
Tras iniciar el proyecto ya podremos entrar la url en nuestro navegador.
https://magento.test
Comandos
Podemos consultar los comandos disponibles en el fichero package.json del root del proyecto:
yarn dev
- Inicializa la aplicación en modo desarrollo.
yarn build
- Lo usaremos cuando queramos hacer “build” de la aplicación. Es recomendable ejecutarlo antes de hacer un “merge” de nuestras modificaciones para comprobar que no hay ningún error, y para testear la aplicación como si estuviéramos en modo producción.
yarn start
Inicializa la aplicación que hemos construido. Se usa junto con “build” para testear la aplicación como si estuviéramos en modo producción.
yarn lint
Ejecuta el linter para poder encontrar errores en la aplicación.
yarn clear
Para limpiar la instalación. Después de este comando habrá que ejecutar “yarn” para que vuelva a instalar todos los paquetes.
yarn preinstall
Ejecuta npx google-artifactregistry-auth para permitir la descarga de paquetes de glcoud.
yarn install
Para descargar e instalar todos los paquetes.
yarn upgrade-packages
Actualiza todos los paquetes de la aplicación.
Configuración
Dentro del directorio de mercury encontramos el fichero mercury.config.js. Este fichero nos permite configurar comportamientos globales del tema.
Tiene 2 apartados principales:
- components
Nos permite configurar propiedades generales de los componentes de la aplicación. Hay muchos componentes que nos permitirán configurar variaciones desde este archivo de configuración. Por ejemplo: logo, menu, quickSearch, etc…
- tailwind
En este apartado podemos configurar los estilos de las clases generales de tailwind de la misma forma que lo haríamos en tailwind.config.js.
Customización
Ahora que ya tenemos la aplicación instalada en local y los conocimientos básicos de la estructura de directorios de Mercury ya estamos listos para ver como podemos empezar a realizar modificaciones a la aplicación.
Estilos
Mercury usa Tailwind para los estilos CSS de la aplicación.
Existen 2 sitios donde podemos ajustar los estilos de la aplicación dependiendo de nuestro propósito:
- mercury.config.js
En este archivo podemos ajustar los estilos generales de las clases de tailwind. Como hemos visto en el apartado de “Configuración”.
- styles/theme.css
En este fichero podemos sobreescribir las clases que no son de tailwind y que usan los componentes de Mercury.
A continuación dejamos un ejemplo del tipo de clases que podemos sobreescribir en styles/themes.css
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
@font-face {
font-family: 'Switzer';
src: url('/fonts/Switzer-Light.woff2') format('woff2');
font-weight: 300;
font-display: swap;
font-style: normal;
}
@font-face {
font-family: 'Switzer';
src: url('/fonts/Switzer-Regular.woff2') format('woff2');
font-weight: 400;
font-display: swap;
font-style: normal;
}
@font-face {
font-family: 'Switzer';
src: url('/fonts/Switzer-Medium.woff2') format('woff2');
font-weight: 500;
font-display: swap;
font-style: normal;
}
@font-face {
font-family: 'Switzer';
src: url('/fonts/Switzer-Semibold.woff2') format('woff2');
font-weight: 600;
font-display: swap;
font-style: normal;
}
body {
@apply font-sans text-base text-gray-900 bg-[#FBFDFA];
}
h1 {
@apply text-[36px] leading-[44px] lg:text-[40px] lg:leading-[48px]
}
h2 {
@apply text-[32px] leading-[40px] lg:text-[36px] lg:leading-[44px]
}
h3 {
@apply text-[28px] leading-[36px] lg:text-[32px] lg:leading-[40px]
}
...
.footer-cms-content {
@apply !m-0 w-full lg:justify-end justify-center max-lg:pt-8
}
.footer-cms-content a {
@apply block text-base py-2
}
footer [sanitized-element="column-group"] {
@apply sm:!gap-16 gap-8 md:flex-nowrap flex-wrap basis-full lg:justify-end justify-center
}
footer [sanitized-element="row"] {
@apply w-full
}
footer [sanitized-element="column"] {
/*min-width: 240px;*/
@apply sm:!w-min sm:basis-0 basis-full whitespace-nowrap md:text-left text-center
}
}
Páginas
Mercury usa el sistema de enrutado de páginas de NextJS (ver documentación). Las páginas de la aplicación se encuentran en el directorio pages de mercury/src.
ls mercury/src/pages
Por defecto existen las siguientes páginas:
- Home
- index.tsx
- Categorías
- […urlKey].tsx
- Producto
- product/[…urlKey].tsx
- Búsqueda
- search.tsx
- Página CMS
- page/[…urlKey].tsx
- Mi cuenta
- account/[[…section]].tsx
- Carrito
- cart.tsx
- Errores
- 404.tsx, 500.tsx
Estructura de una página Mercury
Las páginas de la aplicación Mercury, por defecto, tienen todas una misma estructura.
import { Page } from '@mercury/theme/pages/home/client'
export { getStaticProps } from '@mercury/theme/pages/home/server'
export default function Home (props) {
return <Page {...props} />
}
- Importamos el componente que se encarga de renderizar la página (client side), en el ejemplo “Page”.
- Obtenemos las propiedades necesarias para el componente importado (server side) en el ejemplo “getStaticProps”.
- Usamos el componente importado (“Page”) para renderizar nuestra página.
Esta estructura de página nos permite poder sobreescribir el renderizado de la página sin tener que tocar la obtención de propiedades.
Siguiendo el ejemplo anterior, si queremos para sobreescribir la página mercury/src/pages/index.tsx.
import Head from 'next/head'
import { CmsContent } from '@mercury/theme/components'
import type { CmsPageType } from '@mercury/models'
export { getStaticProps } from '@mercury/theme/pages/home/server'
export default function Home (props) {
const { cmsPage, storeConfig } = props
const { title, metaTitle, metaDescription, metaKeywords } = cmsPage as CmsPageType
const { defaultTitle, defaultKeywords, defaultRobots } = storeConfig
return (
<>
<Head>
<title>{metaTitle || title || defaultTitle}</title>
{ metaDescription && <meta name="description" content={metaDescription} /> }
<meta name="keywords" content={metaKeywords || defaultKeywords} />
<meta name="robots" content={defaultRobots} />
<link rel="icon" href="/favicon.ico" />
</Head>
<section className='pt-8 pb-10'>
<h1>Esta es una nueva sección en la página de Inicio</h1>
<p>Bloque de ejemplo para demostrar como sobreescribir una página.</p>
</section>
<section className='pt-8 pb-10'>
<CmsContent content={cmsPage.content} />
</section>
</>
)
}
- Podemos copiar el componente de la página Mercury original @mercury/theme/pages/home/client al componente de nuestra página.
- Importar los componentes de la página Mercury original @mercury/theme/pages/home/client que necesitemos además de los que nuevos que podamos necesitar para nuestras modificaciones.
- Modificar el contenido del componente “Home” para adaptarlo a nuestras necesidades.
Componentes
Mercury tiene su propia colección de componentes (ver documentación temas) que se usan a lo largo de toda la aplicación.
Muchos de estos componentes se puede personalizar a través de las opciones de configuración en el fichero mercury.config.js, y a través de los ajustes de estilos con el fichero theme.css.
Para cuando la configuración y el ajuste de estilos del componente no sea suficiente para conseguir la personalización deseada nos queda la opción de poder crear nuestro propio componente dentro de aplicación, lo que sería un “Custom Component”.
Los componentes propios de la aplicación los crearemos dentro de un “Custom Theme”. La estructura de direcotios sería la seiguiente:

Crear un Custom Component
Crearemos los “Custom Components” dentro del directorio mercury/src/component/CustomTheme.
Nuestro nuevo componente puede importar otros componentes, hooks, etc… de Mercury o de nuestra propia aplicación.
En el ejemplo de a continuación creams una personalización de la “Card” de producto basada en el componente original de Mercury.
import { useI18n } from '@mercury/i18n'
import { useProductPrice } from '@mercury/service-adobe-commerce'
import { AddToCart, CardProductProps, Price, addToFormatClasses } from '@mercury/theme/components'
import { Badge, Card, Typography } from '@mercury/ui'
import Image from 'next/image'
import { useEffect, useState } from 'react'
export default function CustomCard ({
product,
addToFormat = 'vertical',
border = 'none',
buttonSize,
hover = 'border',
nameSize = 'h6',
nameWeight = 'bold',
radius = 'none',
shadow = 'lg',
showBadge = false,
size = 'sm',
...props
}: CardProductProps) {
const { t } = useI18n()
const { sku, name, image, urlKey } = product
const { price, loading: priceLoading } = useProductPrice({ sku })
const [badge, setBadge] = useState<{text: string, color: string, variant?: string}>()
useEffect(() => {
if (showBadge && price && price.finalPrice < price.regularPrice) {
setBadge({
text: t('productPage.sale'),
color: 'green'
})
}
}, [price, setBadge, showBadge, t])
return (
<>
<Card
size={size}
border={border}
radius={radius}
shadow={shadow}
hover={hover}
className='px-3 py-2 bg-white md:px-4'
{...props}
>
<div>
<div className='relative w-full aspect-square md:mb-4 lg:mb-6'>
{ badge &&
<Badge variant={badge.variant ? badge.variant : 'default'}
color={badge.color ? badge.color : 'default'}
style={ { position: 'absolute', zIndex: 1 } }>
{badge.text}
</Badge>
}
<a href={`/product/${urlKey}`} className='relative block w-full h-full'>
<Image
src={image.src}
alt={name}
placeholder='blur'
blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mPcXA8AAesBNGQg4IAAAAAASUVORK5CYII="
quality={60}
className='object-contain'
fill
/>
</a>
</div>
{ sku && <h6 className='hidden mb-2 text-gray-400 text'>{t('productPage.ref')}: {sku}</h6> }
<Typography htmlTag='h6' variant={nameSize} weight={nameWeight} customClass='mb-4 line-clamp-3'>
<a href={`/product/${urlKey}`}>{name}</a>
</Typography>
</div>
<div>
<div className={addToFormatClasses[addToFormat]}>
<div className='md:min-h-[30px] md:overflow-hidden price-wrapper price-carousel'>
<Price variants='h5 h6' price={price?.finalPrice} regularPrice={price?.regularPrice} loading={priceLoading} />
</div>
<div className='mt-4'>
<AddToCart
buttonVariant='primary'
buttonSize={buttonSize}
nextJSLinkHref={{
pathname: `/product/${urlKey}`,
query: product.variants ? null : { added: 'true' }
}}
>
<span className='hidden md:inline'>{t('cart.add')}</span>
<span className='inline md:hidden'>{t('cart.addMobile')}</span>
</AddToCart>
</div>
</div>
</div>
</Card>
</>
)
}
Una vez tenemos creado nuestro “Custom Component” deberemos indicar a Mercury que use nuestro componente en lugar del suyo.
import '@mercury/theme/styles'
import Mercury from '@mercury/theme/configs'
import NextNProgress from 'nextjs-progressbar'
import { Layout } from '@mercury/theme/components'
import { ApiProvider } from '@mercury/service-adobe-commerce'
import ContextProvider, { MercuryContext } from '@mercury/theme/features'
import 'glider-js/glider.min.css'
import '../styles/theme.css'
// Importamos MercuryConfigType para poder sobrescribir la configuración por defecto.
import { MercuryConfigType } from '@mercury/theme/src/features/MercuryContext'
// Importamos nuestro Custom Component.
import CustomCard from '@/components/CustomTheme/CustomCard'
// Inidicamos el componente que queremos substituir con el nuestro.
const mercuryConfig: MercuryConfigType = {
ProductCardComponent: CustomCard
}
function MyApp ({ Component, pageProps }) {
const getLayout = Component.getLayout || ((page, pageProps) => <Layout pageProps={pageProps}>{page}</Layout>)
return (
<MercuryContext.Provider value={mercuryConfig}>
<ApiProvider pageProps={pageProps}>
<ContextProvider pageProps={pageProps}>
{getLayout(
<>
<NextNProgress color={Mercury.components.pageLoader.color} />
<Component {...pageProps} />
</>,
pageProps
)}
</ContextProvider>
</ApiProvider>
</MercuryContext.Provider>
)
}
export default MyApp
- Entraremos en mercury/src/pages/_app.tsx.
- Importarems nuestro “Custom Component”.
- Indicamos en mercuryConfig el componente de Mercury que sobreescribimos con en el de la aplicación.
También podemos usar nuestros “Custom Components” directamente en las páginas que hayamos sobreescrito. En estos casos no haría falta declarar la sobreescritura en el archivo mercury/src/pages/_app.tsx.